Index: GSPrivate.h =================================================================== RCS file: /cvsroot/gnustep/gnustep/core/base/Source/GSPrivate.h,v retrieving revision 1.7 diff -u -r1.7 GSPrivate.h --- GSPrivate.h 16 Oct 2003 20:41:50 -0000 1.7 +++ GSPrivate.h 4 Nov 2003 20:41:22 -0000 @@ -76,7 +76,8 @@ unsigned int _count; struct { unsigned int wide: 1; // 16-bit characters in string? - unsigned int free: 1; // Should free memory? + unsigned int free: 1; // Set if the instance owns the + // _contents buffer unsigned int unused: 2; unsigned int hash: 28; } _flags; Index: GSString.m =================================================================== RCS file: /cvsroot/gnustep/gnustep/core/base/Source/GSString.m,v retrieving revision 1.85 diff -u -r1.85 GSString.m --- GSString.m 3 Nov 2003 08:35:44 -0000 1.85 +++ GSString.m 4 Nov 2003 20:41:23 -0000 @@ -61,6 +61,7 @@ struct objc_class _NSConstantStringClassReference; #endif + /* * GSPlaceholderString - placeholder class for objects awaiting intialisation. */ @@ -69,71 +70,116 @@ } @end + /* - * GSCString - concrete class for strings using 8-bit character sets. - */ +GSString is the root class of our hierarchy of string classes. All our +classes except GSPlaceholderString inherit from it. GSString provides +the common part of the ivar layout. It also has safe implementations +of all the memory management methods subclasses should override. + +Concrete subclasses of GSString are identified by two properties: +how the string is encoded (its structure; 8-bit data or 16-bit unicode +data), and how the memory for the contents is handled (its memory +management). + +Two direct subclasses of GSString provide the code for the two structures. +The memory management part of the concrete subclasses is abstracted to +a single flag for the structure classes: free. This is set only if the +_contents buffer is guaranteed to remain valid at least until the instance +has been deallocated. + +(It really should be named 'ownsContents' or something similar, but it's +'free' in GSMutableString, and the structures need to be interchangeable.) + +Many optimizations, such as retaining instead of copying, and using pointers +to another strings _contents buffer, are valid only if this flag is set. + +GSCString, an abstract class that stores the string as 8-bit data in the +internal encoding. GSCString and its subclasses are used only if +intEnc==defEnc. +*/ @interface GSCString : GSString { } @end /* - * GSCInlineString - concrete subclass of GSCString, that expects the - * characterData to appear in memory immediately after the object itsself. - */ -@interface GSCInlineString : GSCString +And GSUnicodeString, an abstract class that stores the string as 16-bit +unicode characters. +*/ +@interface GSUnicodeString : GSString { } @end + /* - * GSCSubString - concrete subclass of GSCString, that relies on the - * data stored in a GSCString object. - */ -@interface GSCSubString : GSCString +For each memory management scheme, there is a pair of concrete subclasses +of the two abstract structure classes. Each subclass has a single -init... +method which can be used to initialize that specific subclass. + +GS*BufferString, concrete subclasses that store the data in an external +(wrt. the instance itself) buffer. The buffer may or may not be owned +by the instance; the 'free' flag indicates which. If it is set, +we need to free the buffer when we are deallocated. +*/ +@interface GSCBufferString : GSCString { -@public - GSCString *_parent; } +- (id) initWithCStringNoCopy: (char*)chars + length: (unsigned int)length + freeWhenDone: (BOOL)flag; @end -/* - * GSCEmptyString - concrete class for empty string - */ -@interface GSCEmptyString : GSCString +@interface GSUnicodeBufferString : GSUnicodeString { } +- (id) initWithCharactersNoCopy: (unichar *)chars + length: (unsigned int)length + freeWhenDone: (BOOL)flag; @end + /* - * GSUnicodeString - concrete class for strings using 16-bit character sets. - */ -@interface GSUnicodeString : GSString +GS*InlineString, concrete subclasses that store the data immediately after +the instance iself. +*/ +@interface GSCInlineString : GSCString { } +- (id) initWithCString: (const char*)chars length: (unsigned)length; @end -/* - * GSUnicodeInlineString - concrete subclass of GSUnicodeString, that - * expects the characterData to appear in memory immediately after the - * object itsself. - */ @interface GSUnicodeInlineString : GSUnicodeString { } +- (id) initWithCharacters: (const unichar*)chars length: (unsigned)length; @end + /* - * GSUnicodeSubString - concrete subclass of GSUnicodeString, that - * relies on data stored in a GSUnicodeString object. - */ +GS*SubString, concrete subclasses that use the data in another string +instance. +*/ +@interface GSCSubString : GSCString +{ +@public + GSCString *_parent; +} +- (id) initWithCString: (char *)chars length: (unsigned)length + fromParent: (GSCString *)parent; +@end + @interface GSUnicodeSubString : GSUnicodeString { @public GSUnicodeString *_parent; } +- (id) initWithCharacters: (unichar *)chars length: (unsigned)length + fromParent: (GSUnicodeString *)parent; @end + /* * GSMutableString - concrete mutable string, capable of changing its storage * from holding 8-bit to 16-bit character set. @@ -207,9 +253,11 @@ static Class NSStringClass = 0; static Class GSStringClass = 0; static Class GSCStringClass = 0; +static Class GSCBufferStringClass = 0; static Class GSCInlineStringClass = 0; static Class GSCSubStringClass = 0; static Class GSUnicodeStringClass = 0; +static Class GSUnicodeBufferStringClass = 0; static Class GSUnicodeSubStringClass = 0; static Class GSUnicodeInlineStringClass = 0; static Class GSMutableStringClass = 0; @@ -250,6 +298,8 @@ GSStringClass = [GSString class]; GSCStringClass = [GSCString class]; GSUnicodeStringClass = [GSUnicodeString class]; + GSCBufferStringClass = [GSCBufferString class]; + GSUnicodeBufferStringClass = [GSUnicodeBufferString class]; GSCInlineStringClass = [GSCInlineString class]; GSUnicodeInlineStringClass = [GSUnicodeInlineString class]; GSCSubStringClass = [GSCSubString class]; @@ -319,11 +369,11 @@ - (void) dealloc { - return; // placeholders never get deallocated. + return; // Placeholders never get deallocated. } /* - * Replace self with an inline unicode string + * Replace self with an inline unicode string. */ - (id) initWithCharacters: (const unichar*)chars length: (unsigned)length @@ -335,12 +385,13 @@ me->_contents.u = (unichar*)&((GSUnicodeInlineString*)me)[1]; me->_count = length; me->_flags.wide = 1; + me->_flags.free = 1; memcpy(me->_contents.u, chars, length*sizeof(unichar)); return (id)me; } /* - * Replace self with a simple unicode string + * Replace self with a simple unicode string. */ - (id) initWithCharactersNoCopy: (unichar*)chars length: (unsigned)length @@ -348,7 +399,7 @@ { ivars me; - me = (ivars)NSAllocateObject(GSUnicodeStringClass, 0, GSObjCZone(self)); + me = (ivars)NSAllocateObject(GSUnicodeBufferStringClass, 0, GSObjCZone(self)); me->_contents.u = chars; me->_count = length; me->_flags.wide = 1; @@ -360,7 +411,7 @@ } /* - * Replace self with an inline 'C' string + * Replace self with an inline 'C' string. */ - (id) initWithCString: (const char*)chars length: (unsigned)length @@ -374,6 +425,7 @@ me->_contents.c = (unsigned char*)&((GSCInlineString*)me)[1]; me->_count = length; me->_flags.wide = 0; + me->_flags.free = 1; memcpy(me->_contents.c, chars, length); return (id)me; } @@ -401,7 +453,7 @@ { ivars me; - me = (ivars)NSAllocateObject(GSCStringClass, 0, GSObjCZone(self)); + me = (ivars)NSAllocateObject(GSCBufferStringClass, 0, GSObjCZone(self)); me->_contents.c = (unsigned char*)chars; me->_count = length; me->_flags.wide = 0; @@ -464,6 +516,7 @@ me->_contents.c = (unsigned char*)&((GSCInlineString*)me)[1]; me->_count = length; me->_flags.wide = 0; + me->_flags.free = 1; memcpy(me->_contents.c, ((ivars)string)->_contents.c, length); } else if (GSObjCIsKindOf(c, GSUnicodeStringClass) == YES @@ -478,6 +531,7 @@ me->_contents.u = (unichar*)&((GSUnicodeInlineString*)me)[1]; me->_count = length; me->_flags.wide = 1; + me->_flags.free = 1; memcpy(me->_contents.u, ((ivars)string)->_contents.u, length*sizeof(unichar)); } @@ -492,6 +546,7 @@ me->_contents.u = (unichar*)&((GSUnicodeInlineString*)me)[1]; me->_count = length; me->_flags.wide = 1; + me->_flags.free = 1; [string getCharacters: me->_contents.u]; } return (id)me; @@ -1692,13 +1747,9 @@ if (self->_flags.free == 1) { sub = NSAllocateObject(GSCSubStringClass, 0, NSDefaultMallocZone()); - sub = [sub initWithCStringNoCopy: self->_contents.c + aRange.location - length: aRange.length - freeWhenDone: NO]; - if (sub != nil) - { - ((GSCSubString*)sub)->_parent = RETAIN((id)self); - } + sub = [sub initWithCString: self->_contents.c + aRange.location + length: aRange.length + fromParent: (GSCString *)self]; } else { @@ -1719,13 +1770,9 @@ if (self->_flags.free == 1) { sub = NSAllocateObject(GSUnicodeSubStringClass, 0, NSDefaultMallocZone()); - sub = [sub initWithCharactersNoCopy: self->_contents.u + aRange.location - length: aRange.length - freeWhenDone: NO]; - if (sub != nil) - { - ((GSUnicodeSubString*)sub)->_parent = RETAIN((id)self); - } + sub = [sub initWithCharacters: self->_contents.u + aRange.location + length: aRange.length + fromParent: (GSUnicodeString *)self]; } else { @@ -1862,11 +1909,11 @@ /* * The GSString class is actually only provided to provide a common ivar * layout for all subclasses, so that they can all share the same code. - * We don't expect this class to ever be instantiated, but we do provide - * a common deallocation method, and standard initialisation methods that - * will try to convert an instance to a type we can really use if necessary. + * This class should never be instantiated, but we do provide standard + * initialisation methods that will try to convert an instance to a type we + * can really use if necessary. */ -@implementation GSString +@implementation GSString + (void) initialize { @@ -1875,12 +1922,15 @@ - (void) dealloc { + [self subclassResponsibility: _cmd]; +/* XXX if (_flags.free == 1 && _contents.c != 0) { NSZoneFree(NSZoneFromPointer(_contents.c), _contents.c); _contents.c = 0; } NSDeallocateObject(self); +*/ } /* @@ -1890,9 +1940,12 @@ length: (unsigned int)length freeWhenDone: (BOOL)flag { + [self subclassResponsibility: _cmd]; + return nil; +#if 0 /* XXX */ if (isa == GSStringClass) { - isa = GSUnicodeStringClass; + isa = GSUnicodeBufferStringClass; } else if (_contents.u != 0) { @@ -1904,9 +1957,10 @@ _flags.wide = 1; if (flag == YES) { - _flags.free = 1; + _flags.owns_contents = 1; } return self; +#endif } /* @@ -1917,6 +1971,9 @@ length: (unsigned int)length freeWhenDone: (BOOL)flag { + [self subclassResponsibility: _cmd]; + return nil; +#if 0 /* XXX */ if (isa == GSStringClass) { isa = GSCStringClass; @@ -1934,7 +1991,15 @@ _flags.free = 1; } return self; +#endif } + +- (id) copyWithZone: (NSZone*)z +{ + [self subclassResponsibility: _cmd]; + return nil; +} + @end @@ -1972,23 +2037,6 @@ return compare_c((ivars)self, aString, mask, aRange); } -- (id) copyWithZone: (NSZone*)z -{ - if (_flags.free == NO - || NSShouldRetainWithZone(self, z) == NO) - { - NSString *obj; - - obj = (NSString*)NSAllocateObject(GSCInlineStringClass, _count, z); - obj = [obj initWithCString: _contents.c length: _count]; - return obj; - } - else - { - return RETAIN(self); - } -} - - (const char *) cString { return cString_c((ivars)self); @@ -2160,6 +2208,59 @@ return _count; } +/* +Default copy implementation. Retain if we own the buffer and the zones +agree, create a new GSCInlineString otherwise. +*/ +- (id) copyWithZone: (NSZone*)z +{ + if (!_flags.free || NSShouldRetainWithZone(self, z) == NO) + { + NSString *obj; + + obj = (NSString*)NSAllocateObject(GSCInlineStringClass, _count, z); + obj = [obj initWithCString: _contents.c length: _count]; + return obj; + } + else + { + return RETAIN(self); + } +} + +@end + + + +@implementation GSCBufferString +- (id) initWithCStringNoCopy: (char*)chars + length: (unsigned int)length + freeWhenDone: (BOOL)flag +{ + if (_contents.c != 0) + { + [NSException raise: NSInternalInconsistencyException + format: @"re-initialisation of string"]; + } + _count = length; + _contents.c = chars; + _flags.wide = 0; + if (flag == YES) + { + _flags.free = 1; + } + return self; +} + +- (void) dealloc +{ + if (_flags.free && _contents.c != 0) + { + NSZoneFree(NSZoneFromPointer(_contents.c), _contents.c); + _contents.c = 0; + } + NSDeallocateObject(self); +} @end @@ -2169,15 +2270,6 @@ * in memory immediately after the object. */ @implementation GSCInlineString -- (id) initWithCStringNoCopy: (char*)chars - length: (unsigned)length - freeWhenDone: (BOOL)flag -{ - RELEASE(self); - [NSException raise: NSInternalInconsistencyException - format: @"Illegal method used to initialise inline string"]; - return nil; -} - (id) initWithCString: (const char*)chars length: (unsigned)length { if (_contents.c != 0) @@ -2190,23 +2282,10 @@ if (_count > 0) memcpy(_contents.c, chars, length); _flags.wide = 0; + _flags.free = 1; return self; } -- (id) copyWithZone: (NSZone*)z -{ - if (NSShouldRetainWithZone(self, z) == NO) - { - NSString *obj; - obj = (NSString*)NSAllocateObject(GSCInlineStringClass, _count, z); - obj = [obj initWithCString: _contents.c length: _count]; - return obj; - } - else - { - return RETAIN(self); - } -} - (void) dealloc { NSDeallocateObject(self); @@ -2220,6 +2299,22 @@ * a section of a parent constant string class. */ @implementation GSCSubString +- (id) initWithCString: (char *)chars length: (unsigned)length + fromParent: (GSCString *)parent +{ + if (_contents.c != 0) + { + [NSException raise: NSInternalInconsistencyException + format: @"re-initialisation of string"]; + } + _count = length; + _contents.c = (unsigned char *)chars; + _flags.wide = 0; + _flags.free = 1; + ASSIGN(_parent, parent); + return self; +} + /* * Assume that a copy should be a new string, never just a retained substring. */ @@ -2231,9 +2326,10 @@ obj = [obj initWithCString: _contents.c length: _count]; return obj; } + - (void) dealloc { - RELEASE(_parent); + DESTROY(_parent); NSDeallocateObject(self); } @end @@ -2268,24 +2364,6 @@ return compare_u((ivars)self, aString, mask, aRange); } -- (id) copyWithZone: (NSZone*)z -{ - if (_flags.free == NO - || NSShouldRetainWithZone(self, z) == NO) - { - NSString *obj; - - obj = (NSString*)NSAllocateObject(GSUnicodeInlineStringClass, - _count*sizeof(unichar), z); - obj = [obj initWithCharacters: _contents.u length: _count]; - return obj; - } - else - { - return RETAIN(self); - } -} - - (const char *) cString { return cString_u((ivars)self); @@ -2466,20 +2544,65 @@ return blen; } +/* +Default -copy implementation. Retain if we own the buffer and the zones +agree, create a new GSUnicodeInlineString otherwise. +*/ +- (id) copyWithZone: (NSZone*)z +{ + if (!_flags.free || NSShouldRetainWithZone(self, z) == NO) + { + NSString *obj; + + obj = (NSString*)NSAllocateObject(GSUnicodeInlineStringClass, + _count*sizeof(unichar), z); + obj = [obj initWithCharacters: _contents.u length: _count]; + return obj; + } + else + { + return RETAIN(self); + } +} + @end -@implementation GSUnicodeInlineString -- (id) initWithCharactersNoCopy: (unichar*)chars - length: (unsigned)length +@implementation GSUnicodeBufferString +- (id) initWithCharactersNoCopy: (unichar *)chars + length: (unsigned int)length freeWhenDone: (BOOL)flag { - RELEASE(self); - [NSException raise: NSInternalInconsistencyException - format: @"Illegal method used to initialise inline string"]; - return nil; + if (_contents.u != 0) + { + [NSException raise: NSInternalInconsistencyException + format: @"re-initialisation of string"]; + } + _count = length; + _contents.u = chars; + _flags.wide = 1; + if (flag == YES) + { + _flags.free = 1; + } + return self; } + +- (void) dealloc +{ + if (_flags.free && _contents.u != 0) + { + NSZoneFree(NSZoneFromPointer(_contents.u), _contents.u); + _contents.u = 0; + } + NSDeallocateObject(self); +} +@end + + + +@implementation GSUnicodeInlineString - (id) initWithCharacters: (const unichar*)chars length: (unsigned)length { if (_contents.u != 0) @@ -2492,24 +2615,10 @@ if (_count > 0) memcpy(_contents.u, chars, length*sizeof(unichar)); _flags.wide = 1; + _flags.free = 1; return self; } -- (id) copyWithZone: (NSZone*)z -{ - if (NSShouldRetainWithZone(self, z) == NO) - { - NSString *obj; - obj = (NSString*)NSAllocateObject(GSUnicodeInlineStringClass, - _count*sizeof(unichar), z); - obj = [obj initWithCharacters: _contents.u length: _count]; - return obj; - } - else - { - return RETAIN(self); - } -} - (void) dealloc { NSDeallocateObject(self); @@ -2523,6 +2632,22 @@ * into a section of a parent constant string class. */ @implementation GSUnicodeSubString +- (id) initWithCharacters: (unichar *)chars length: (unsigned)length + fromParent: (GSUnicodeString *)parent +{ + if (_contents.u != 0) + { + [NSException raise: NSInternalInconsistencyException + format: @"re-initialisation of string"]; + } + _count = length; + _contents.u = chars; + _flags.wide = 1; + _flags.free = 1; + ASSIGN(_parent, parent); + return self; +} + /* * Assume that a copy should be a new string, never just a retained substring. */ @@ -2535,9 +2660,10 @@ obj = [obj initWithCharacters: _contents.u length: _count]; return obj; } + - (void) dealloc { - RELEASE(_parent); + DESTROY(_parent); NSDeallocateObject(self); } @end @@ -2982,11 +3108,11 @@ #endif if (_flags.wide == 1) { - isa = [GSUnicodeString class]; + isa = [GSUnicodeBufferString class]; } else { - isa = [GSCString class]; + isa = [GSCBufferString class]; } #ifndef NDEBUG GSDebugAllocationAdd(isa, self); @@ -3895,7 +4021,7 @@ NSLog(@"Warning - decoding archive containing obsolete %@ object - please delete/replace this archive", NSStringFromClass([self class])); RELEASE(self); - self = (id)NSAllocateObject(GSCStringClass, 0, NSDefaultMallocZone()); + self = (id)NSAllocateObject(GSCBufferStringClass, 0, NSDefaultMallocZone()); [aCoder decodeValueOfObjCType: @encode(unsigned) at: &count]; if (count > 0) { @@ -3957,7 +4083,7 @@ NSLog(@"Warning - decoding archive containing obsolete %@ object - please delete/replace this archive", NSStringFromClass([self class])); RELEASE(self); - self = (id)NSAllocateObject(GSUnicodeStringClass, 0, NSDefaultMallocZone()); + self = (id)NSAllocateObject(GSUnicodeBufferStringClass, 0, NSDefaultMallocZone()); [aCoder decodeValueOfObjCType: @encode(unsigned) at: &count]; if (count > 0) { Index: NSSerializer.m =================================================================== RCS file: /cvsroot/gnustep/gnustep/core/base/Source/NSSerializer.m,v retrieving revision 1.54 diff -u -r1.54 NSSerializer.m --- NSSerializer.m 31 Jul 2003 23:49:31 -0000 1.54 +++ NSSerializer.m 4 Nov 2003 20:41:24 -0000 @@ -45,9 +45,11 @@ @class GSMutableDictionary; @class NSDataMalloc; @class GSInlineArray; -@class GSMutableArray; +@class GSMutableArray; @class GSCString; +@class GSCBufferString; @class GSUnicodeString; +@class GSUnicodeBufferString; @class GSMutableString; /* @@ -100,7 +102,8 @@ /* - * Variables to cache class information. + * Variables to cache class information. These are used to check how + * an instance should be serialized. */ static Class ArrayClass = 0; static Class MutableArrayClass = 0; @@ -421,10 +424,12 @@ +static BOOL uniquing = NO; /* Make incoming strings unique */ + /* - * Variables to cache class information. + * Variables to cache class information. These are used to create instances + * when deserializing. */ -static BOOL uniquing = NO; /* Make incoming strings unique */ static Class MACls = 0; /* Mutable Array */ static Class DCls = 0; /* Data */ static Class MDCls = 0; /* Mutable Dictionary */ @@ -818,8 +823,8 @@ MACls = [GSMutableArray class]; DCls = [NSDataMalloc class]; MDCls = [GSMutableDictionary class]; - USCls = [GSUnicodeString class]; - CSCls = [GSCString class]; + USCls = [GSUnicodeBufferString class]; + CSCls = [GSCBufferString class]; csInitImp = [CSCls instanceMethodForSelector: csInitSel]; usInitImp = [USCls instanceMethodForSelector: usInitSel]; dInitImp = [DCls instanceMethodForSelector: dInitSel];