NSSerialisation rewrite.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@3242 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-MacDonald 1998-11-19 15:45:00 +00:00
parent 273e6d76d4
commit 71e18a67f4
4 changed files with 486 additions and 311 deletions

View file

@ -1,3 +1,11 @@
Thu Nov 19 16:00:00 1998 Richard Frith-Macdonald <richard@brainstorm.co.uk>
* src/include/NSSerializer.h: Removed erroneous protocol conformance
for NSSerializer and NSDeserializer.
* src/NSSerializer.m: Complete rewrite - full OpenStep compatibility
and huge speed increase.
* doc/todo.tmpl.texi: Update todo list.
Thu Nov 19 12:30:00 1998 Richard Frith-Macdonald <richard@brainstorm.co.uk>
* src/NSData.m: ([-encodeWithCoder:]) bugfix for case where data object

View file

@ -12,17 +12,12 @@
@item Implement Formatter classes (NSDateFormatter, etc) [5] (980722).
@item Implement NSDecimalNumber stuff [5] (981119).
@item Check that every class implements coding (correctly). [2, NSCoder] (980721).
@item Check that every class implements copying (correctly) - i.e. non-mutable classes should just retain, etc. [1, NSCopying] (980721).
@item Implement [NSDeserializer +deserializePropertyListLazilyFromData:atCursor:length:mutableContainers:]
[3 NSProxy] (980712)
@item Implement [NSDeserialiser - deserializeObjectAt:ofObjCType:fromData:atCursor:] [1] (980712)
@item Implement [NSSerialiser - serializeObjectAt:ofObjCType:fromData:] [1] (980712)
@item Improve initWithFormat releated methods for NSString and find out how to implemente the locale stuff [4 OPENSTEP/Rhapsody] (980712)
@item Make gstep-base 64bit clean [5, 64bit machine] (980629)
@ -41,7 +36,7 @@ an exception. [4, exceptions, threads] (980220)
@item Fix all the places marked FIXME or xxx. [3-9] (980629)
@item Write tests for various classes using the testsuite build
@item Write tests for various classes using the testsuite built
by Richard Frith-Macdonald @email{richard@@brainstorm.co.uk}. Contact
him for details. [3, guile] (980629)

View file

@ -4,7 +4,7 @@
Written by: Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
Date: 1995
Updated by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
Date: 1997
Date: 1998
This file is part of the GNUstep Base Library.
@ -38,27 +38,22 @@
intoData: (NSMutableData*)data;
@end
@interface NSSerializer: NSObject <NSObjCTypeSerializationCallBack>
{
}
@interface NSSerializer: NSObject
+ (NSData*) serializePropertyList: (id)propertyList;
+ (void) serializePropertyList: (id)propertyList
intoData: (NSMutableData*)d;
@end
@interface NSDeserializer: NSObject <NSObjCTypeSerializationCallBack>
{
BOOL mutableContainer;
}
@interface NSDeserializer: NSObject
+ (id) deserializePropertyListFromData: (NSData*)data
atCursor: (unsigned int*)cursor
mutableContainers: (BOOL)flag;
+ (id) deserializePropertyListFromData: (NSData*)data
mutableContainers: (BOOL)flag;
+ (id) deserializePropertyListLazilyFromData: (NSData*)data
atCursor: (unsigned*)cursor
length: (unsigned)length
mutableContainers: (BOOL)flag;
atCursor: (unsigned*)cursor
length: (unsigned)length
mutableContainers: (BOOL)flag;
@end

View file

