diff --git a/Headers/gnustep/base/NSSerialization.h b/Headers/gnustep/base/NSSerialization.h index 230c38753..567eb92c0 100644 --- a/Headers/gnustep/base/NSSerialization.h +++ b/Headers/gnustep/base/NSSerialization.h @@ -44,6 +44,27 @@ intoData: (NSMutableData*)d; @end +#ifndef NO_GNUSTEP +/* + * GNUstep extends serialization by having the option to make the + * resulting data more compact by ensuring that repeated strings + * are only stored once. If the property-list has a lot of repeated + * strings in it, this will be both faster and more space efficient + * but it will be slower if the property-list has few repeated + * strings. The default is to generate compact versions of the data. + * + * The [+shouldBeCompact:] method sets default behavior. + * The [+serializePropertyList:intoData:compact:] method lets you + * override the default behavior. + */ +@interface NSSerializer (GNUstep) ++ (void) shouldBeCompact: (BOOL)flag; ++ (void) serializePropertyList: (id)propertyList + intoData: (NSMutableData*)d + compact: (BOOL)flag; +@end +#endif + @interface NSDeserializer: NSObject + (id) deserializePropertyListFromData: (NSData*)data atCursor: (unsigned int*)cursor diff --git a/Source/FastArray.x b/Source/FastArray.x new file mode 100644 index 000000000..4aa5dd317 --- /dev/null +++ b/Source/FastArray.x @@ -0,0 +1,147 @@ +/* A fast inline array table implementation without objc method overhead. + * Copyright (C) 1998 Free Software Foundation, Inc. + * + * Author: Richard Frith-Macdonald + * Created: Nov 1998 + * + * This file is part of the GNUstep Base Library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include + +/* To easily un-inline functions for debugging */ +#ifndef INLINE +#define INLINE inline +#endif + +/* + * This file should be INCLUDED in files wanting to use the FastArray + * functions - these are all declared inline for maximum performance. + * + * The file including this one may predefine some macros to alter + * the behaviour (default macros assume the items are NSObjects + * that are to be retained in the array) ... + * + * FAST_ARRAY_RETAIN() + * Macro to retain an array item + * + * FAST_ARRAY_RELEASE() + * Macro to release the item. + * + */ + +#ifndef FAST_ARRAY_RETAIN +#define FAST_ARRAY_RETAIN(X) [(X).o retain] +#endif + +#ifndef FAST_ARRAY_RELEASE +#define FAST_ARRAY_RELEASE(X) [(X).o release] +#endif + +typedef union { + id o; + Class c; + int i; + unsigned I; + long l; + unsigned long L; + void *p; + const void *P; + char *s; + const char *S; +} FastArrayItem; + +struct _FastArray { + FastArrayItem *ptr; + unsigned count; + unsigned cap; + unsigned old; + NSZone *zone; +}; +typedef struct _FastArray FastArray_t; +typedef struct _FastArray *FastArray; + +static INLINE void +FastArrayAddItem(FastArray array, FastArrayItem item) +{ + if (array->count == array->cap) + { + unsigned next; + FastArrayItem *tmp; + + next = array->cap + array->old; + tmp = NSZoneRealloc(array->zone, array->ptr, next*sizeof(FastArrayItem)); + + if (tmp == 0) + { + [NSException raise: NSMallocException + format: @"failed to grow FastArray"]; + } + array->ptr = tmp; + array->old = array->cap; + array->cap = next; + } + array->ptr[array->count++] = FAST_ARRAY_RETAIN(item); +} + +static INLINE void +FastArrayRemoveItemAtIndex(FastArray array, unsigned index) +{ + NSCAssert(index < array->count, NSInvalidArgumentException); + FAST_ARRAY_RELEASE(array->ptr[index]); + while (++index < array->count) + array->ptr[index-1] = array->ptr[index]; + array->count--; +} + +static INLINE FastArrayItem +FastArrayItemAtIndex(FastArray array, unsigned index) +{ + return array->ptr[index]; +} + +static INLINE unsigned +FastArrayLength(FastArray array) +{ + return array->count; +} + +static INLINE void +FastArrayEmpty(FastArray array) +{ + unsigned i = array->count; + + while (i-- > 0) + FAST_ARRAY_RELEASE(array->ptr[i]); + NSZoneFree(array->zone, (void*)array->ptr); +} + +static INLINE FastArray +FastArrayInitWithZoneAndCapacity(FastArray array, NSZone *zone, size_t capacity) +{ + array->zone = zone; + array->count = 0; + if (capacity < 1) + capacity = 2; + array->cap = capacity; + array->old = capacity/2; + array->ptr=(FastArrayItem*)NSZoneMalloc(zone,capacity*sizeof(FastArrayItem)); + return array; +} + + diff --git a/Source/NSSerializer.m b/Source/NSSerializer.m index 7307a6eee..4d49dcb5b 100644 --- a/Source/NSSerializer.m +++ b/Source/NSSerializer.m @@ -40,17 +40,39 @@ @class NSGMutableDictionary; @class NSDataMalloc; +/* + * Setup for inline operation of string map tables. + */ +#define FAST_MAP_RETAIN_KEY(X) X +#define FAST_MAP_RELEASE_KEY(X) +#define FAST_MAP_RETAIN_VAL(X) X +#define FAST_MAP_RELEASE_VAL(X) +#define FAST_MAP_HASH(X) [(X).o hash] +#define FAST_MAP_EQUAL(X,Y) [(X).o isEqualToString: (Y).o] + +#include "FastMap.x" + +/* + * Setup for inline operation of string arrays. + */ +#define FAST_ARRAY_RETAIN(X) X +#define FAST_ARRAY_RELEASE(X) + +#include "FastArray.x" + /* * 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 +#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 +static char st_xref = (char)ST_XREF; static char st_cstring = (char)ST_CSTRING; static char st_string = (char)ST_STRING; static char st_array = (char)ST_ARRAY; @@ -77,6 +99,9 @@ typedef struct { unsigned int (*lenImp)(); // Length of data. void (*serImp)(); // Serialize integer. void (*setImp)(); // Set length of data. + unsigned count; // String counter. + FastMapTable_t map; // For uniquing. + BOOL shouldUnique; // Do we do uniquing? } _NSSerializerInfo; static SEL appSel = @selector(appendBytes:length:); @@ -86,7 +111,7 @@ static SEL serSel = @selector(serializeInt:); static SEL setSel = @selector(setLength:); static void -initSerializerInfo(_NSSerializerInfo* info, NSMutableData *d) +initSerializerInfo(_NSSerializerInfo* info, NSMutableData *d, BOOL u) { Class c = fastClass(d); @@ -96,6 +121,20 @@ initSerializerInfo(_NSSerializerInfo* info, NSMutableData *d) info->lenImp = (unsigned int (*)())get_imp(c, lenSel); info->serImp = (void (*)())get_imp(c, serSel); info->setImp = (void (*)())get_imp(c, setSel); + info->shouldUnique = u; + (*info->appImp)(d, appSel, &info->shouldUnique, 1); + if (u) + { + FastMapInitWithZoneAndCapacity(&info->map, NSDefaultMallocZone(), 16); + info->count = 0; + } +} + +static void +endSerializerInfo(_NSSerializerInfo* info) +{ + if (info->shouldUnique) + FastMapEmptyMap(&info->map); } static id @@ -106,25 +145,61 @@ serializeToInfo(id object, _NSSerializerInfo* info) if (c == _fastCls._NSGCString || c == _fastCls._NSGMutableCString || c == _fastCls._NXConstantString) { - unsigned slen = [object cStringLength] + 1; - unsigned dlen; + FastMapNode node; - (*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]; + if (info->shouldUnique) + node = FastMapNodeForKey(&info->map, (FastMapItem)object); + else + node = 0; + if (node == 0) + { + unsigned slen; + unsigned dlen; + + slen = [object cStringLength] + 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]; + if (info->shouldUnique) + FastMapAddPair(&info->map, + (FastMapItem)object, (FastMapItem)info->count++); + } + else + { + (*info->appImp)(info->data, appSel, &st_xref, 1); + (*info->serImp)(info->data, serSel, node->value.I); + } } else if (fastClassIsKindOfClass(c, _fastCls._NSString)) { - unsigned slen = [object length]; - unsigned dlen; + FastMapNode node; - (*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]; + if (info->shouldUnique) + node = FastMapNodeForKey(&info->map, (FastMapItem)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)); + [object getCharacters: (*info->datImp)(info->data, datSel) + dlen]; + if (info->shouldUnique) + FastMapAddPair(&info->map, + (FastMapItem)object, (FastMapItem)info->count++); + } + else + { + (*info->appImp)(info->data, appSel, &st_xref, 1); + (*info->serImp)(info->data, serSel, node->value.I); + } } else if (fastClassIsKindOfClass(c, ArrayClass)) { @@ -191,6 +266,8 @@ serializeToInfo(id object, _NSSerializerInfo* info) @implementation NSSerializer +static BOOL shouldBeCompact = YES; + + (void) initialize { if (self == [NSSerializer class]) @@ -206,10 +283,13 @@ serializeToInfo(id object, _NSSerializerInfo* info) + (NSData*) serializePropertyList: (id)propertyList { _NSSerializerInfo info; + NSMutableData *d; NSAssert(propertyList != nil, NSInvalidArgumentException); - initSerializerInfo(&info, [NSMutableData dataWithCapacity: 1024]); + d = [NSMutableData dataWithCapacity: 1024]; + initSerializerInfo(&info, d, shouldBeCompact); serializeToInfo(propertyList, &info); + endSerializerInfo(&info); return info.data; } @@ -220,8 +300,29 @@ serializeToInfo(id object, _NSSerializerInfo* info) NSAssert(propertyList != nil, NSInvalidArgumentException); NSAssert(d != nil, NSInvalidArgumentException); - initSerializerInfo(&info, d); + initSerializerInfo(&info, d, shouldBeCompact); serializeToInfo(propertyList, &info); + 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); +} ++ (void) shouldBeCompact: (BOOL)flag +{ + shouldBeCompact = flag; } @end @@ -240,8 +341,10 @@ typedef struct { NSData *data; unsigned *cursor; BOOL mutable; + BOOL didUnique; void (*debImp)(); unsigned int (*deiImp)(); + FastArray_t array; } _NSDeserializerInfo; static SEL debSel = @selector(deserializeBytes:length:atCursor:); @@ -255,6 +358,16 @@ initDeserializerInfo(_NSDeserializerInfo* info, NSData *d, unsigned *c, BOOL m) info->mutable = m; info->debImp = (void (*)())[d methodForSelector: debSel]; info->deiImp = (unsigned int (*)())[d methodForSelector: deiSel]; + (*info->debImp)(d, debSel, &info->didUnique, 1, c); + if (info->didUnique) + FastArrayInitWithZoneAndCapacity(&info->array, NSDefaultMallocZone(), 16); +} + +static void +endDeserializerInfo(_NSDeserializerInfo* info) +{ + if (info->didUnique) + FastArrayEmpty(&info->array); } static id @@ -268,6 +381,11 @@ deserializeFromInfo(_NSDeserializerInfo* info) switch (code) { + case ST_XREF: + { + return [FastArrayItemAtIndex(&info->array, size).o retain]; + } + case ST_CSTRING: { NSGCString *s; @@ -278,6 +396,8 @@ deserializeFromInfo(_NSDeserializerInfo* info) s = [s initWithCStringNoCopy: b length: size-1 fromZone: NSDefaultMallocZone()]; + if (info->didUnique) + FastArrayAddItem(&info->array, (FastArrayItem)s); return s; } @@ -291,6 +411,8 @@ deserializeFromInfo(_NSDeserializerInfo* info) s = [s initWithCharactersNoCopy: b length: size fromZone: NSDefaultMallocZone()]; + if (info->didUnique) + FastArrayAddItem(&info->array, (FastArrayItem)s); return s; } @@ -431,6 +553,7 @@ deserializeFromInfo(_NSDeserializerInfo* info) - (void) dealloc { [info.data release]; + endDeserializerInfo(&info); [plist release]; [super dealloc]; } @@ -495,6 +618,7 @@ deserializeFromInfo(_NSDeserializerInfo* info) NSAssert(cursor != 0, NSInvalidArgumentException); initDeserializerInfo(&info, data, cursor, flag); o = deserializeFromInfo(&info); + endDeserializerInfo(&info); [o autorelease]; return o; } @@ -509,6 +633,7 @@ deserializeFromInfo(_NSDeserializerInfo* info) NSAssert(data != nil, NSInvalidArgumentException); initDeserializerInfo(&info, data, &cursor, flag); o = deserializeFromInfo(&info); + endDeserializerInfo(&info); [o autorelease]; return o; } @@ -527,6 +652,7 @@ deserializeFromInfo(_NSDeserializerInfo* info) initDeserializerInfo(&info, data, cursor, flag); o = deserializeFromInfo(&info); + endDeserializerInfo(&info); [o autorelease]; return o; }