2001-12-17 14:31:42 +00:00
|
|
|
|
/** Class for serialization in GNUStep
|
1997-09-01 21:59:51 +00:00
|
|
|
|
Copyright (C) 1997 Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
|
|
Written by: Richard Frith-Macdoanld <richard@brainstorm.co.uk>
|
|
|
|
|
Date: August 1997
|
|
|
|
|
|
|
|
|
|
This file is part of the GNUstep Base Library.
|
|
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
2007-09-14 11:36:11 +00:00
|
|
|
|
modify it under the terms of the GNU Lesser General Public
|
1997-09-01 21:59:51 +00:00
|
|
|
|
License as published by the Free Software Foundation; either
|
2008-06-08 10:38:33 +00:00
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
2007-09-14 11:36:11 +00:00
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
1997-09-01 21:59:51 +00:00
|
|
|
|
License along with this library; if not, write to the Free
|
2006-10-20 10:56:27 +00:00
|
|
|
|
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
|
Boston, MA 02111 USA.
|
2001-12-18 16:54:15 +00:00
|
|
|
|
|
|
|
|
|
<title>NSSerializer class reference</title>
|
|
|
|
|
$Date$ $Revision$
|
1997-09-01 21:59:51 +00:00
|
|
|
|
*/
|
|
|
|
|
|
2003-06-07 01:24:41 +00:00
|
|
|
|
#include "config.h"
|
2003-07-31 23:49:32 +00:00
|
|
|
|
#include "GNUstepBase/preface.h"
|
2003-06-07 01:24:41 +00:00
|
|
|
|
#include "Foundation/NSData.h"
|
|
|
|
|
#include "Foundation/NSDictionary.h"
|
|
|
|
|
#include "Foundation/NSArray.h"
|
|
|
|
|
#include "Foundation/NSString.h"
|
|
|
|
|
#include "Foundation/NSException.h"
|
2007-12-02 21:13:09 +00:00
|
|
|
|
#include "Foundation/NSEnumerator.h"
|
2003-06-07 01:24:41 +00:00
|
|
|
|
#include "Foundation/NSProxy.h"
|
|
|
|
|
#include "Foundation/NSLock.h"
|
|
|
|
|
#include "Foundation/NSSet.h"
|
|
|
|
|
#include "Foundation/NSThread.h"
|
2006-10-31 07:05:46 +00:00
|
|
|
|
#include "Foundation/NSNotification.h"
|
2003-06-07 01:24:41 +00:00
|
|
|
|
#include "Foundation/NSNotificationQueue.h"
|
|
|
|
|
#include "Foundation/NSObjCRuntime.h"
|
|
|
|
|
#include "Foundation/NSValue.h"
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-03-13 09:58:43 +00:00
|
|
|
|
#include "GSPrivate.h"
|
|
|
|
|
|
2001-01-09 09:17:31 +00:00
|
|
|
|
@class GSDictionary;
|
|
|
|
|
@class GSMutableDictionary;
|
2005-07-08 11:48:37 +00:00
|
|
|
|
@interface GSMutableDictionary : NSObject // Help the compiler
|
|
|
|
|
@end
|
1998-11-19 15:45:00 +00:00
|
|
|
|
@class NSDataMalloc;
|
2005-07-08 11:48:37 +00:00
|
|
|
|
@interface NSDataMalloc : NSObject // Help the compiler
|
|
|
|
|
@end
|
1998-11-19 15:45:00 +00:00
|
|
|
|
|
|
|
|
|
/*
|
1998-11-20 12:12:08 +00:00
|
|
|
|
* Setup for inline operation of string map tables.
|
1998-11-19 15:45:00 +00:00
|
|
|
|
*/
|
2002-01-24 17:03:04 +00:00
|
|
|
|
#define GSI_MAP_RETAIN_KEY(M, X)
|
|
|
|
|
#define GSI_MAP_RELEASE_KEY(M, X)
|
|
|
|
|
#define GSI_MAP_RETAIN_VAL(M, X)
|
|
|
|
|
#define GSI_MAP_RELEASE_VAL(M, X)
|
|
|
|
|
#define GSI_MAP_HASH(M, X) [(X).obj hash]
|
|
|
|
|
#define GSI_MAP_EQUAL(M, X,Y) [(X).obj isEqualToString: (Y).obj]
|
2002-02-13 18:49:32 +00:00
|
|
|
|
#define GSI_MAP_NOCLEAN 1
|
1998-11-20 12:12:08 +00:00
|
|
|
|
|
2009-03-09 15:11:51 +00:00
|
|
|
|
#if GS_WITH_GC
|
|
|
|
|
#define GSI_MAP_NODES(M, X) \
|
|
|
|
|
(GSIMapNode)NSAllocateCollectable(X * sizeof(GSIMapNode_t), 0)
|
|
|
|
|
#endif
|
|
|
|
|
|
2003-07-31 23:49:32 +00:00
|
|
|
|
#include "GNUstepBase/GSIMap.h"
|
1998-11-19 15:45:00 +00:00
|
|
|
|
|
1998-11-20 12:12:08 +00:00
|
|
|
|
/*
|
|
|
|
|
* Setup for inline operation of string arrays.
|
|
|
|
|
*/
|
2002-01-31 07:20:16 +00:00
|
|
|
|
#define GSI_ARRAY_RETAIN(A, X)
|
|
|
|
|
#define GSI_ARRAY_RELEASE(A, X)
|
1999-06-21 08:30:26 +00:00
|
|
|
|
#define GSI_ARRAY_TYPES GSUNION_OBJ
|
1998-11-20 12:12:08 +00:00
|
|
|
|
|
2003-07-31 23:49:32 +00:00
|
|
|
|
#include "GNUstepBase/GSIArray.h"
|
1998-11-20 12:12:08 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Define constants for data types and variables to hold them.
|
|
|
|
|
*/
|
|
|
|
|
#define ST_XREF 0
|
|
|
|
|
#define ST_CSTRING 1
|
|
|
|
|
#define ST_STRING 2
|
|
|
|
|
#define ST_ARRAY 3
|
|
|
|
|
#define ST_MARRAY 4
|
|
|
|
|
#define ST_DICT 5
|
|
|
|
|
#define ST_MDICT 6
|
|
|
|
|
#define ST_DATA 7
|
2002-11-10 11:46:49 +00:00
|
|
|
|
#define ST_DATE 8
|
|
|
|
|
#define ST_NUMBER 9
|
1998-11-20 12:12:08 +00:00
|
|
|
|
|
|
|
|
|
static char st_xref = (char)ST_XREF;
|
1998-11-19 15:45:00 +00:00
|
|
|
|
static char st_cstring = (char)ST_CSTRING;
|
|
|
|
|
static char st_string = (char)ST_STRING;
|
|
|
|
|
static char st_array = (char)ST_ARRAY;
|
|
|
|
|
static char st_marray = (char)ST_MARRAY;
|
|
|
|
|
static char st_dict = (char)ST_DICT;
|
|
|
|
|
static char st_mdict = (char)ST_MDICT;
|
|
|
|
|
static char st_data = (char)ST_DATA;
|
2002-11-10 11:46:49 +00:00
|
|
|
|
static char st_date = (char)ST_DATE;
|
|
|
|
|
static char st_number = (char)ST_NUMBER;
|
1998-11-19 15:45:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2003-11-05 02:11:49 +00:00
|
|
|
|
* Variables to cache class information. These are used to check how
|
|
|
|
|
* an instance should be serialized.
|
1998-11-19 15:45:00 +00:00
|
|
|
|
*/
|
|
|
|
|
static Class ArrayClass = 0;
|
|
|
|
|
static Class MutableArrayClass = 0;
|
|
|
|
|
static Class DataClass = 0;
|
2002-11-10 11:46:49 +00:00
|
|
|
|
static Class DateClass = 0;
|
1998-11-19 15:45:00 +00:00
|
|
|
|
static Class DictionaryClass = 0;
|
|
|
|
|
static Class MutableDictionaryClass = 0;
|
2000-10-09 04:41:18 +00:00
|
|
|
|
static Class StringClass = 0;
|
2002-11-10 11:46:49 +00:00
|
|
|
|
static Class NumberClass = 0;
|
2000-10-09 04:41:18 +00:00
|
|
|
|
|
2006-05-20 10:11:39 +00:00
|
|
|
|
typedef struct {
|
|
|
|
|
@defs(GSString)
|
|
|
|
|
} *ivars;
|
|
|
|
|
|
1998-11-19 15:45:00 +00:00
|
|
|
|
typedef struct {
|
|
|
|
|
NSMutableData *data;
|
2002-11-09 16:40:00 +00:00
|
|
|
|
void (*appImp)(NSData*,SEL,const void*,unsigned);
|
|
|
|
|
void* (*datImp)(NSMutableData*,SEL); // Bytes pointer.
|
|
|
|
|
unsigned int (*lenImp)(NSData*,SEL); // Length of data.
|
|
|
|
|
void (*serImp)(NSMutableData*,SEL,int); // Serialize integer.
|
|
|
|
|
void (*setImp)(NSMutableData*,SEL,unsigned); // Set length of data.
|
1998-11-20 12:12:08 +00:00
|
|
|
|
unsigned count; // String counter.
|
2000-04-18 09:02:38 +00:00
|
|
|
|
GSIMapTable_t map; // For uniquing.
|
1998-11-20 12:12:08 +00:00
|
|
|
|
BOOL shouldUnique; // Do we do uniquing?
|
1998-11-19 15:45:00 +00:00
|
|
|
|
} _NSSerializerInfo;
|
|
|
|
|
|
2000-10-30 18:00:27 +00:00
|
|
|
|
static SEL appSel;
|
|
|
|
|
static SEL datSel;
|
|
|
|
|
static SEL lenSel;
|
|
|
|
|
static SEL serSel;
|
|
|
|
|
static SEL setSel;
|
1998-11-19 15:45:00 +00:00
|
|
|
|
|
2005-06-17 14:51:57 +00:00
|
|
|
|
/* Compatibility methods from NEXTSTEP (Implemented in NSObject) */
|
|
|
|
|
@interface NSObject (Serializer)
|
|
|
|
|
- (retval_t) performv: (SEL)aSel :(arglist_t)argFrame;
|
|
|
|
|
@end
|
|
|
|
|
|
1998-11-19 15:45:00 +00:00
|
|
|
|
static void
|
1998-11-20 12:12:08 +00:00
|
|
|
|
initSerializerInfo(_NSSerializerInfo* info, NSMutableData *d, BOOL u)
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2000-10-31 11:05:23 +00:00
|
|
|
|
Class c;
|
1998-11-19 15:45:00 +00:00
|
|
|
|
|
2000-10-31 16:17:33 +00:00
|
|
|
|
c = GSObjCClass(d);
|
2005-02-22 11:22:44 +00:00
|
|
|
|
info->data = d;
|
2002-11-09 16:40:00 +00:00
|
|
|
|
info->appImp = (void (*)(NSData*,SEL,const void*,unsigned))get_imp(c, appSel);
|
|
|
|
|
info->datImp = (void* (*)(NSMutableData*,SEL))get_imp(c, datSel);
|
|
|
|
|
info->lenImp = (unsigned int (*)(NSData*,SEL))get_imp(c, lenSel);
|
|
|
|
|
info->serImp = (void (*)(NSMutableData*,SEL,int))get_imp(c, serSel);
|
|
|
|
|
info->setImp = (void (*)(NSMutableData*,SEL,unsigned))get_imp(c, setSel);
|
1998-11-20 12:12:08 +00:00
|
|
|
|
info->shouldUnique = u;
|
|
|
|
|
(*info->appImp)(d, appSel, &info->shouldUnique, 1);
|
|
|
|
|
if (u)
|
|
|
|
|
{
|
1999-06-21 08:30:26 +00:00
|
|
|
|
GSIMapInitWithZoneAndCapacity(&info->map, NSDefaultMallocZone(), 16);
|
1998-11-20 12:12:08 +00:00
|
|
|
|
info->count = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
endSerializerInfo(_NSSerializerInfo* info)
|
|
|
|
|
{
|
|
|
|
|
if (info->shouldUnique)
|
1999-06-21 08:30:26 +00:00
|
|
|
|
GSIMapEmptyMap(&info->map);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-06-24 19:30:29 +00:00
|
|
|
|
static void
|
1998-11-19 15:45:00 +00:00
|
|
|
|
serializeToInfo(id object, _NSSerializerInfo* info)
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2000-10-31 11:05:23 +00:00
|
|
|
|
Class c;
|
1998-11-19 15:45:00 +00:00
|
|
|
|
|
2000-10-31 16:17:33 +00:00
|
|
|
|
if (object == nil || GSObjCIsInstance(object) == NO)
|
1998-11-23 11:27:18 +00:00
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"Class (%@) in property list - expected instance",
|
2001-03-03 14:49:11 +00:00
|
|
|
|
[object description]];
|
1998-11-23 11:27:18 +00:00
|
|
|
|
}
|
2000-10-31 16:17:33 +00:00
|
|
|
|
c = GSObjCClass(object);
|
2003-11-05 13:52:18 +00:00
|
|
|
|
|
2006-05-20 14:52:38 +00:00
|
|
|
|
if (GSObjCIsKindOf(c, StringClass)
|
2003-11-05 13:52:18 +00:00
|
|
|
|
/*
|
|
|
|
|
We can only save it as a c-string if it only contains ASCII characters.
|
|
|
|
|
Other characters might be decoded incorrectly when deserialized since
|
|
|
|
|
the c-string encoding might be different then.
|
|
|
|
|
*/
|
2006-05-20 10:11:39 +00:00
|
|
|
|
&& [object canBeConvertedToEncoding: NSASCIIStringEncoding])
|
|
|
|
|
{
|
|
|
|
|
GSIMapNode node;
|
2006-05-20 09:27:31 +00:00
|
|
|
|
|
2006-05-20 10:11:39 +00:00
|
|
|
|
if (info->shouldUnique)
|
|
|
|
|
node = GSIMapNodeForKey(&info->map, (GSIMapKey)object);
|
|
|
|
|
else
|
|
|
|
|
node = 0;
|
|
|
|
|
if (node == 0)
|
|
|
|
|
{
|
|
|
|
|
unsigned slen;
|
|
|
|
|
unsigned dlen;
|
|
|
|
|
|
|
|
|
|
slen = [object length] + 1;
|
|
|
|
|
(*info->appImp)(info->data, appSel, &st_cstring, 1);
|
|
|
|
|
(*info->serImp)(info->data, serSel, slen);
|
|
|
|
|
dlen = (*info->lenImp)(info->data, lenSel);
|
|
|
|
|
(*info->setImp)(info->data, setSel, dlen + slen);
|
|
|
|
|
[object getCString: (*info->datImp)(info->data, datSel) + dlen
|
|
|
|
|
maxLength: slen
|
|
|
|
|
encoding: NSASCIIStringEncoding];
|
1998-11-20 12:12:08 +00:00
|
|
|
|
if (info->shouldUnique)
|
2006-05-20 10:11:39 +00:00
|
|
|
|
GSIMapAddPair(&info->map,
|
|
|
|
|
(GSIMapKey)object, (GSIMapVal)info->count++);
|
1998-11-20 12:12:08 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2006-05-20 10:11:39 +00:00
|
|
|
|
(*info->appImp)(info->data, appSel, &st_xref, 1);
|
|
|
|
|
(*info->serImp)(info->data, serSel, node->value.uint);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (GSObjCIsKindOf(c, StringClass))
|
|
|
|
|
{
|
|
|
|
|
GSIMapNode node;
|
1998-11-20 12:12:08 +00:00
|
|
|
|
|
2006-05-20 10:11:39 +00:00
|
|
|
|
if (info->shouldUnique)
|
|
|
|
|
node = GSIMapNodeForKey(&info->map, (GSIMapKey)object);
|
|
|
|
|
else
|
|
|
|
|
node = 0;
|
|
|
|
|
if (node == 0)
|
|
|
|
|
{
|
|
|
|
|
unsigned slen;
|
|
|
|
|
unsigned dlen;
|
|
|
|
|
|
|
|
|
|
slen = [object length];
|
|
|
|
|
(*info->appImp)(info->data, appSel, &st_string, 1);
|
|
|
|
|
(*info->serImp)(info->data, serSel, slen);
|
|
|
|
|
dlen = (*info->lenImp)(info->data, lenSel);
|
|
|
|
|
(*info->setImp)(info->data, setSel, dlen + slen*sizeof(unichar));
|
2006-05-20 09:27:31 +00:00
|
|
|
|
#if NEED_WORD_ALIGNMENT
|
2006-05-20 10:11:39 +00:00
|
|
|
|
/*
|
|
|
|
|
* When packing data, an item may not be aligned on a
|
|
|
|
|
* word boundary, so we work with an aligned buffer
|
|
|
|
|
* and use memcmpy()
|
|
|
|
|
*/
|
|
|
|
|
if ((dlen % __alignof__(uint32_t)) != 0)
|
2006-05-20 09:27:31 +00:00
|
|
|
|
{
|
2006-05-20 10:11:39 +00:00
|
|
|
|
unichar buffer[slen];
|
|
|
|
|
[object getCharacters: buffer];
|
|
|
|
|
memcpy((*info->datImp)(info->data, datSel) + dlen, buffer,
|
|
|
|
|
slen*sizeof(unichar));
|
2006-05-20 09:27:31 +00:00
|
|
|
|
}
|
2006-05-20 10:11:39 +00:00
|
|
|
|
else
|
|
|
|
|
#endif
|
|
|
|
|
[object getCharacters: (*info->datImp)(info->data, datSel) + dlen];
|
|
|
|
|
if (info->shouldUnique)
|
|
|
|
|
GSIMapAddPair(&info->map,
|
|
|
|
|
(GSIMapKey)object, (GSIMapVal)info->count++);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
(*info->appImp)(info->data, appSel, &st_xref, 1);
|
|
|
|
|
(*info->serImp)(info->data, serSel, node->value.uint);
|
1998-11-20 12:12:08 +00:00
|
|
|
|
}
|
1998-11-19 15:45:00 +00:00
|
|
|
|
}
|
2000-10-31 16:17:33 +00:00
|
|
|
|
else if (GSObjCIsKindOf(c, ArrayClass))
|
1998-11-19 15:45:00 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned int count;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1998-11-19 15:45:00 +00:00
|
|
|
|
if ([object isKindOfClass: MutableArrayClass])
|
|
|
|
|
(*info->appImp)(info->data, appSel, &st_marray, 1);
|
|
|
|
|
else
|
|
|
|
|
(*info->appImp)(info->data, appSel, &st_array, 1);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1998-11-19 15:45:00 +00:00
|
|
|
|
count = [object count];
|
|
|
|
|
(*info->serImp)(info->data, serSel, count);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1998-11-19 15:45:00 +00:00
|
|
|
|
if (count)
|
|
|
|
|
{
|
|
|
|
|
id objects[count];
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
2008-07-06 09:18:30 +00:00
|
|
|
|
if ([object isProxy])
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
objects[i] = [object objectAtIndex: i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
[object getObjects: objects];
|
|
|
|
|
}
|
1998-11-19 15:45:00 +00:00
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
serializeToInfo(objects[i], info);
|
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2000-10-31 16:17:33 +00:00
|
|
|
|
else if (GSObjCIsKindOf(c, DictionaryClass))
|
1998-11-19 15:45:00 +00:00
|
|
|
|
{
|
|
|
|
|
NSEnumerator *e = [object keyEnumerator];
|
|
|
|
|
id k;
|
|
|
|
|
IMP nxtImp;
|
|
|
|
|
IMP objImp;
|
|
|
|
|
|
|
|
|
|
nxtImp = [e methodForSelector: @selector(nextObject)];
|
|
|
|
|
objImp = [object methodForSelector: @selector(objectForKey:)];
|
|
|
|
|
|
|
|
|
|
if ([object isKindOfClass: MutableDictionaryClass])
|
|
|
|
|
(*info->appImp)(info->data, appSel, &st_mdict, 1);
|
|
|
|
|
else
|
|
|
|
|
(*info->appImp)(info->data, appSel, &st_dict, 1);
|
|
|
|
|
|
|
|
|
|
(*info->serImp)(info->data, serSel, [object count]);
|
|
|
|
|
while ((k = (*nxtImp)(e, @selector(nextObject))) != nil)
|
|
|
|
|
{
|
|
|
|
|
id o = (*objImp)(object, @selector(objectForKey:), k);
|
|
|
|
|
|
|
|
|
|
serializeToInfo(k, info);
|
|
|
|
|
serializeToInfo(o, info);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2000-10-31 16:17:33 +00:00
|
|
|
|
else if (GSObjCIsKindOf(c, DataClass))
|
1998-11-19 15:45:00 +00:00
|
|
|
|
{
|
|
|
|
|
(*info->appImp)(info->data, appSel, &st_data, 1);
|
|
|
|
|
(*info->serImp)(info->data, serSel, [object length]);
|
|
|
|
|
(*info->appImp)(info->data, appSel, [object bytes], [object length]);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
2002-11-10 11:46:49 +00:00
|
|
|
|
else if (GSObjCIsKindOf(c, DateClass))
|
|
|
|
|
{
|
|
|
|
|
NSTimeInterval ti = [object timeIntervalSinceReferenceDate];
|
|
|
|
|
|
|
|
|
|
(*info->appImp)(info->data, appSel, &st_date, 1);
|
|
|
|
|
[info->data serializeDataAt: &ti
|
|
|
|
|
ofObjCType: @encode(NSTimeInterval)
|
|
|
|
|
context: nil];
|
|
|
|
|
}
|
|
|
|
|
else if (GSObjCIsKindOf(c, NumberClass))
|
|
|
|
|
{
|
|
|
|
|
double d = [object doubleValue];
|
|
|
|
|
|
|
|
|
|
(*info->appImp)(info->data, appSel, &st_number, 1);
|
|
|
|
|
[info->data serializeDataAt: &d
|
|
|
|
|
ofObjCType: @encode(double)
|
|
|
|
|
context: nil];
|
|
|
|
|
}
|
1998-11-19 15:45:00 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
1998-11-23 11:27:18 +00:00
|
|
|
|
format: @"Unknown class (%@) in property list",
|
|
|
|
|
[c description]];
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1998-11-19 15:45:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@implementation NSSerializer
|
|
|
|
|
|
1998-12-07 11:39:40 +00:00
|
|
|
|
static BOOL shouldBeCompact = NO;
|
1998-11-20 12:12:08 +00:00
|
|
|
|
|
1998-11-19 15:45:00 +00:00
|
|
|
|
+ (void) initialize
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
1998-11-19 15:45:00 +00:00
|
|
|
|
if (self == [NSSerializer class])
|
|
|
|
|
{
|
2000-10-30 18:00:27 +00:00
|
|
|
|
appSel = @selector(appendBytes:length:);
|
|
|
|
|
datSel = @selector(mutableBytes);
|
|
|
|
|
lenSel = @selector(length);
|
|
|
|
|
serSel = @selector(serializeInt:);
|
|
|
|
|
setSel = @selector(setLength:);
|
1998-11-19 15:45:00 +00:00
|
|
|
|
ArrayClass = [NSArray class];
|
|
|
|
|
MutableArrayClass = [NSMutableArray class];
|
|
|
|
|
DataClass = [NSData class];
|
2002-11-10 11:46:49 +00:00
|
|
|
|
DateClass = [NSDate class];
|
|
|
|
|
NumberClass = [NSNumber class];
|
1998-11-19 15:45:00 +00:00
|
|
|
|
DictionaryClass = [NSDictionary class];
|
|
|
|
|
MutableDictionaryClass = [NSMutableDictionary class];
|
2000-10-09 04:41:18 +00:00
|
|
|
|
StringClass = [NSString class];
|
1998-11-19 15:45:00 +00:00
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2003-06-28 05:25:31 +00:00
|
|
|
|
/**
|
|
|
|
|
* Converts the supplied propertyList into a serialized representation
|
|
|
|
|
* in the returned NSData object.
|
|
|
|
|
*/
|
1998-11-19 15:45:00 +00:00
|
|
|
|
+ (NSData*) serializePropertyList: (id)propertyList
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
1998-11-19 15:45:00 +00:00
|
|
|
|
_NSSerializerInfo info;
|
1998-11-20 12:12:08 +00:00
|
|
|
|
NSMutableData *d;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1998-11-19 15:45:00 +00:00
|
|
|
|
NSAssert(propertyList != nil, NSInvalidArgumentException);
|
1998-11-20 12:12:08 +00:00
|
|
|
|
d = [NSMutableData dataWithCapacity: 1024];
|
|
|
|
|
initSerializerInfo(&info, d, shouldBeCompact);
|
1998-11-19 15:45:00 +00:00
|
|
|
|
serializeToInfo(propertyList, &info);
|
1998-11-20 12:12:08 +00:00
|
|
|
|
endSerializerInfo(&info);
|
1998-11-19 15:45:00 +00:00
|
|
|
|
return info.data;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2003-06-28 05:25:31 +00:00
|
|
|
|
/**
|
|
|
|
|
* Converts the supplied propertyList into a serialized representation
|
|
|
|
|
* stored in d.
|
|
|
|
|
*/
|
1998-11-19 15:45:00 +00:00
|
|
|
|
+ (void) serializePropertyList: (id)propertyList
|
|
|
|
|
intoData: (NSMutableData*)d
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
1998-11-19 15:45:00 +00:00
|
|
|
|
_NSSerializerInfo info;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1998-11-19 15:45:00 +00:00
|
|
|
|
NSAssert(propertyList != nil, NSInvalidArgumentException);
|
|
|
|
|
NSAssert(d != nil, NSInvalidArgumentException);
|
1998-11-20 12:12:08 +00:00
|
|
|
|
initSerializerInfo(&info, d, shouldBeCompact);
|
1998-11-19 15:45:00 +00:00
|
|
|
|
serializeToInfo(propertyList, &info);
|
1998-11-20 12:12:08 +00:00
|
|
|
|
endSerializerInfo(&info);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation NSSerializer (GNUstep)
|
|
|
|
|
+ (void) serializePropertyList: (id)propertyList
|
|
|
|
|
intoData: (NSMutableData*)d
|
|
|
|
|
compact: (BOOL)flag
|
|
|
|
|
{
|
|
|
|
|
_NSSerializerInfo info;
|
|
|
|
|
|
|
|
|
|
NSAssert(propertyList != nil, NSInvalidArgumentException);
|
|
|
|
|
NSAssert(d != nil, NSInvalidArgumentException);
|
|
|
|
|
initSerializerInfo(&info, d, flag);
|
|
|
|
|
serializeToInfo(propertyList, &info);
|
|
|
|
|
endSerializerInfo(&info);
|
|
|
|
|
}
|
2000-10-09 04:41:18 +00:00
|
|
|
|
|
1998-11-20 12:12:08 +00:00
|
|
|
|
+ (void) shouldBeCompact: (BOOL)flag
|
|
|
|
|
{
|
|
|
|
|
shouldBeCompact = flag;
|
1998-11-19 15:45:00 +00:00
|
|
|
|
}
|
|
|
|
|
@end
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1998-11-19 15:45:00 +00:00
|
|
|
|
|
|
|
|
|
|
2003-11-05 02:11:49 +00:00
|
|
|
|
static BOOL uniquing = NO; /* Make incoming strings unique */
|
|
|
|
|
|
1998-11-19 15:45:00 +00:00
|
|
|
|
/*
|
2003-11-05 02:11:49 +00:00
|
|
|
|
* Variables to cache class information. These are used to create instances
|
|
|
|
|
* when deserializing.
|
1998-11-19 15:45:00 +00:00
|
|
|
|
*/
|
1999-05-26 17:09:21 +00:00
|
|
|
|
static Class MACls = 0; /* Mutable Array */
|
|
|
|
|
static Class DCls = 0; /* Data */
|
|
|
|
|
static Class MDCls = 0; /* Mutable Dictionary */
|
1998-11-19 15:45:00 +00:00
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
NSData *data;
|
|
|
|
|
unsigned *cursor;
|
|
|
|
|
BOOL mutable;
|
1998-11-20 12:12:08 +00:00
|
|
|
|
BOOL didUnique;
|
1998-11-19 15:45:00 +00:00
|
|
|
|
void (*debImp)();
|
|
|
|
|
unsigned int (*deiImp)();
|
1999-06-21 08:30:26 +00:00
|
|
|
|
GSIArray_t array;
|
1998-11-19 15:45:00 +00:00
|
|
|
|
} _NSDeserializerInfo;
|
|
|
|
|
|
2000-10-30 18:00:27 +00:00
|
|
|
|
static SEL debSel;
|
|
|
|
|
static SEL deiSel;
|
|
|
|
|
static SEL dInitSel;
|
|
|
|
|
static SEL maInitSel;
|
|
|
|
|
static SEL mdInitSel;
|
2003-03-23 07:06:27 +00:00
|
|
|
|
static SEL maAddSel;
|
|
|
|
|
static SEL mdSetSel;
|
1999-05-26 17:09:21 +00:00
|
|
|
|
static IMP dInitImp;
|
|
|
|
|
static IMP maInitImp;
|
|
|
|
|
static IMP mdInitImp;
|
2003-03-23 07:06:27 +00:00
|
|
|
|
static IMP maAddImp;
|
|
|
|
|
static IMP mdSetImp;
|
1998-11-19 15:45:00 +00:00
|
|
|
|
|
2003-03-23 07:06:27 +00:00
|
|
|
|
static BOOL
|
1998-11-19 15:45:00 +00:00
|
|
|
|
initDeserializerInfo(_NSDeserializerInfo* info, NSData *d, unsigned *c, BOOL m)
|
|
|
|
|
{
|
2003-03-23 07:06:27 +00:00
|
|
|
|
unsigned char u;
|
|
|
|
|
|
1998-11-19 15:45:00 +00:00
|
|
|
|
info->data = d;
|
|
|
|
|
info->cursor = c;
|
|
|
|
|
info->mutable = m;
|
|
|
|
|
info->debImp = (void (*)())[d methodForSelector: debSel];
|
|
|
|
|
info->deiImp = (unsigned int (*)())[d methodForSelector: deiSel];
|
2003-03-23 07:06:27 +00:00
|
|
|
|
(*info->debImp)(d, debSel, &u, 1, c);
|
|
|
|
|
if (u == 0 || u == 1)
|
|
|
|
|
{
|
|
|
|
|
info->didUnique = u; // Old (current) format
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (u == 'G')
|
|
|
|
|
{
|
|
|
|
|
const unsigned char *b = [d bytes];
|
|
|
|
|
unsigned int l = [d length];
|
|
|
|
|
|
|
|
|
|
if (*c + 11 < l && memcmp(&b[*c-1], "GNUstepSer", 10) == 0)
|
|
|
|
|
{
|
2005-02-22 11:22:44 +00:00
|
|
|
|
*c += 9;
|
2003-03-23 07:06:27 +00:00
|
|
|
|
(*info->debImp)(d, debSel, &u, 1, c);
|
|
|
|
|
NSLog(@"Serialised data version %d not supported ..."
|
|
|
|
|
@" try another version of GNUstep");
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
NSLog(@"Bad serialised data");
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
1998-11-20 12:12:08 +00:00
|
|
|
|
if (info->didUnique)
|
2003-03-23 07:06:27 +00:00
|
|
|
|
{
|
|
|
|
|
GSIArrayInitWithZoneAndCapacity(&info->array, NSDefaultMallocZone(), 16);
|
|
|
|
|
}
|
|
|
|
|
return YES;
|
1998-11-20 12:12:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
endDeserializerInfo(_NSDeserializerInfo* info)
|
|
|
|
|
{
|
|
|
|
|
if (info->didUnique)
|
2005-06-30 07:44:45 +00:00
|
|
|
|
{
|
|
|
|
|
GSIArrayEmpty(&info->array);
|
|
|
|
|
}
|
1998-11-19 15:45:00 +00:00
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1998-11-19 15:45:00 +00:00
|
|
|
|
static id
|
|
|
|
|
deserializeFromInfo(_NSDeserializerInfo* info)
|
|
|
|
|
{
|
|
|
|
|
char code;
|
|
|
|
|
unsigned int size;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1998-11-19 15:45:00 +00:00
|
|
|
|
(*info->debImp)(info->data, debSel, &code, 1, info->cursor);
|
|
|
|
|
|
|
|
|
|
switch (code)
|
|
|
|
|
{
|
1998-11-20 12:12:08 +00:00
|
|
|
|
case ST_XREF:
|
2005-06-30 08:32:07 +00:00
|
|
|
|
if (info->didUnique)
|
|
|
|
|
{
|
|
|
|
|
size = (*info->deiImp)(info->data, deiSel, info->cursor);
|
|
|
|
|
if (size < GSIArrayCount(&info->array))
|
|
|
|
|
{
|
|
|
|
|
return RETAIN(GSIArrayItemAtIndex(&info->array, size).obj);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"Bad cross reference in property list"];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"Unexpected cross reference in property list"];
|
|
|
|
|
}
|
1998-11-20 12:12:08 +00:00
|
|
|
|
|
1998-11-19 15:45:00 +00:00
|
|
|
|
case ST_CSTRING:
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2006-05-20 14:52:38 +00:00
|
|
|
|
NSString *s;
|
2000-11-03 10:11:56 +00:00
|
|
|
|
char *b;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-12-04 14:44:06 +00:00
|
|
|
|
size = (*info->deiImp)(info->data, deiSel, info->cursor);
|
2009-02-11 17:33:31 +00:00
|
|
|
|
#if GS_WITH_GC
|
|
|
|
|
b = NSAllocateCollectable(size, 0);
|
|
|
|
|
#else
|
2000-11-03 10:11:56 +00:00
|
|
|
|
b = NSZoneMalloc(NSDefaultMallocZone(), size);
|
2009-02-11 17:33:31 +00:00
|
|
|
|
#endif
|
1998-11-19 15:45:00 +00:00
|
|
|
|
(*info->debImp)(info->data, debSel, b, size, info->cursor);
|
2006-05-20 14:52:38 +00:00
|
|
|
|
s = [[StringClass alloc] initWithBytesNoCopy: b
|
|
|
|
|
length: size - 1
|
|
|
|
|
encoding: NSASCIIStringEncoding
|
|
|
|
|
freeWhenDone: YES];
|
1999-06-02 09:32:16 +00:00
|
|
|
|
/*
|
|
|
|
|
* If we are supposed to be doing uniquing of strings, handle it.
|
|
|
|
|
*/
|
2000-04-18 09:02:38 +00:00
|
|
|
|
if (uniquing == YES)
|
2005-06-30 08:32:07 +00:00
|
|
|
|
{
|
|
|
|
|
s = GSUnique(s);
|
|
|
|
|
}
|
1999-06-02 09:32:16 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If uniquing was done on serialisation, store the string for
|
|
|
|
|
* later reference.
|
|
|
|
|
*/
|
1998-11-20 12:12:08 +00:00
|
|
|
|
if (info->didUnique)
|
2005-06-30 08:32:07 +00:00
|
|
|
|
{
|
2005-10-07 09:57:51 +00:00
|
|
|
|
GSIArrayAddItem(&info->array, (GSIArrayItem)((id)s));
|
2005-06-30 08:32:07 +00:00
|
|
|
|
}
|
1998-11-19 15:45:00 +00:00
|
|
|
|
return s;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-11-19 15:45:00 +00:00
|
|
|
|
case ST_STRING:
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2001-06-26 13:30:39 +00:00
|
|
|
|
NSString *s;
|
|
|
|
|
unichar *b;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-12-04 14:44:06 +00:00
|
|
|
|
size = (*info->deiImp)(info->data, deiSel, info->cursor);
|
2009-02-11 17:33:31 +00:00
|
|
|
|
#if GS_WITH_GC
|
|
|
|
|
b = NSAllocateCollectable(size*sizeof(unichar), 0);
|
|
|
|
|
#else
|
2000-11-03 10:11:56 +00:00
|
|
|
|
b = NSZoneMalloc(NSDefaultMallocZone(), size*sizeof(unichar));
|
2009-02-11 17:33:31 +00:00
|
|
|
|
#endif
|
2000-11-03 10:11:56 +00:00
|
|
|
|
(*info->debImp)(info->data, debSel, b, size*sizeof(unichar),
|
|
|
|
|
info->cursor);
|
2006-05-20 14:52:38 +00:00
|
|
|
|
s = [[StringClass alloc] initWithBytesNoCopy: b
|
|
|
|
|
length: size*sizeof(unichar)
|
|
|
|
|
encoding: NSUnicodeStringEncoding
|
|
|
|
|
freeWhenDone: YES];
|
1999-06-02 09:32:16 +00:00
|
|
|
|
/*
|
|
|
|
|
* If we are supposed to be doing uniquing of strings, handle it.
|
|
|
|
|
*/
|
2000-04-18 09:02:38 +00:00
|
|
|
|
if (uniquing == YES)
|
2005-06-30 08:32:07 +00:00
|
|
|
|
{
|
|
|
|
|
s = GSUnique(s);
|
|
|
|
|
}
|
1999-06-02 09:32:16 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If uniquing was done on serialisation, store the string for
|
|
|
|
|
* later reference.
|
|
|
|
|
*/
|
1998-11-20 12:12:08 +00:00
|
|
|
|
if (info->didUnique)
|
2005-06-30 08:32:07 +00:00
|
|
|
|
{
|
2005-10-07 09:57:51 +00:00
|
|
|
|
GSIArrayAddItem(&info->array, (GSIArrayItem)((id)s));
|
2005-06-30 08:32:07 +00:00
|
|
|
|
}
|
1998-11-19 15:45:00 +00:00
|
|
|
|
return s;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-11-19 15:45:00 +00:00
|
|
|
|
case ST_ARRAY:
|
|
|
|
|
case ST_MARRAY:
|
2002-12-04 14:44:06 +00:00
|
|
|
|
size = (*info->deiImp)(info->data, deiSel, info->cursor);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
1999-05-26 17:09:21 +00:00
|
|
|
|
id a;
|
1998-11-19 15:45:00 +00:00
|
|
|
|
|
2003-03-23 07:06:27 +00:00
|
|
|
|
a = NSAllocateObject(MACls, 0, NSDefaultMallocZone());
|
|
|
|
|
a = (*maInitImp)(a, maInitSel, size);
|
|
|
|
|
if (a != nil)
|
1998-11-19 15:45:00 +00:00
|
|
|
|
{
|
2003-03-23 07:06:27 +00:00
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < size; i++)
|
1998-11-19 15:45:00 +00:00
|
|
|
|
{
|
2003-03-23 07:06:27 +00:00
|
|
|
|
id o = deserializeFromInfo(info);
|
|
|
|
|
|
|
|
|
|
if (o == nil)
|
1998-11-19 15:45:00 +00:00
|
|
|
|
{
|
2003-03-23 07:06:27 +00:00
|
|
|
|
RELEASE(a);
|
|
|
|
|
return nil;
|
1998-11-19 15:45:00 +00:00
|
|
|
|
}
|
2003-03-23 07:06:27 +00:00
|
|
|
|
(*maAddImp)(a, maAddSel, o);
|
|
|
|
|
RELEASE(o);
|
|
|
|
|
}
|
|
|
|
|
if (code != ST_MARRAY && info->mutable == NO)
|
|
|
|
|
{
|
|
|
|
|
[a makeImmutableCopyOnFail: NO];
|
1998-11-19 15:45:00 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return a;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-11-19 15:45:00 +00:00
|
|
|
|
case ST_DICT:
|
|
|
|
|
case ST_MDICT:
|
2002-12-04 14:44:06 +00:00
|
|
|
|
size = (*info->deiImp)(info->data, deiSel, info->cursor);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2003-01-03 20:14:47 +00:00
|
|
|
|
id d;
|
1998-11-19 15:45:00 +00:00
|
|
|
|
|
2003-03-23 07:06:27 +00:00
|
|
|
|
d = NSAllocateObject(MDCls, 0, NSDefaultMallocZone());
|
|
|
|
|
d = (*mdInitImp)(d, mdInitSel, size);
|
|
|
|
|
if (d != nil)
|
1998-11-19 15:45:00 +00:00
|
|
|
|
{
|
2003-03-23 07:06:27 +00:00
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < size; i++)
|
1998-11-19 15:45:00 +00:00
|
|
|
|
{
|
2003-03-23 07:06:27 +00:00
|
|
|
|
id k = deserializeFromInfo(info);
|
|
|
|
|
|
|
|
|
|
if (k == nil)
|
1998-11-19 15:45:00 +00:00
|
|
|
|
{
|
2003-03-23 07:06:27 +00:00
|
|
|
|
RELEASE(d);
|
|
|
|
|
return nil;
|
1998-11-19 15:45:00 +00:00
|
|
|
|
}
|
2003-03-23 07:06:27 +00:00
|
|
|
|
else
|
1998-11-19 15:45:00 +00:00
|
|
|
|
{
|
2003-03-23 07:06:27 +00:00
|
|
|
|
id o = deserializeFromInfo(info);
|
|
|
|
|
|
|
|
|
|
if (o == nil)
|
|
|
|
|
{
|
|
|
|
|
RELEASE(k);
|
|
|
|
|
RELEASE(d);
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
(*mdSetImp)(d, mdSetSel, o, k);
|
2006-05-22 09:57:21 +00:00
|
|
|
|
/*
|
|
|
|
|
* Since a dictionary copies its keys rather
|
|
|
|
|
* than retaining them, we must autorelease
|
|
|
|
|
* rather than simply releasing as the key may
|
|
|
|
|
* be referred to by a cross-reference later.
|
|
|
|
|
*/
|
2009-01-12 12:48:46 +00:00
|
|
|
|
IF_NO_GC(AUTORELEASE(k);)
|
2003-03-23 07:06:27 +00:00
|
|
|
|
RELEASE(o);
|
|
|
|
|
}
|
1998-11-19 15:45:00 +00:00
|
|
|
|
}
|
2003-03-23 07:06:27 +00:00
|
|
|
|
}
|
|
|
|
|
if (code != ST_MDICT && info->mutable == NO)
|
|
|
|
|
{
|
|
|
|
|
[d makeImmutableCopyOnFail: NO];
|
1998-11-19 15:45:00 +00:00
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
1998-11-19 15:45:00 +00:00
|
|
|
|
return d;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case ST_DATA:
|
|
|
|
|
{
|
|
|
|
|
NSData *d;
|
2000-09-27 15:26:16 +00:00
|
|
|
|
|
2002-12-04 14:44:06 +00:00
|
|
|
|
size = (*info->deiImp)(info->data, deiSel, info->cursor);
|
1999-05-26 17:09:21 +00:00
|
|
|
|
d = (NSData*)NSAllocateObject(DCls, 0, NSDefaultMallocZone());
|
2000-09-27 15:26:16 +00:00
|
|
|
|
if (size > 0)
|
|
|
|
|
{
|
|
|
|
|
void *b = NSZoneMalloc(NSDefaultMallocZone(), size);
|
|
|
|
|
|
|
|
|
|
(*info->debImp)(info->data, debSel, b, size, info->cursor);
|
|
|
|
|
d = (*dInitImp)(d, dInitSel, b, size);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
d = (*dInitImp)(d, dInitSel, 0, 0);
|
|
|
|
|
}
|
1998-11-19 15:45:00 +00:00
|
|
|
|
return d;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-11-10 11:46:49 +00:00
|
|
|
|
case ST_DATE:
|
|
|
|
|
{
|
|
|
|
|
NSTimeInterval ti;
|
|
|
|
|
|
|
|
|
|
[info->data deserializeDataAt: &ti
|
|
|
|
|
ofObjCType: @encode(NSTimeInterval)
|
|
|
|
|
atCursor: info->cursor
|
|
|
|
|
context: nil];
|
|
|
|
|
return [[NSDate alloc] initWithTimeIntervalSinceReferenceDate: ti];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case ST_NUMBER:
|
|
|
|
|
{
|
|
|
|
|
double d;
|
|
|
|
|
|
|
|
|
|
[info->data deserializeDataAt: &d
|
|
|
|
|
ofObjCType: @encode(double)
|
|
|
|
|
atCursor: info->cursor
|
|
|
|
|
context: nil];
|
|
|
|
|
return [[NSNumber alloc] initWithDouble: d];
|
|
|
|
|
}
|
|
|
|
|
|
1998-11-19 15:45:00 +00:00
|
|
|
|
default:
|
2005-06-30 08:32:07 +00:00
|
|
|
|
break;
|
1998-11-19 15:45:00 +00:00
|
|
|
|
}
|
2005-06-30 08:32:07 +00:00
|
|
|
|
return nil;
|
1998-11-19 15:45:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@interface _NSDeserializerProxy : NSProxy
|
|
|
|
|
{
|
|
|
|
|
_NSDeserializerInfo info;
|
|
|
|
|
id plist;
|
|
|
|
|
}
|
|
|
|
|
+ (_NSDeserializerProxy*) proxyWithData: (NSData*)d
|
|
|
|
|
atCursor: (unsigned int*)c
|
|
|
|
|
mutable: (BOOL)m;
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation _NSDeserializerProxy
|
|
|
|
|
+ (_NSDeserializerProxy*) proxyWithData: (NSData*)d
|
|
|
|
|
atCursor: (unsigned int*)c
|
|
|
|
|
mutable: (BOOL)m
|
|
|
|
|
{
|
|
|
|
|
_NSDeserializerProxy *proxy;
|
|
|
|
|
|
|
|
|
|
proxy = (_NSDeserializerProxy*)NSAllocateObject(self,0,NSDefaultMallocZone());
|
2003-03-23 07:06:27 +00:00
|
|
|
|
if (initDeserializerInfo(&proxy->info, RETAIN(d), c, m) == YES)
|
|
|
|
|
{
|
|
|
|
|
return AUTORELEASE(proxy);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
DESTROY(proxy);
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
1998-11-19 15:45:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
|
{
|
1999-07-03 19:59:44 +00:00
|
|
|
|
RELEASE(info.data);
|
1998-11-20 12:12:08 +00:00
|
|
|
|
endDeserializerInfo(&info);
|
1999-07-03 19:59:44 +00:00
|
|
|
|
RELEASE(plist);
|
1998-11-19 15:45:00 +00:00
|
|
|
|
[super dealloc];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- forward: (SEL)aSel :(arglist_t)frame
|
|
|
|
|
{
|
|
|
|
|
if (plist == nil && info.data != nil)
|
|
|
|
|
{
|
|
|
|
|
plist = deserializeFromInfo(&info);
|
1999-07-03 19:59:44 +00:00
|
|
|
|
RELEASE(info.data);
|
1998-11-19 15:45:00 +00:00
|
|
|
|
info.data = nil;
|
|
|
|
|
}
|
|
|
|
|
return [plist performv: aSel :frame];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL) isEqual: (id)other
|
|
|
|
|
{
|
|
|
|
|
if (other == self)
|
|
|
|
|
return YES;
|
|
|
|
|
else
|
|
|
|
|
return [[self self] isEqual: other];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id) self
|
|
|
|
|
{
|
|
|
|
|
if (plist == nil && info.data != nil)
|
|
|
|
|
{
|
|
|
|
|
plist = deserializeFromInfo(&info);
|
1999-07-03 19:59:44 +00:00
|
|
|
|
RELEASE(info.data);
|
1998-11-19 15:45:00 +00:00
|
|
|
|
info.data = nil;
|
|
|
|
|
}
|
|
|
|
|
return plist;
|
|
|
|
|
}
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@implementation NSDeserializer
|
|
|
|
|
|
|
|
|
|
+ (void) initialize
|
|
|
|
|
{
|
|
|
|
|
if (self == [NSDeserializer class])
|
|
|
|
|
{
|
2000-10-30 18:00:27 +00:00
|
|
|
|
debSel = @selector(deserializeBytes:length:atCursor:);
|
|
|
|
|
deiSel = @selector(deserializeIntAtCursor:);
|
|
|
|
|
dInitSel = @selector(initWithBytesNoCopy:length:);
|
2003-03-23 07:06:27 +00:00
|
|
|
|
maInitSel = @selector(initWithCapacity:);
|
|
|
|
|
mdInitSel = @selector(initWithCapacity:);
|
|
|
|
|
maAddSel = @selector(addObject:);
|
|
|
|
|
mdSetSel = @selector(setObject:forKey:);
|
2001-01-08 16:45:36 +00:00
|
|
|
|
MACls = [GSMutableArray class];
|
1999-05-26 17:09:21 +00:00
|
|
|
|
DCls = [NSDataMalloc class];
|
2001-01-09 09:17:31 +00:00
|
|
|
|
MDCls = [GSMutableDictionary class];
|
1999-05-26 17:09:21 +00:00
|
|
|
|
dInitImp = [DCls instanceMethodForSelector: dInitSel];
|
|
|
|
|
maInitImp = [MACls instanceMethodForSelector: maInitSel];
|
|
|
|
|
mdInitImp = [MDCls instanceMethodForSelector: mdInitSel];
|
2003-03-23 07:06:27 +00:00
|
|
|
|
maAddImp = [MACls instanceMethodForSelector: maAddSel];
|
|
|
|
|
mdSetImp = [MDCls instanceMethodForSelector: mdSetSel];
|
2006-05-20 14:52:38 +00:00
|
|
|
|
StringClass = [NSString class];
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-06-28 05:25:31 +00:00
|
|
|
|
/**
|
|
|
|
|
* <p>Deserializes the property list stored in data at the offset specified
|
|
|
|
|
* by the value pointed to by cursor. Upon completion the value in cursor
|
|
|
|
|
* is updated to refer to a position immediately after the end of the
|
|
|
|
|
* deserialized sequence.
|
|
|
|
|
* </p>
|
|
|
|
|
* <p>The flag is used to specify whether container objects in the
|
|
|
|
|
* deserialized list should be instances of mutable or immutable classes.
|
|
|
|
|
* </p>
|
|
|
|
|
*/
|
1998-11-19 15:45:00 +00:00
|
|
|
|
+ (id) deserializePropertyListFromData: (NSData*)data
|
|
|
|
|
atCursor: (unsigned int*)cursor
|
|
|
|
|
mutableContainers: (BOOL)flag
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
1998-11-19 15:45:00 +00:00
|
|
|
|
_NSDeserializerInfo info;
|
|
|
|
|
id o;
|
|
|
|
|
|
1999-12-21 14:39:56 +00:00
|
|
|
|
if (data == nil || [data isKindOfClass: [NSData class]] == NO)
|
|
|
|
|
{
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
1998-11-19 15:45:00 +00:00
|
|
|
|
NSAssert(cursor != 0, NSInvalidArgumentException);
|
2003-03-23 07:06:27 +00:00
|
|
|
|
if (initDeserializerInfo(&info, data, cursor, flag) == YES)
|
|
|
|
|
{
|
|
|
|
|
o = deserializeFromInfo(&info);
|
|
|
|
|
endDeserializerInfo(&info);
|
|
|
|
|
return AUTORELEASE(o);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
1998-11-19 15:45:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2003-06-28 05:25:31 +00:00
|
|
|
|
/**
|
|
|
|
|
* <p>Deserializes the property list stored in data to produce a single
|
|
|
|
|
* property list object (dictionary, array, string, data, number or date).
|
|
|
|
|
* </p>
|
|
|
|
|
* <p>The flag is used to specify whether container objects in the
|
|
|
|
|
* deserialized list should be instances of mutable or immutable classes.
|
|
|
|
|
* </p>
|
|
|
|
|
*/
|
1998-11-19 15:45:00 +00:00
|
|
|
|
+ (id) deserializePropertyListFromData: (NSData*)data
|
|
|
|
|
mutableContainers: (BOOL)flag
|
|
|
|
|
{
|
|
|
|
|
_NSDeserializerInfo info;
|
|
|
|
|
unsigned int cursor = 0;
|
|
|
|
|
id o;
|
|
|
|
|
|
1999-12-21 14:39:56 +00:00
|
|
|
|
if (data == nil || [data isKindOfClass: [NSData class]] == NO)
|
|
|
|
|
{
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
2003-03-23 07:06:27 +00:00
|
|
|
|
if (initDeserializerInfo(&info, data, &cursor, flag) == YES)
|
|
|
|
|
{
|
|
|
|
|
o = deserializeFromInfo(&info);
|
|
|
|
|
endDeserializerInfo(&info);
|
|
|
|
|
return AUTORELEASE(o);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
1998-11-19 15:45:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2003-06-28 05:25:31 +00:00
|
|
|
|
/**
|
|
|
|
|
* <p>Deserializes the property list stored in data at the offset specified
|
|
|
|
|
* by the value pointed to by cursor. Upon completion the value in cursor
|
|
|
|
|
* is updated to refer to a position immediately after the end of the
|
|
|
|
|
* deserialized sequence.
|
|
|
|
|
* </p>
|
|
|
|
|
* <p>The flag is used to specify whether container objects in the
|
|
|
|
|
* deserialized list should be instances of mutable or immutable classes.
|
|
|
|
|
* </p>
|
|
|
|
|
* <p>The length is used to determine whether lazy deserialization is done,
|
|
|
|
|
* if the data is longer than this value, a proxy is returned rather than
|
|
|
|
|
* the actual property list, and the real deserialization can be done
|
|
|
|
|
* later.
|
|
|
|
|
* </p>
|
|
|
|
|
*/
|
1998-11-19 15:45:00 +00:00
|
|
|
|
+ (id) deserializePropertyListLazilyFromData: (NSData*)data
|
|
|
|
|
atCursor: (unsigned*)cursor
|
|
|
|
|
length: (unsigned)length
|
|
|
|
|
mutableContainers: (BOOL)flag
|
|
|
|
|
{
|
1999-12-21 14:39:56 +00:00
|
|
|
|
if (data == nil || [data isKindOfClass: [NSData class]] == NO)
|
|
|
|
|
{
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
1998-11-19 15:45:00 +00:00
|
|
|
|
NSAssert(cursor != 0, NSInvalidArgumentException);
|
|
|
|
|
if (length > [data length] - *cursor)
|
|
|
|
|
{
|
|
|
|
|
_NSDeserializerInfo info;
|
|
|
|
|
id o;
|
|
|
|
|
|
2003-03-23 07:06:27 +00:00
|
|
|
|
if (initDeserializerInfo(&info, data, cursor, flag) == YES)
|
|
|
|
|
{
|
|
|
|
|
o = deserializeFromInfo(&info);
|
|
|
|
|
endDeserializerInfo(&info);
|
|
|
|
|
return AUTORELEASE(o);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
1998-11-19 15:45:00 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return [_NSDeserializerProxy proxyWithData: data
|
|
|
|
|
atCursor: cursor
|
|
|
|
|
mutable: flag];
|
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
@end
|
|
|
|
|
|
2000-04-18 09:02:38 +00:00
|
|
|
|
@implementation NSDeserializer (GNUstep)
|
2002-08-20 10:22:05 +00:00
|
|
|
|
/**
|
|
|
|
|
* This method turns on/off uniquing of strings as they are
|
|
|
|
|
* deserialized from data objects. The uniquing mechanism
|
|
|
|
|
* employs the GNUstep-specific functions documented with
|
|
|
|
|
* the NSCountedSet class.
|
|
|
|
|
*/
|
1999-06-02 09:32:16 +00:00
|
|
|
|
+ (void) uniquing: (BOOL)flag
|
|
|
|
|
{
|
2000-04-18 09:02:38 +00:00
|
|
|
|
if (flag == YES)
|
|
|
|
|
GSUniquing(YES);
|
|
|
|
|
uniquing = flag;
|
1999-06-02 09:32:16 +00:00
|
|
|
|
}
|
|
|
|
|
@end
|
|
|
|
|
|