diff --git a/ChangeLog b/ChangeLog index 942e5f0f0..1ef8a4080 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2002-11-10 Richard Frith-Macdonald + + * Headers/gnustep/base/NSObject.h: Removed GNUstep plist extensions. + * Source/NSObject.m: ditto. + * Source/GSCompatibility.m: Implement new plist generator. + * Source/NSArray.m: Use new plist code + * Source/NSData.m: ditto + * Source/NSDictionary.m: ditto + * Source/NSString.m: ditto + Remove GNUstep property list extensions from the api ... make more + like MacOS and OpenStep spec by having a central mechanism for + generating property lists rather than spreading the code across the + plist classes. + 2002-11-09 Richard Frith-Macdonald * Source/NSObject.m: use NSString implementation of diff --git a/Headers/gnustep/base/NSObject.h b/Headers/gnustep/base/NSObject.h index f67d2845b..8dfa45afb 100644 --- a/Headers/gnustep/base/NSObject.h +++ b/Headers/gnustep/base/NSObject.h @@ -252,40 +252,8 @@ enum {NSNotFound = 0x7fffffff}; creating the potential for deadlock. */ GS_EXPORT NSRecursiveLock *gnustep_global_lock; -/* - * The GNUDescriptionDestination protocol declares methods used to - * append a property-list description string to some output destination - * so that property-lists can be converted to strings in a stream avoiding - * the use of ridiculous amounts of memory for deeply nested data structures. - */ -@protocol GNUDescriptionDestination -- (void) appendFormat: (NSString*)str, ...; -- (void) appendString: (NSString*)str; -@end - @interface NSObject (GNU) - (NSComparisonResult) compare: (id)anObject; -/* - * Default description methods - - * [descriptionWithLocale:] calls [description] - * [descriptionWithLocale:indent:] calls [descriptionWithLocale:] - * [descriptionWithLocale:indent:to:] calls [descriptionWithLocale:indent:] - * So - to have working descriptions, it is only necessary to implement the - * [description] method, and to have efficient property-list generation, it - * is necessary to override [descriptionWithLocale:indent:to:] - */ -- (NSString*) descriptionWithLocale: (NSDictionary*)aLocale; -+ (NSString*) descriptionWithLocale: (NSDictionary*)aLocale; -- (NSString*) descriptionWithLocale: (NSDictionary*)aLocale - indent: (unsigned)level; -+ (NSString*) descriptionWithLocale: (NSDictionary*)aLocale - indent: (unsigned)level; -- (void) descriptionWithLocale: (NSDictionary*)aLocale - indent: (unsigned)level - to: (id)output; -+ (void) descriptionWithLocale: (NSDictionary*)aLocale - indent: (unsigned)level - to: (id)output; - (id) makeImmutableCopyOnFail: (BOOL)force; - (Class) transmuteClassTo: (Class)aClassObject; - (id) subclassResponsibility: (SEL)aSel; diff --git a/Source/GSCompatibility.m b/Source/GSCompatibility.m index 113c9fdb5..6ad4ca38f 100644 --- a/Source/GSCompatibility.m +++ b/Source/GSCompatibility.m @@ -26,6 +26,8 @@ #include "GSPrivate.h" +@class GSMutableString; + #ifndef HAVE_RINT #include static double rint(double a) @@ -112,28 +114,144 @@ encodeBase64(NSData *source) initWithCStringNoCopy: dBuf length: destlen-1 freeWhenDone: YES]; } -static NSCharacterSet *quotables = nil; +static NSCharacterSet *xmlQuotables = nil; +static NSCharacterSet *plQuotables = nil; +static NSCharacterSet *oldPlQuotables = nil; +static unsigned const char *plQuotablesBitmapRep = NULL; +#define GS_IS_QUOTABLE(X) IS_BIT_SET(plQuotablesBitmapRep[(X)/8], (X) % 8) -static void setupQuotables(void) +static inline void Append(NSString *src, GSMutableString *dst) { - if (quotables == nil) - { - NSMutableCharacterSet *s; + [(NSMutableString*)dst appendString: src]; +} - s = [[NSCharacterSet characterSetWithCharactersInString: - @"&<>'\\\""] mutableCopy]; - [s addCharactersInRange: NSMakeRange(0x0001, 0x001f)]; - [s removeCharactersInRange: NSMakeRange(0x0009, 0x0002)]; - [s removeCharactersInRange: NSMakeRange(0x000D, 0x0001)]; - [s addCharactersInRange: NSMakeRange(0xD800, 0x07FF)]; - [s addCharactersInRange: NSMakeRange(0xFFFE, 0x0002)]; - quotables = [s copy]; - RELEASE(s); +static void +PString(NSString *obj, GSMutableString *output) +{ + unsigned length; + + if ((length = [obj length]) == 0) + { + Append(@"\"\"", output); + return; + } + + if ([obj rangeOfCharacterFromSet: oldPlQuotables].length > 0 + || [obj characterAtIndex: 0] == '/') + { + unichar tmp[length <= 1024 ? length : 0]; + unichar *ustring; + unichar *from; + unichar *end; + int len = 0; + + if (length <= 1024) + { + ustring = tmp; + } + else + { + ustring = NSZoneMalloc(NSDefaultMallocZone(), length*sizeof(unichar)); + } + end = &ustring[length]; + [obj getCharacters: ustring]; + for (from = ustring; from < end; from++) + { + switch (*from) + { + case '\a': + case '\b': + case '\t': + case '\r': + case '\n': + case '\v': + case '\f': + case '\\': + case '\'' : + case '"' : + len += 2; + break; + + default: + if (*from < 128) + { + if (isprint(*from) || *from == ' ') + { + len++; + } + else + { + len += 4; + } + } + else + { + len += 6; + } + break; + } + } + + { + char buf[len+3]; + char *ptr = buf; + + *ptr++ = '"'; + for (from = ustring; from < end; from++) + { + switch (*from) + { + case '\a': *ptr++ = '\\'; *ptr++ = 'a'; break; + case '\b': *ptr++ = '\\'; *ptr++ = 'b'; break; + case '\t': *ptr++ = '\\'; *ptr++ = 't'; break; + case '\r': *ptr++ = '\\'; *ptr++ = 'r'; break; + case '\n': *ptr++ = '\\'; *ptr++ = 'n'; break; + case '\v': *ptr++ = '\\'; *ptr++ = 'v'; break; + case '\f': *ptr++ = '\\'; *ptr++ = 'f'; break; + case '\\': *ptr++ = '\\'; *ptr++ = '\\'; break; + case '\'': *ptr++ = '\\'; *ptr++ = '\''; break; + case '"' : *ptr++ = '\\'; *ptr++ = '"'; break; + + default: + if (*from < 128) + { + if (isprint(*from) || *from == ' ') + { + *ptr++ = *from; + } + else + { + sprintf(ptr, "\\%03o", *(unsigned char*)from); + ptr = &ptr[4]; + } + } + else + { + sprintf(ptr, "\\u%04x", *from); + ptr = &ptr[6]; + } + break; + } + } + *ptr++ = '"'; + *ptr = '\0'; + obj = [[NSString alloc] initWithCString: buf]; + Append(obj, output); + RELEASE(obj); + } + if (length > 1024) + { + NSZoneFree(NSDefaultMallocZone(), ustring); + } + } + else + { + Append(obj, output); } } -static NSString* -XMLString(NSString* obj) +static void +XString(NSString* obj, GSMutableString *output) { static char *hexdigits = "0123456789ABCDEF"; unsigned end; @@ -141,15 +259,10 @@ XMLString(NSString* obj) end = [obj length]; if (end == 0) { - return obj; + return; } - if (quotables == nil) - { - setupQuotables(); - } - - if ([obj rangeOfCharacterFromSet: quotables].length > 0) + if ([obj rangeOfCharacterFromSet: xmlQuotables].length > 0) { unichar *base; unichar *map; @@ -294,43 +407,68 @@ XMLString(NSString* obj) } } NSZoneFree(NSDefaultMallocZone(), base); - return [NSString stringWithCharacters: map length: len]; + obj = [[NSString alloc] initWithCharacters: map length: len]; + Append(obj, output); + RELEASE(obj); } else { - return obj; + Append(obj, output); } } static NSString *indentStrings[] = { @"", + @" ", @" ", + @" ", @"\t", + @"\t ", @"\t ", + @"\t ", @"\t\t", + @"\t\t ", @"\t\t ", + @"\t\t ", @"\t\t\t", + @"\t\t\t ", @"\t\t\t ", + @"\t\t\t ", @"\t\t\t\t", + @"\t\t\t\t ", @"\t\t\t\t ", + @"\t\t\t\t ", @"\t\t\t\t\t", + @"\t\t\t\t\t ", @"\t\t\t\t\t ", + @"\t\t\t\t\t ", @"\t\t\t\t\t\t" }; +/** + * obj is the object to be written out
+ * loc is the locale for formatting (or nil to indicate no formatting)
+ * lev is the level of indentation to use
+ * step is the indentation step (0 == 0, 1 = 2, 2 = 4, 3 = 8)
+ * x is a flag to indicate xml property list format
+ * dest is the output buffer. + */ static void -XMLPlObject(NSMutableString *dest, id obj, NSDictionary *loc, unsigned lev) +OAppend(id obj, NSDictionary *loc, unsigned lev, unsigned step, + BOOL x, GSMutableString *dest) { - if (lev >= sizeof(indentStrings) / sizeof(*indentStrings)) - lev = sizeof(indentStrings) / sizeof(*indentStrings) - 1; - - [dest appendString: indentStrings[lev]]; - if ([obj isKindOfClass: [NSString class]]) { - [dest appendString: @""]; - [dest appendString: XMLString(obj)]; - [dest appendString: @"\n"]; + if (x == YES) + { + Append(@"", dest); + XString(obj, dest); + Append(@"\n", dest); + } + else + { + PString(obj, dest); + } } else if ([obj isKindOfClass: [NSNumber class]]) { @@ -338,98 +476,463 @@ XMLPlObject(NSMutableString *dest, id obj, NSDictionary *loc, unsigned lev) if (val == 1.0) { - [dest appendString: @"\n"]; + if (x) + { + Append(@"\n", dest); + } + else + { + Append(@"YES", dest); + } } else if (val == 0.0) { - [dest appendString: @"\n"]; + if (x) + { + Append(@"\n", dest); + } + else + { + Append(@"NO", dest); + } } else if (rint(val) == val) { - [dest appendString: @""]; - [dest appendString: [obj stringValue]]; - [dest appendString: @"\n"]; + if (x == YES) + { + Append(@"", dest); + XString([obj stringValue], dest); + Append(@"\n", dest); + } + else + { + PString([obj stringValue], dest); + } } else { - [dest appendString: @""]; - [dest appendString: [obj stringValue]]; - [dest appendString: @"\n"]; + if (x == YES) + { + Append(@"", dest); + XString([obj stringValue], dest); + Append(@"\n", dest); + } + else + { + PString([obj stringValue], dest); + } } } else if ([obj isKindOfClass: [NSData class]]) { - [dest appendString: @""]; - [dest appendString: encodeBase64(obj)]; - [dest appendString: @"\n"]; + if (x == YES) + { + Append(@"", dest); + Append(encodeBase64(obj), dest); + Append(@"\n", dest); + } + else + { + NSString *str; + const char *src; + char *dst; + int length; + int i; + int j; + NSZone *z = NSDefaultMallocZone(); + + src = [obj bytes]; + length = [obj length]; + #define num2char(num) ((num) < 0xa ? ((num)+'0') : ((num)+0x57)) + + dst = (char*) NSZoneMalloc(z, 2*length+length/4+3); + dst[0] = '<'; + for (i = 0, j = 1; i < length; i++, j++) + { + dst[j++] = num2char((src[i]>>4) & 0x0f); + dst[j] = num2char(src[i] & 0x0f); + if ((i&0x3) == 3 && i != length-1) + { + /* if we've just finished a 32-bit int, print a space */ + dst[++j] = ' '; + } + } + dst[j++] = '>'; + dst[j] = '\0'; + str = [[NSString allocWithZone: z] initWithCStringNoCopy: dst + length: j + freeWhenDone: YES]; + Append(str, dest); + RELEASE(str); + } } else if ([obj isKindOfClass: [NSDate class]]) { - [dest appendString: @""]; - [dest appendString: - [obj descriptionWithCalendarFormat: @"%Y-%m-%d %H:%M:%S %z"]]; - [dest appendString: @"\n"]; + if (x == YES) + { + Append(@"", dest); + Append([obj descriptionWithCalendarFormat: @"%Y-%m-%d %H:%M:%S %z"], + dest); + Append(@"\n", dest); + } + else + { + PString([obj descriptionWithCalendarFormat: @"%Y-%m-%d %H:%M:%S %z"], + dest); + } } else if ([obj isKindOfClass: [NSArray class]]) { - NSEnumerator *e; + NSString *iBaseString; + NSString *iSizeString; + unsigned level = lev; - [dest appendString: @"\n"]; - e = [obj objectEnumerator]; - while ((obj = [e nextObject])) - { - XMLPlObject(dest, obj, loc, lev + 1); - } - [dest appendString: indentStrings[lev]]; - [dest appendString: @"\n"]; + if (level*step < sizeof(indentStrings)/sizeof(id)) + { + iBaseString = indentStrings[level*step]; + } + else + { + iBaseString + = indentStrings[sizeof(indentStrings)/sizeof(id)-1]; + } + level++; + if (level*step < sizeof(indentStrings)/sizeof(id)) + { + iSizeString = indentStrings[level*step]; + } + else + { + iSizeString + = indentStrings[sizeof(indentStrings)/sizeof(id)-1]; + } + + if (x == YES) + { + NSEnumerator *e; + + Append(@"", dest); + e = [obj objectEnumerator]; + while ((obj = [e nextObject])) + { + Append(iSizeString, dest); + OAppend(obj, loc, level, step, YES, dest); + } + Append(iBaseString, dest); + Append(@"\n", dest); + } + else + { + unsigned count = [obj count]; + unsigned last = count - 1; + NSString *plists[count]; + unsigned i; + + [obj getObjects: plists]; + + if (loc == nil) + { + Append(@"(", dest); + for (i = 0; i < count; i++) + { + id item = plists[i]; + + OAppend(item, nil, 0, step, NO, dest); + if (i != last) + { + Append(@", ", dest); + } + } + Append(@")", dest); + } + else + { + Append(@"(\n", dest); + for (i = 0; i < count; i++) + { + id item = plists[i]; + + Append(iSizeString, dest); + OAppend(item, loc, level, step, NO, dest); + if (i == last) + { + Append(@"\n", dest); + } + else + { + Append(@",\n", dest); + } + } + Append(iBaseString, dest); + Append(@")", dest); + } + } } else if ([obj isKindOfClass: [NSDictionary class]]) { - NSEnumerator *e; - id key; - unsigned nxt = lev + 1; + NSString *iBaseString; + NSString *iSizeString; + unsigned level = lev; - if (lev >= sizeof(indentStrings) / sizeof(*indentStrings)) - lev = sizeof(indentStrings) / sizeof(*indentStrings) - 1; + if (level*step < sizeof(indentStrings)/sizeof(id)) + { + iBaseString = indentStrings[level*step]; + } + else + { + iBaseString + = indentStrings[sizeof(indentStrings)/sizeof(id)-1]; + } + level++; + if (level*step < sizeof(indentStrings)/sizeof(id)) + { + iSizeString = indentStrings[level*step]; + } + else + { + iSizeString + = indentStrings[sizeof(indentStrings)/sizeof(id)-1]; + } - [dest appendString: @"\n"]; - e = [obj keyEnumerator]; - while ((key = [e nextObject])) - { - id val; + if (x == YES) + { + NSEnumerator *e; + id key; - val = [obj objectForKey: key]; - [dest appendString: indentStrings[nxt]]; - [dest appendString: @""]; - [dest appendString: XMLString(key)]; - [dest appendString: @"\n"]; - XMLPlObject(dest, val, loc, nxt); - } - [dest appendString: indentStrings[lev]]; - [dest appendString: @"\n"]; + Append(@"\n", dest); + e = [obj keyEnumerator]; + while ((key = [e nextObject])) + { + id val; + + val = [obj objectForKey: key]; + Append(iSizeString, dest); + Append(@"", dest); + XString(key, dest); + Append(@"\n", dest); + OAppend(val, loc, level, step, YES, dest); + } + Append(iBaseString, dest); + Append(@"\n", dest); + } + else + { + SEL objSel = @selector(objectForKey:); + IMP myObj = [obj methodForSelector: objSel]; + unsigned i; + NSArray *keyArray = [obj allKeys]; + unsigned numKeys = [keyArray count]; + NSString *plists[numKeys]; + NSString *keys[numKeys]; + + [keyArray getObjects: keys]; + + if (loc == nil) + { + for (i = 0; i < numKeys; i++) + { + plists[i] = (*myObj)(obj, objSel, keys[i]); + } + + Append(@"{", dest); + for (i = 0; i < numKeys; i++) + { + OAppend(keys[i], nil, 0, step, NO, dest); + Append(@" = ", dest); + OAppend(plists[i], nil, 0, step, NO, dest); + Append(@"; ", dest); + } + Append(@"}", dest); + } + else + { + BOOL canCompare = YES; + Class lastClass = 0; + + for (i = 0; i < numKeys; i++) + { + if (GSObjCClass(keys[i]) == lastClass) + continue; + if ([keys[i] respondsToSelector: @selector(compare:)] == NO) + { + canCompare = NO; + break; + } + lastClass = GSObjCClass(keys[i]); + } + + if (canCompare == YES) + { + #define STRIDE_FACTOR 3 + unsigned c,d, stride; + BOOL found; + NSComparisonResult (*comp)(id, SEL, id) = 0; + int count = numKeys; + #ifdef GSWARN + BOOL badComparison = NO; + #endif + + stride = 1; + while (stride <= count) + { + stride = stride * STRIDE_FACTOR + 1; + } + lastClass = 0; + while (stride > (STRIDE_FACTOR - 1)) + { + // loop to sort for each value of stride + stride = stride / STRIDE_FACTOR; + for (c = stride; c < count; c++) + { + found = NO; + if (stride > c) + { + break; + } + d = c - stride; + while (!found) + { + id a = keys[d + stride]; + id b = keys[d]; + Class x; + NSComparisonResult r; + + x = GSObjCClass(a); + if (x != lastClass) + { + lastClass = x; + comp = (NSComparisonResult (*)(id, SEL, id)) + [a methodForSelector: @selector(compare:)]; + } + r = (*comp)(a, @selector(compare:), b); + if (r < 0) + { + #ifdef GSWARN + if (r != NSOrderedAscending) + { + badComparison = YES; + } + #endif + keys[d + stride] = b; + keys[d] = a; + if (stride > d) + { + break; + } + d -= stride; + } + else + { + #ifdef GSWARN + if (r != NSOrderedDescending + && r != NSOrderedSame) + { + badComparison = YES; + } + #endif + found = YES; + } + } + } + } + #ifdef GSWARN + if (badComparison == YES) + { + NSWarnFLog(@"Detected bad return value from comparison"); + } + #endif + } + + for (i = 0; i < numKeys; i++) + { + plists[i] = (*myObj)(obj, objSel, keys[i]); + } + + Append(@"{\n", dest); + for (i = 0; i < numKeys; i++) + { + Append(iSizeString, dest); + OAppend(keys[i], loc, level, step, NO, dest); + Append(@" = ", dest); + OAppend(plists[i], loc, level, step, NO, dest); + Append(@";\n", dest); + } + Append(iBaseString, dest); + Append(@"}", dest); + } + } } else { NSLog(@"Non-property-list class encoded as string"); - [dest appendString: @""]; - [dest appendString: [obj description]]; - [dest appendString: @"\n"]; + Append(@"", dest); + Append([obj description], dest); + Append(@"\n", dest); } } -NSString* -GSXMLPlMake(id obj, NSDictionary *loc) +void +GSPropertyListMake(id obj, NSDictionary *loc, BOOL xml, unsigned step, id *str) { - NSMutableString *dest; + GSMutableString *dest; - dest = [NSMutableString stringWithCString: - "\n\n" - "\n"]; + if (plQuotablesBitmapRep == NULL) + { + NSMutableCharacterSet *s; + NSData *bitmap; - XMLPlObject(dest, obj, loc, 0); - [dest appendString: @""]; - return dest; + s = [[NSCharacterSet characterSetWithCharactersInString: + @"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + @"abcdefghijklmnopqrstuvwxyz!#$%&*+-./:?@|~_^"] + mutableCopy]; + [s invert]; + plQuotables = [s copy]; + RELEASE(s); + bitmap = RETAIN([plQuotables bitmapRepresentation]); + plQuotablesBitmapRep = [bitmap bytes]; + s = [[NSCharacterSet characterSetWithCharactersInString: + @"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + @"abcdefghijklmnopqrstuvwxyz$./_"] + mutableCopy]; + [s invert]; + oldPlQuotables = [s copy]; + RELEASE(s); + + s = [[NSCharacterSet characterSetWithCharactersInString: + @"&<>'\\\""] mutableCopy]; + [s addCharactersInRange: NSMakeRange(0x0001, 0x001f)]; + [s removeCharactersInRange: NSMakeRange(0x0009, 0x0002)]; + [s removeCharactersInRange: NSMakeRange(0x000D, 0x0001)]; + [s addCharactersInRange: NSMakeRange(0xD800, 0x07FF)]; + [s addCharactersInRange: NSMakeRange(0xFFFE, 0x0002)]; + xmlQuotables = [s copy]; + RELEASE(s); + } + + if (*str == nil) + { + *str = AUTORELEASE([GSMutableString new]); + } + else if (GSObjCClass(*str) != [GSMutableString class]) + { + [NSException raise: NSInvalidArgumentException + format: @"Illegal object (%@) at argument 0", *str]; + } + dest = *str; + + if (xml == YES) + { + Append([NSMutableString stringWithCString: + "\n\n" + "\n"], dest); + } + + OAppend(obj, loc, 0, step > 3 ? 3 : step, xml, dest); + if (xml == YES) + { + Append(@"", dest); + } } diff --git a/Source/NSArray.m b/Source/NSArray.m index 4d466ef6e..14ac54cdf 100644 --- a/Source/NSArray.m +++ b/Source/NSArray.m @@ -47,6 +47,7 @@ #include "GSPrivate.h" extern BOOL GSMacOSXCompatiblePropertyLists(void); +extern void GSPropertyListMake(id, NSDictionary*, BOOL, unsigned, id*); @class NSArrayEnumerator; @class NSArrayEnumeratorReverse; @@ -977,106 +978,13 @@ static int compare(id elem1, id elem2, void* context) - (NSString*) descriptionWithLocale: (NSDictionary*)locale indent: (unsigned int)level { - NSMutableString *result; + NSString *result = nil; + + GSPropertyListMake(self, locale, NO, level == 1 ? 3 : 2, &result); - result = [[NSMutableString alloc] initWithCapacity: 20*[self count]]; - result = AUTORELEASE(result); - [self descriptionWithLocale: locale - indent: level - to: (id)result]; return result; } -static NSString *indentStrings[] = { - @"", - @" ", - @"\t", - @"\t ", - @"\t\t", - @"\t\t ", - @"\t\t\t", - @"\t\t\t ", - @"\t\t\t\t", - @"\t\t\t\t ", - @"\t\t\t\t\t", - @"\t\t\t\t\t ", - @"\t\t\t\t\t\t" -}; - -- (void) descriptionWithLocale: (NSDictionary*)locale - indent: (unsigned int)level - to: (id)result -{ - unsigned count = [self count]; - unsigned last = count - 1; - NSString *plists[count]; - unsigned i; - IMP appImp; - - appImp = [(NSObject*)result methodForSelector: appSel]; - - [self getObjects: plists]; - - if (locale == nil) - { - (*appImp)(result, appSel, @"("); - for (i = 0; i < count; i++) - { - id item = plists[i]; - - [item descriptionWithLocale: nil indent: 0 to: result]; - if (i != last) - { - (*appImp)(result, appSel, @", "); - } - } - (*appImp)(result, appSel, @")"); - } - else - { - NSString *iBaseString; - NSString *iSizeString; - - if (level < sizeof(indentStrings)/sizeof(id)) - { - iBaseString = indentStrings[level]; - } - else - { - iBaseString = indentStrings[sizeof(indentStrings)/sizeof(id)-1]; - } - level++; - if (level < sizeof(indentStrings)/sizeof(id)) - { - iSizeString = indentStrings[level]; - } - else - { - iSizeString = indentStrings[sizeof(indentStrings)/sizeof(id)-1]; - } - - (*appImp)(result, appSel, @"(\n"); - for (i = 0; i < count; i++) - { - id item = plists[i]; - - (*appImp)(result, appSel, iSizeString); - - [item descriptionWithLocale: locale indent: level to: result]; - if (i == last) - { - (*appImp)(result, appSel, @"\n"); - } - else - { - (*appImp)(result, appSel, @",\n"); - } - } - (*appImp)(result, appSel, iBaseString); - (*appImp)(result, appSel, @")"); - } -} - /** *

Writes the contents of the array to the file specified by path. * The file contents will be in property-list format ... under GNUstep @@ -1102,27 +1010,16 @@ static NSString *indentStrings[] = { */ - (BOOL) writeToFile: (NSString *)path atomically: (BOOL)useAuxiliaryFile { - NSDictionary *loc; - NSString *desc; - - loc = GSUserDefaultsDictionaryRepresentation(); + NSDictionary *loc = GSUserDefaultsDictionaryRepresentation(); + NSString *desc = nil; if (GSMacOSXCompatiblePropertyLists() == YES) { - extern NSString *GSXMLPlMake(id obj, NSDictionary *loc); - - desc = GSXMLPlMake(self, loc); + GSPropertyListMake(self, loc, YES, 2, &desc); } else { - NSMutableString *result; - - result = [[NSMutableString alloc] initWithCapacity: 20*[self count]]; - result = AUTORELEASE(result); - [self descriptionWithLocale: loc - indent: 0 - to: (id)result]; - desc = result; + GSPropertyListMake(self, loc, NO, 2, &desc); } return [[desc dataUsingEncoding: NSUTF8StringEncoding] @@ -1137,27 +1034,16 @@ static NSString *indentStrings[] = { */ - (BOOL) writeToURL: (NSURL *)url atomically: (BOOL)useAuxiliaryFile { - NSDictionary *loc; - NSString *desc; - - loc = GSUserDefaultsDictionaryRepresentation(); + NSDictionary *loc = GSUserDefaultsDictionaryRepresentation(); + NSString *desc = nil; if (GSMacOSXCompatiblePropertyLists() == YES) { - extern NSString *GSXMLPlMake(id obj, NSDictionary *loc); - - desc = GSXMLPlMake(self, loc); + GSPropertyListMake(self, loc, YES, 2, &desc); } else { - NSMutableString *result; - - result = [[NSMutableString alloc] initWithCapacity: 20*[self count]]; - result = AUTORELEASE(result); - [self descriptionWithLocale: loc - indent: 0 - to: (id)result]; - desc = result; + GSPropertyListMake(self, loc, NO, 2, &desc); } return [[desc dataUsingEncoding: NSUTF8StringEncoding] diff --git a/Source/NSData.m b/Source/NSData.m index 5f48b6dd2..7f95a4fa5 100644 --- a/Source/NSData.m +++ b/Source/NSData.m @@ -678,47 +678,11 @@ failure: - (NSString*) description { - NSString *str; - const char *src; - char *dest; - int length; - int i; - int j; - NSZone *z; + extern void GSPropertyListMake(id, NSDictionary*, BOOL, unsigned, id*); + NSMutableString *result = nil; - src = [self bytes]; - length = [self length]; - z = [self zone]; -#define num2char(num) ((num) < 0xa ? ((num)+'0') : ((num)+0x57)) - - /* we can just build a cString and convert it to an NSString */ -#if GS_WITH_GC - dest = (char*) NSZoneMalloc(GSAtomicMallocZone(), 2*length+length/4+3); -#else - dest = (char*) NSZoneMalloc(z, 2*length+length/4+3); -#endif - if (dest == 0) - { - [NSException raise: NSMallocException - format: @"No memory for description of NSData object"]; - } - dest[0] = '<'; - for (i = 0, j = 1; i < length; i++, j++) - { - dest[j++] = num2char((src[i]>>4) & 0x0f); - dest[j] = num2char(src[i] & 0x0f); - if ((i&0x3) == 3 && i != length-1) - { - /* if we've just finished a 32-bit int, print a space */ - dest[++j] = ' '; - } - } - dest[j++] = '>'; - dest[j] = '\0'; - str = [[NSString allocWithZone: z] initWithCStringNoCopy: dest - length: j - freeWhenDone: YES]; - return AUTORELEASE(str); + GSPropertyListMake(self, nil, NO, 0, &result); + return result; } /** diff --git a/Source/NSDictionary.m b/Source/NSDictionary.m index e3f8224b4..573694796 100644 --- a/Source/NSDictionary.m +++ b/Source/NSDictionary.m @@ -47,6 +47,8 @@ @class GSMutableDictionary; extern BOOL GSMacOSXCompatiblePropertyLists(void); +extern void GSPropertyListMake(id, NSDictionary*, BOOL, unsigned, id*); + static Class NSArray_class; static Class NSDictionaryClass; @@ -740,27 +742,16 @@ compareIt(id o1, id o2, void* context) */ - (BOOL) writeToFile: (NSString *)path atomically: (BOOL)useAuxiliaryFile { - NSDictionary *loc; - NSString *desc; - - loc = GSUserDefaultsDictionaryRepresentation(); + NSDictionary *loc = GSUserDefaultsDictionaryRepresentation(); + NSString *desc = nil; if (GSMacOSXCompatiblePropertyLists() == YES) { - extern NSString *GSXMLPlMake(id obj, NSDictionary *loc); - - desc = GSXMLPlMake(self, loc); + GSPropertyListMake(self, loc, YES, 2, &desc); } else { - NSMutableString *result; - - result = AUTORELEASE([[NSMutableString alloc] initWithCapacity: - 20*[self count]]); - [self descriptionWithLocale: loc - indent: 0 - to: (id)result]; - desc = result; + GSPropertyListMake(self, loc, NO, 2, &desc); } return [[desc dataUsingEncoding: NSUTF8StringEncoding] @@ -775,27 +766,16 @@ compareIt(id o1, id o2, void* context) */ - (BOOL) writeToURL: (NSURL *)url atomically: (BOOL)useAuxiliaryFile { - NSDictionary *loc; - NSString *desc; - - loc = GSUserDefaultsDictionaryRepresentation(); + NSDictionary *loc = GSUserDefaultsDictionaryRepresentation(); + NSString *desc = nil; if (GSMacOSXCompatiblePropertyLists() == YES) { - extern NSString *GSXMLPlMake(id obj, NSDictionary *loc); - - desc = GSXMLPlMake(self, loc); + GSPropertyListMake(self, loc, YES, 2, &desc); } else { - NSMutableString *result; - - result = AUTORELEASE([[NSMutableString alloc] initWithCapacity: - 20*[self count]]); - [self descriptionWithLocale: loc - indent: 0 - to: (id)result]; - desc = result; + GSPropertyListMake(self, loc, NO, 2, &desc); } return [[desc dataUsingEncoding: NSUTF8StringEncoding] @@ -809,30 +789,23 @@ compareIt(id o1, id o2, void* context) - (NSString*) descriptionInStringsFileFormat { - NSMutableString *result; + NSMutableString *result = nil; NSEnumerator *enumerator = [self keyEnumerator]; IMP nxtObj = [enumerator methodForSelector: nxtSel]; IMP myObj = [self methodForSelector: objSel]; - IMP appImp; id key; - result = AUTORELEASE([[NSMutableString alloc] initWithCapacity: 1024]); - appImp = [(NSObject*)result methodForSelector: appSel]; while ((key = (*nxtObj)(enumerator, nxtSel)) != nil) { id val = (*myObj)(self, objSel, key); - [key descriptionWithLocale: nil - indent: 0 - to: (id)result]; + GSPropertyListMake(key, nil, NO, 0, &result); if (val != nil && [val isEqualToString: @""] == NO) { - (*appImp)(result, appSel, @" = "); - [val descriptionWithLocale: nil - indent: 0 - to: (id)result]; + [result appendString: @" = "]; + GSPropertyListMake(val, nil, NO, 0, &result); } - (*appImp)(result, appSel, @";\n"); + [result appendString: @";\n"]; } return result; @@ -846,211 +819,12 @@ compareIt(id o1, id o2, void* context) - (NSString*) descriptionWithLocale: (NSDictionary*)locale indent: (unsigned int)level { - NSMutableString *result; + NSMutableString *result = nil; - result = AUTORELEASE([[NSMutableString alloc] initWithCapacity: - 20*[self count]]); - [self descriptionWithLocale: locale - indent: level - to: (id)result]; + GSPropertyListMake(self, locale, NO, level == 1 ? 3 : 2, &result); return result; } -static NSString *indentStrings[] = { - @"", - @" ", - @"\t", - @"\t ", - @"\t\t", - @"\t\t ", - @"\t\t\t", - @"\t\t\t ", - @"\t\t\t\t", - @"\t\t\t\t ", - @"\t\t\t\t\t", - @"\t\t\t\t\t ", - @"\t\t\t\t\t\t" -}; - -- (void) descriptionWithLocale: (NSDictionary*)locale - indent: (unsigned int)level - to: (id)result -{ - IMP myObj = [self methodForSelector: objSel]; - unsigned i; - NSArray *keyArray = [self allKeys]; - unsigned numKeys = [keyArray count]; - NSString *plists[numKeys]; - NSString *keys[numKeys]; - IMP appImp; - - appImp = [(NSObject*)result methodForSelector: appSel]; - - [keyArray getObjects: keys]; - - if (locale == nil) - { - for (i = 0; i < numKeys; i++) - { - plists[i] = (*myObj)(self, objSel, keys[i]); - } - - (*appImp)(result, appSel, @"{"); - for (i = 0; i < numKeys; i++) - { - id o = plists[i]; - - [keys[i] descriptionWithLocale: nil indent: 0 to: result]; - (*appImp)(result, appSel, @" = "); - [o descriptionWithLocale: nil indent: 0 to: result]; - (*appImp)(result, appSel, @"; "); - } - (*appImp)(result, appSel, @"}"); - } - else - { - NSString *iBaseString; - NSString *iSizeString; - BOOL canCompare = YES; - Class lastClass = 0; - - if (level < sizeof(indentStrings)/sizeof(id)) - { - iBaseString = indentStrings[level]; - } - else - { - iBaseString = indentStrings[sizeof(indentStrings)/sizeof(id)-1]; - } - level++; - if (level < sizeof(indentStrings)/sizeof(id)) - { - iSizeString = indentStrings[level]; - } - else - { - iSizeString = indentStrings[sizeof(indentStrings)/sizeof(id)-1]; - } - - for (i = 0; i < numKeys; i++) - { - if (GSObjCClass(keys[i]) == lastClass) - continue; - if ([keys[i] respondsToSelector: @selector(compare:)] == NO) - { - canCompare = NO; - break; - } - lastClass = GSObjCClass(keys[i]); - } - - if (canCompare == YES) - { - /* - * Shell sort algorithm taken from SortingInAction - a NeXT example - * good value for stride factor is not well-understood - * 3 is a fairly good choice (Sedgewick) - */ -#define STRIDE_FACTOR 3 - unsigned c,d, stride; - BOOL found; - NSComparisonResult (*comp)(id, SEL, id) = 0; - int count = numKeys; -#ifdef GSWARN - BOOL badComparison = NO; -#endif - - stride = 1; - while (stride <= count) - { - stride = stride * STRIDE_FACTOR + 1; - } - lastClass = 0; - while (stride > (STRIDE_FACTOR - 1)) - { - // loop to sort for each value of stride - stride = stride / STRIDE_FACTOR; - for (c = stride; c < count; c++) - { - found = NO; - if (stride > c) - { - break; - } - d = c - stride; - while (!found) // move to left until correct place - { - id a = keys[d + stride]; - id b = keys[d]; - Class x; - NSComparisonResult r; - - x = GSObjCClass(a); - if (x != lastClass) - { - lastClass = x; - comp = (NSComparisonResult (*)(id, SEL, id)) - [a methodForSelector: @selector(compare:)]; - } - r = (*comp)(a, @selector(compare:), b); - if (r < 0) - { -#ifdef GSWARN - if (r != NSOrderedAscending) - { - badComparison = YES; - } -#endif - keys[d + stride] = b; - keys[d] = a; - if (stride > d) - { - break; - } - d -= stride; // jump by stride factor - } - else - { -#ifdef GSWARN - if (r != NSOrderedDescending && r != NSOrderedSame) - { - badComparison = YES; - } -#endif - found = YES; - } - } - } - } -#ifdef GSWARN - if (badComparison == YES) - { - NSWarnMLog(@"Detected bad return value from comparison"); - } -#endif - } - - for (i = 0; i < numKeys; i++) - { - plists[i] = (*myObj)(self, objSel, keys[i]); - } - - (*appImp)(result, appSel, @"{\n"); - for (i = 0; i < numKeys; i++) - { - id o = plists[i]; - - (*appImp)(result, appSel, iSizeString); - [keys[i] descriptionWithLocale: nil indent: 0 to: result]; - (*appImp)(result, appSel, @" = "); - [o descriptionWithLocale: locale indent: level to: result]; - (*appImp)(result, appSel, @";\n"); - } - (*appImp)(result, appSel, iBaseString); - (*appImp)(result, appSel, @"}"); - } -} - /** * Default implementation for this class is to return the value stored in * the dictionary under the specified key, or nil if there is no value. diff --git a/Source/NSObject.m b/Source/NSObject.m index 5a7dc8047..ff2426325 100644 --- a/Source/NSObject.m +++ b/Source/NSObject.m @@ -2017,27 +2017,6 @@ static BOOL double_release_check_enabled = NO; return [self descriptionWithLocale: aLocale]; } -/** - * Uses the [NSString] implementation. - */ -- (void) descriptionWithLocale: (NSDictionary*)aLocale - indent: (unsigned)level - to: (id)output -{ - NSString *tmp = [(id)self descriptionWithLocale: aLocale]; - - [tmp descriptionWithLocale: aLocale indent: level to: output]; -} - -+ (void) descriptionWithLocale: (NSDictionary*)aLocale - indent: (unsigned)level - to: (id)output -{ - NSString *tmp = [(id)self descriptionWithLocale: aLocale]; - - [tmp descriptionWithLocale: aLocale indent: level to: output]; -} - - (BOOL) _dealloc { return YES; diff --git a/Source/NSString.m b/Source/NSString.m index 7a3a1544c..c4c1b02d9 100644 --- a/Source/NSString.m +++ b/Source/NSString.m @@ -3742,135 +3742,6 @@ handle_printf_atsign (FILE *stream, return [d writeToURL: anURL atomically: atomically]; } -- (void) descriptionWithLocale: (NSDictionary*)aLocale - indent: (unsigned int)level - to: (id)output -{ - unsigned length; - - if ((length = [self length]) == 0) - { - [output appendString: @"\"\""]; - return; - } - - if (quotablesBitmapRep == NULL) - { - setupQuotables(); - } - if ([self rangeOfCharacterFromSet: oldQuotables].length > 0 - || [self characterAtIndex: 0] == '/') - { - unichar tmp[length <= 1024 ? length : 0]; - unichar *ustring; - unichar *from; - unichar *end; - int len = 0; - - if (length <= 1024) - { - ustring = tmp; - } - else - { - ustring = NSZoneMalloc(NSDefaultMallocZone(), length*sizeof(unichar)); - } - end = &ustring[length]; - [self getCharacters: ustring]; - for (from = ustring; from < end; from++) - { - switch (*from) - { - case '\a': - case '\b': - case '\t': - case '\r': - case '\n': - case '\v': - case '\f': - case '\\': - case '\'' : - case '"' : - len += 2; - break; - - default: - if (*from < 128) - { - if (isprint(*from) || *from == ' ') - { - len++; - } - else - { - len += 4; - } - } - else - { - len += 6; - } - break; - } - } - - { - char buf[len+3]; - char *ptr = buf; - - *ptr++ = '"'; - for (from = ustring; from < end; from++) - { - switch (*from) - { - case '\a': *ptr++ = '\\'; *ptr++ = 'a'; break; - case '\b': *ptr++ = '\\'; *ptr++ = 'b'; break; - case '\t': *ptr++ = '\\'; *ptr++ = 't'; break; - case '\r': *ptr++ = '\\'; *ptr++ = 'r'; break; - case '\n': *ptr++ = '\\'; *ptr++ = 'n'; break; - case '\v': *ptr++ = '\\'; *ptr++ = 'v'; break; - case '\f': *ptr++ = '\\'; *ptr++ = 'f'; break; - case '\\': *ptr++ = '\\'; *ptr++ = '\\'; break; - case '\'': *ptr++ = '\\'; *ptr++ = '\''; break; - case '"' : *ptr++ = '\\'; *ptr++ = '"'; break; - - default: - if (*from < 128) - { - if (isprint(*from) || *from == ' ') - { - *ptr++ = *from; - } - else - { - sprintf(ptr, "\\%03o", *(unsigned char*)from); - ptr = &ptr[4]; - } - } - else - { - sprintf(ptr, "\\u%04x", *from); - ptr = &ptr[6]; - } - break; - } - } - *ptr++ = '"'; - *ptr = '\0'; - [output appendString: [NSStringClass stringWithCString: buf]]; - } - if (length > 1024) - { - NSZoneFree(NSDefaultMallocZone(), ustring); - } - } - else - { - [output appendString: self]; - } -} - - /* NSCopying Protocol */ - (id) copyWithZone: (NSZone*)zone