@ -23,342 +23,519 @@
#include <config.h>
#include <gnustep/base/preface.h>
#include <gnustep/base/fast.x>
#include <gnustep/base/mframe.h>
#include <Foundation/NSData.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSArray.h>
#include <Foundation/NSString.h>
#include <Foundation/NSException.h>
#include <Foundation/NSProxy.h>
@class NSGMutableCString;
@class NSGCString;
#ifdef UNICODE
@class NSGString;
@class NSGMutableString;
#endif
@class NSGArray;
@class NSGMutableArray;
@class NSGDictionary;
@class NSGMutableDictionary;
@class NSDataMalloc;
typedef enum {
ST_CSTRING,
ST_MCSTRING,
ST_STRING,
ST_MSTRING,
ST_DATA,
ST_MDATA,
ST_ARRAY,
ST_MARRAY,
ST_DICT,
ST_MDICT
} SerializerType;
/*
* Define constants for data types and variables to hold them.
*/
#define ST_CSTRING 0
#define ST_STRING 1
#define ST_ARRAY 2
#define ST_MARRAY 3
#define ST_DICT 4
#define ST_MDICT 5
#define ST_DATA 6
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;
/*
* Variables to cache class information.
*/
static Class ArrayClass = 0;
static Class MutableArrayClass = 0;
static Class DataClass = 0;
static Class DictionaryClass = 0;
static Class MutableDictionaryClass = 0;
typedef struct {
NSMutableData *data;
void (*appImp)(); // Append to data.
void* (*datImp)(); // Bytes pointer.
unsigned int (*lenImp)(); // Length of data.
void (*serImp)(); // Serialize integer.
void (*setImp)(); // Set length of data.
} _NSSerializerInfo;
static SEL appSel = @selector(appendBytes:length:);
static SEL datSel = @selector(mutableBytes);
static SEL lenSel = @selector(length);
static SEL serSel = @selector(serializeInt:);
static SEL setSel = @selector(setLength:);
static void
initSerializerInfo(_NSSerializerInfo* info, NSMutableData *d)
{
Class c = fastClass(d);
info->data = d;
info->appImp = (void (*)())get_imp(c, appSel);
info->datImp = (void* (*)())get_imp(c, datSel);
info->lenImp = (unsigned int (*)())get_imp(c, lenSel);
info->serImp = (void (*)())get_imp(c, serSel);
info->setImp = (void (*)())get_imp(c, setSel);
}
static id
serializeToInfo(id object, _NSSerializerInfo* info)
{
Class c = fastClass(object);
if (c == _fastCls._NSGCString || c == _fastCls._NSGMutableCString ||
c == _fastCls._NXConstantString)
{
unsigned slen = [object cStringLength] + 1;
unsigned dlen;
(*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];
}
else if (fastClassIsKindOfClass(c, _fastCls._NSString))
{
unsigned slen = [object length];
unsigned dlen;
(*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));
[object getCharacters: (*info->datImp)(info->data, datSel) + dlen];
}
else if (fastClassIsKindOfClass(c, ArrayClass))
{
unsigned int count;
if ([object isKindOfClass: MutableArrayClass])
(*info->appImp)(info->data, appSel, &st_marray, 1);
else
(*info->appImp)(info->data, appSel, &st_array, 1);
count = [object count];
(*info->serImp)(info->data, serSel, count);
if (count)
{
id objects[count];
unsigned int i;
[object getObjects: objects];
for (i = 0; i < count; i++)
{
serializeToInfo(objects[i], info);
}
}
}
else if (fastClassIsKindOfClass(c, DictionaryClass))
{
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);
}
}
else if (fastClassIsKindOfClass(c, DataClass))
{
(*info->appImp)(info->data, appSel, &st_data, 1);
(*info->serImp)(info->data, serSel, [object length]);
(*info->appImp)(info->data, appSel, [object bytes], [object length]);
}
else
{
[NSException raise: NSInvalidArgumentException
format: @"Unknown class in property list"];
}
}
@implementation NSSerializer
+ (void) initialize
{
if (self == [NSSerializer class])
{
ArrayClass = [NSArray class];
MutableArrayClass = [NSMutableArray class];
DataClass = [NSData class];
DictionaryClass = [NSDictionary class];
MutableDictionaryClass = [NSMutableDictionary class];
}
}
+ (NSData*) serializePropertyList: (id)propertyList
{
NSMutableData* d = [NSMutableData new];
_NSSerializerInfo info;
[self serializePropertyList: propertyList intoData: d];
return [d autorelease];
NSAssert(propertyList != nil, NSInvalidArgumentException);
initSerializerInfo(&info, [NSMutableData dataWithCapacity: 1024]);
serializeToInfo(propertyList, &info);
return info.data;
}
+ (void) serializePropertyList: (id)propertyList
intoData: (NSMutableData*)d
{
NSSerializer* s = [self new];
_NSSerializerInfo info;
[d serializeDataAt: &propertyList
ofObjCType: @encode(id)
context: s];
[s release];
}
- (void) deserializeObjectAt: (id*)object
ofObjCType: (const char *)type
fromData: (NSData*)data
atCursor: (unsigned*)cursor
{
[self shouldNotImplement: _cmd];
}
- (void) serializeObjectAt: (id*)objPtr
ofObjCType: (const char *)type
intoData: (NSMutableData*)data
{
id object;
assert(objPtr != 0);
assert(type == @encode(id));
object = *objPtr;
if ([object isKindOfClass: [NSMutableData class]]) {
[data serializeInt: ST_MDATA];
[data serializeInt: [object length]];
[data appendBytes: [object bytes] length: [object length]];
}
else if ([object isKindOfClass: [NSData class]]) {
[data serializeInt: ST_DATA];
[data serializeInt: [object length]];
[data appendBytes: [object bytes] length: [object length]];
}
else if ([object isKindOfClass: [NSGMutableCString class]]) {
[data serializeInt: ST_MCSTRING];
[data serializeInt: [object cStringLength]];
[data appendBytes: [object cString] length: [object cStringLength]];
}
else if ([object isKindOfClass: [NSGCString class]]) {
[data serializeInt: ST_CSTRING];
[data serializeInt: [object cStringLength]];
[data appendBytes: [object cString] length: [object cStringLength]];
}
#ifdef UNICODE
else if ([object isKindOfClass: [NSMutableString class]] ||
[object isKindOfClass: [NSGMutableString class]]) {
[data serializeInt: ST_MSTRING];
[data serializeInt: [object cStringLength]];
[data appendBytes: [object cString] length: [object cStringLength]];
}
else if ([object isKindOfClass: [NSString class]] ||
[object isKindOfClass: [NSGString class]]) {
[data serializeInt: ST_STRING];
[data serializeInt: [object cStringLength]];
[data appendBytes: [object cString] length: [object cStringLength]];
}
#endif
else if ([object isKindOfClass: [NSMutableArray class]] ||
[object isKindOfClass: [NSGMutableArray class]]) {
unsigned int i;
[data serializeInt: ST_MARRAY];
[data serializeInt: [object count]];
for (i = 0; i < [object count]; i++) {
id o = [object objectAtIndex: i];
[data serializeDataAt: &o
ofObjCType: @encode(id)
context: self];
}
}
else if ([object isKindOfClass: [NSArray class]] ||
[object isKindOfClass: [NSGArray class]]) {
unsigned int i;
[data serializeInt: ST_ARRAY];
[data serializeInt: [object count]];
for (i = 0; i < [object count]; i++) {
id o = [object objectAtIndex: i];
[data serializeDataAt: &o
ofObjCType: @encode(id)
context: self];
}
}
else if ([object isKindOfClass: [NSMutableDictionary class]] ||
[object isKindOfClass: [NSGMutableDictionary class]]) {
NSEnumerator* e = [object keyEnumerator];
id k;
[data serializeInt: ST_MDICT];
[data serializeInt: [object count]];
while ((k = [e nextObject]) != nil) {
id o = [object objectForKey:k];
[data serializeDataAt: &k
ofObjCType: @encode(id)
context: self];
[data serializeDataAt: &o
ofObjCType: @encode(id)
context: self];
}
}
else if ([object isKindOfClass: [NSDictionary class]] ||
[object isKindOfClass: [NSGDictionary class]]) {
NSEnumerator* e = [object keyEnumerator];
id k;
[data serializeInt: ST_DICT];
[data serializeInt: [object count]];
while ((k = [e nextObject]) != nil) {
id o = [object objectForKey:k];
[data serializeDataAt: &k
ofObjCType: @encode(id)
context: self];
[data serializeDataAt: &o
ofObjCType: @encode(id)
context: self];
}
}
else {
[NSException raise: NSGenericException
format: @"Unknown class in property list"];
}
NSAssert(propertyList != nil, NSInvalidArgumentException);
NSAssert(d != nil, NSInvalidArgumentException);
initSerializerInfo(&info, d);
serializeToInfo(propertyList, &info);
}
@end
/*
* Variables to cache class information.
*/
static Class GArrayClass = 0;
static Class GMutableArrayClass = 0;
static Class GDataClass = 0;
static Class GDictionaryClass = 0;
static Class GMutableDictionaryClass = 0;
typedef struct {
NSData *data;
unsigned *cursor;
BOOL mutable;
void (*debImp)();
unsigned int (*deiImp)();
} _NSDeserializerInfo;
static SEL debSel = @selector(deserializeBytes:length:atCursor:);
static SEL deiSel = @selector(deserializeIntAtCursor:);
static void
initDeserializerInfo(_NSDeserializerInfo* info, NSData *d, unsigned *c, BOOL m)
{
info->data = d;
info->cursor = c;
info->mutable = m;
info->debImp = (void (*)())[d methodForSelector: debSel];
info->deiImp = (unsigned int (*)())[d methodForSelector: deiSel];
}
static id
deserializeFromInfo(_NSDeserializerInfo* info)
{
char code;
unsigned int size;
(*info->debImp)(info->data, debSel, &code, 1, info->cursor);
size = (*info->deiImp)(info->data, deiSel, info->cursor);
switch (code)
{
case ST_CSTRING:
{
NSGCString *s;
char *b = objc_malloc(size);
(*info->debImp)(info->data, debSel, b, size, info->cursor);
s = [_fastCls._NSGCString allocWithZone: NSDefaultMallocZone()];
s = [s initWithCStringNoCopy: b
length: size-1
fromZone: NSDefaultMallocZone()];
return s;
}
case ST_STRING:
{
NSGString *s;
unichar *b = objc_malloc(size*2);
(*info->debImp)(info->data, debSel, b, size*2, info->cursor);
s = [_fastCls._NSGString allocWithZone: NSDefaultMallocZone()];
s = [s initWithCharactersNoCopy: b
length: size
fromZone: NSDefaultMallocZone()];
return s;
}
case ST_ARRAY:
case ST_MARRAY:
{
id objects[size];
id a;
int i;
for (i = 0; i < size; i++)
{
objects[i] = deserializeFromInfo(info);
if (objects[i] == nil)
{
while (i > 0)
{
[objects[--i] release];
}
return nil;
}
}
if (code == ST_MARRAY || info->mutable)
{
a = [GMutableArrayClass allocWithZone: NSDefaultMallocZone()];
a = [a initWithObjects: objects
count: size];
}
else
{
a = [GArrayClass allocWithZone: NSDefaultMallocZone()];
a = [a initWithObjects: objects
count: size];
}
while (i > 0)
{
[objects[--i] release];
}
return a;
}
case ST_DICT:
case ST_MDICT:
{
id keys[size];
id objects[size];
id d;
int i;
for (i = 0; i < size; i++)
{
keys[i] = deserializeFromInfo(info);
if (keys[i] == nil)
{
while (i > 0)
{
[keys[--i] release];
[objects[i] release];
}
return nil;
}
objects[i] = deserializeFromInfo(info);
if (objects[i] == nil)
{
[keys[i] release];
while (i > 0)
{
[keys[--i] release];
[objects[i] release];
}
return nil;
}
}
if (code == ST_MDICT || info->mutable)
{
d=[GMutableDictionaryClass allocWithZone: NSDefaultMallocZone()];
d = [d initWithObjects: objects
forKeys: keys
count: size];
}
else
{
d = [GDictionaryClass allocWithZone: NSDefaultMallocZone()];
d = [d initWithObjects: objects
forKeys: keys
count: size];
}
while (i > 0)
{
[keys[--i] release];
[objects[i] release];
}
return d;
}
case ST_DATA:
{
NSData *d;
void *b = objc_malloc(size);
(*info->debImp)(info->data, debSel, b, size, info->cursor);
d = [GDataClass allocWithZone: NSDefaultMallocZone()];
d = [d initWithBytesNoCopy: b
length: size
fromZone: NSDefaultMallocZone()];
return d;
}
default:
return nil;
}
}
@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());
initDeserializerInfo(&proxy->info, [d retain], c, m);
return [proxy autorelease];
}
- (void) dealloc
{
[info.data release];
[plist release];
[super dealloc];
}
- forward: (SEL)aSel :(arglist_t)frame
{
IMP imp;
if (plist == nil && info.data != nil)
{
plist = deserializeFromInfo(&info);
[info.data release];
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);
[info.data release];
info.data = nil;
}
return plist;
}
@end
@implementation NSDeserializer
+ (void) initialize
{
if (self == [NSDeserializer class])
{
GArrayClass = [NSGArray class];
GMutableArrayClass = [NSGMutableArray class];
GDataClass = [NSDataMalloc class];
GDictionaryClass = [NSGDictionary class];
GMutableDictionaryClass = [NSGMutableDictionary class];
}
}
+ (id) deserializePropertyListFromData: (NSData*)data
atCursor: (unsigned int*)cursor
mutableContainers: (BOOL)flag
{
NSDeserializer* s = [self new];
id o = nil;
_NSDeserializerInfo info;
id o;
s->mutableContainer = flag;
[data deserializeDataAt: &o
ofObjCType: @encode(id)
atCursor: cursor
context: s];
[s release];
return o;
NSAssert(data != nil, NSInvalidArgumentException);
NSAssert(cursor != 0, NSInvalidArgumentException);
initDeserializerInfo(&info, data, cursor, flag);
o = deserializeFromInfo(&info);
[o autorelease];
return o;
}
+ (id) deserializePropertyListFromData: (NSData*)data
mutableContainers: (BOOL)flag
{
unsigned int cursor = 0;
_NSDeserializerInfo info;
unsigned int cursor = 0;
id o;
return [self deserializePropertyListFromData: data
atCursor: &cursor
mutableContainers: flag];
NSAssert(data != nil, NSInvalidArgumentException);
initDeserializerInfo(&info, data, &cursor, flag);
o = deserializeFromInfo(&info);
[o autorelease];
return o;
}
- (void) deserializeObjectAt: (id*)object
ofObjCType: (const char *)type
fromData: (NSData*)data
atCursor: (unsigned*)cursor
+ (id) deserializePropertyListLazilyFromData: (NSData*)data
atCursor: (unsigned*)cursor
length: (unsigned)length
mutableContainers: (BOOL)flag
{
SerializerType code;
unsigned int size;
NSAssert(data != nil, NSInvalidArgumentException);
NSAssert(cursor != 0, NSInvalidArgumentException);
if (length > [data length] - *cursor)
{
_NSDeserializerInfo info;
id o;
assert(type == @encode(id));
code = (SerializerType)[data deserializeIntAtCursor: cursor];
size = (unsigned int)[data deserializeIntAtCursor: cursor];
switch (code) {
case ST_MDATA:
{
NSMutableData* d = [NSMutableData dataWithCapacity: size];
void* b = [d mutableBytes];
[data deserializeBytes: b length: size atCursor: cursor];
*object = d;
break;
}
case ST_DATA:
{
NSMutableData* d;
void* b = objc_malloc(size);
[data deserializeBytes: b length: size atCursor: cursor];
d = [NSData dataWithBytesNoCopy: b length: size];
*object = d;
break;
}
case ST_MCSTRING:
case ST_MSTRING:
{
NSMutableString* s;
char* b = objc_malloc(size+1);
b[size] = '\0';
[data deserializeBytes: b length: size atCursor: cursor];
s = [[[NSMutableString alloc] initWithCStringNoCopy: b
length: size
freeWhenDone: YES]
autorelease];
*object = s;
break;
}
case ST_CSTRING:
case ST_STRING:
{
NSString* s;
char* b = objc_malloc(size+1);
b[size] = '\0';
[data deserializeBytes: b length: size atCursor: cursor];
s = [[[NSString alloc] initWithCStringNoCopy: b
length: size
freeWhenDone: YES] autorelease];
*object = s;
break;
}
case ST_MARRAY:
case ST_ARRAY:
{
id *objects = objc_malloc(size*sizeof(id));
id a;
int i;
for (i = 0; i < size; i++) {
[data deserializeDataAt: &objects[i]
ofObjCType: type
atCursor: cursor
context: self];
}
if (code == ST_MARRAY || mutableContainer) {
a = [[NSMutableArray alloc] initWithObjects: objects
count: size];
}
else {
a = [[NSArray alloc] initWithObjects: objects
count: size];
}
objc_free(objects);
[a autorelease];
*object = a;
break;
}
case ST_MDICT:
case ST_DICT:
{
id *keys = objc_malloc(size*sizeof(id));
id *objects = objc_malloc(size*sizeof(id));
id d;
int i;
for (i = 0; i < size; i++) {
[data deserializeDataAt: &keys[i]
ofObjCType: type
atCursor: cursor
context: self];
[data deserializeDataAt: &objects[i]
ofObjCType: type
atCursor: cursor
context: self];
}
if (code == ST_MDICT || mutableContainer) {
d = [NSMutableDictionary dictionaryWithObjects: objects
forKeys: keys
count: size];
}
else {
d = [NSDictionary dictionaryWithObjects: objects
forKeys: keys
count: size];
}
objc_free(keys);
objc_free(objects);
*object = d;
break;
}
default:
[NSException raise: NSGenericException
format: @"Unknown class in property list"];
initDeserializerInfo(&info, data, cursor, flag);
o = deserializeFromInfo(&info);
[o autorelease];
return o;
}
else
{
return [_NSDeserializerProxy proxyWithData: data
atCursor: cursor
mutable: flag];
}
}
- (void) serializeObjectAt: (id*)object
ofObjCType: (const char *)type
intoData: (NSMutableData*)data
{
[self shouldNotImplement: _cmd];
}
@end