diff --git a/ChangeLog b/ChangeLog index 945dbff57..3190bf7f5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2004-05-14 Richard Frith-Macdonald + + * Source/GSFormat.h: + * Source/GSFormat.m: + * Source/GSPrivate.h: + * Source/GSString.m: + * Source/GSeq.h: + * Source/NSString.m: + Updates to allow GSFormat to write directly into an existing string + in either 16bit or 8bit format. Also tidyups and modifications to + use temporary buffers on stack rather than malloc/free as long as + the buffers are reasonably sized. + * Source/benchmark.m: Trivial string format benchmarks ... + Performance improvement of 10% for initWithFormat and 90% for + appendFormat operations. + 2004-05-13 Richard Frith-Macdonald * Source/NSCalendarDate.m: ([initWithString:calendarFormat:locale:]) diff --git a/Source/GSFormat.h b/Source/GSFormat.h index 4e4bbe1f8..4780d7f85 100644 --- a/Source/GSFormat.h +++ b/Source/GSFormat.h @@ -26,18 +26,12 @@ #define __GSFormat_H_ #include +#include "GSPrivate.h" @class NSDictionary; -typedef struct { - unichar *buf; - size_t len; - size_t size; - NSZone *z; -} FormatBuf_t; - void -GSFormat(FormatBuf_t *fb, const unichar *fmt, va_list ap, NSDictionary *loc); +GSFormat(GSStr fb, const unichar *fmt, va_list ap, NSDictionary *loc); #endif diff --git a/Source/GSFormat.m b/Source/GSFormat.m index 917c34c73..24822c063 100644 --- a/Source/GSFormat.m +++ b/Source/GSFormat.m @@ -127,7 +127,7 @@ struct printf_info }; /* Type of a printf specifier-handler function. - STREAM is the FormatBuf_t on which to write output. + STREAM is the GSStr on which to write output. INFO gives information about the format specification. ARGS is a vector of pointers to the argument data; the number of pointers will be the number returned @@ -733,32 +733,8 @@ parse_one_spec (const unichar *format, size_t posn, struct printf_spec *spec, } -#define outchar(Ch) \ - do \ - { \ - register const wint_t outc = (Ch); \ - if (s->len+1 >= s->size) { \ - s->size += s->size / 2; \ - s->buf = NSZoneRealloc(s->z, s->buf, s->size*sizeof(s->buf[0])); \ - } \ - s->buf[s->len++] = outc; \ - ++done; \ - } \ - while (0) - -#define outstring(String, Len) \ - do \ - { \ - unsigned i; \ - \ - if (s->len+((unsigned)(Len)) >= s->size) { \ - s->size += s->size/2 > ((unsigned)(Len))? s->size/2: (unsigned)(Len); \ - s->buf = NSZoneRealloc(s->z, s->buf, s->size*sizeof(s->buf[0])); \ - } \ - for (i=0; i < ((unsigned)(Len)); i++) s->buf[s->len++] = (String)[i]; \ - done += (unsigned)(Len); \ - } \ - while (0) +#define outchar(Ch) GSStrAppendUnichar(s, Ch) +#define outstring(String, Len) GSStrAppendUnichars(s, String, Len) /* For handling long_double and longlong we use the same flag. If `long' and `long long' are effectively the same type define it to @@ -787,7 +763,7 @@ static const unichar null[] = {'(','n','u','l','l',')','\0'}; /* Handle unknown format specifier. */ -static int printf_unknown (FormatBuf_t *, const struct printf_info *, +static int printf_unknown (GSStr, const struct printf_info *, const void *const *); /* Group digits of number string. */ @@ -796,7 +772,7 @@ static unichar *group_number (unichar *, unichar *, const char *, NSString *); /* The function itself. */ void -GSFormat (FormatBuf_t *s, const unichar *format, va_list ap, +GSFormat (GSStr s, const unichar *format, va_list ap, NSDictionary *locale) { /* The character used as thousands separator. */ @@ -866,12 +842,6 @@ NSDictionary *locale) #define CHAR_CLASS(Ch) (jump_table[(wint_t) (Ch) - ' ']) # define JUMP_TABLE_TYPE const void *const - if (s->size == 0) - { - s->buf = NSZoneMalloc(s->z, 100*sizeof(unichar)); - s->size = 100; - } - /* Initialize local variables. */ done = 0; grouping = (const char *) -1; @@ -1768,7 +1738,7 @@ NSDictionary *locale) /* Allocate dynamically an array which definitely is long enough for the wide character version. */ if (len < 8192 - || ((string = (unichar *) NSZoneMalloc(s->z, len * sizeof (unichar))) + || ((string = (unichar *) NSZoneMalloc(s->_zone, len * sizeof (unichar))) == NULL)) string = (unichar *) alloca (len * sizeof (unichar)); else @@ -1795,7 +1765,7 @@ NSDictionary *locale) /* Allocate dynamically an array which definitely is long enough for the wide character version. */ if (len < 8192 - || ((string = (unichar *) NSZoneMalloc(s->z, len * sizeof (unichar))) + || ((string = (unichar *) NSZoneMalloc(s->_zone, len * sizeof (unichar))) == NULL)) string = (unichar *) alloca (len * sizeof (unichar)); else @@ -1819,7 +1789,7 @@ NSDictionary *locale) PAD (' '); } if (string_malloced) - NSZoneFree(s->z, string); + NSZoneFree(s->_zone, string); } break; @@ -1851,7 +1821,7 @@ NSDictionary *locale) /* Allocate dynamically an array which definitely is long enough for the wide character version. */ if (len < 8192 - || ((string = (unichar *) NSZoneMalloc(s->z, len * sizeof (unichar))) + || ((string = (unichar *) NSZoneMalloc(s->_zone, len * sizeof (unichar))) == NULL)) string = (unichar *) alloca (len * sizeof (unichar)); else @@ -1875,7 +1845,7 @@ NSDictionary *locale) PAD (' '); } if (string_malloced) - NSZoneFree(s->z, string); + NSZoneFree(s->_zone, string); } break; @@ -1919,7 +1889,7 @@ all_done: /* Handle an unknown format specifier. This prints out a canonicalized representation of the format spec itself. */ static int -printf_unknown (FormatBuf_t *s, const struct printf_info *info, +printf_unknown (GSStr s, const struct printf_info *info, const void *const *args) { diff --git a/Source/GSPrivate.h b/Source/GSPrivate.h index 38505c93b..b8193f5f9 100644 --- a/Source/GSPrivate.h +++ b/Source/GSPrivate.h @@ -106,6 +106,42 @@ GS_EXPORT BOOL GSIsByteEncoding(NSStringEncoding encoding); } @end +/* + * GSMutableString - concrete mutable string, capable of changing its storage + * from holding 8-bit to 16-bit character set. + */ +@interface GSMutableString : NSMutableString +{ + union { + unichar *u; + unsigned char *c; + } _contents; + unsigned int _count; + struct { + unsigned int wide: 1; + unsigned int free: 1; + unsigned int unused: 2; + unsigned int hash: 28; + } _flags; + NSZone *_zone; + unsigned int _capacity; +} +@end + +/* + * Typedef for access to internals of concrete string objects. + */ +typedef struct { + @defs(GSMutableString) +} GSStr_t; +typedef GSStr_t *GSStr; + +/* + * Functions to append to GSStr + */ +extern void GSStrAppendUnichar(GSStr s, unichar); +extern void GSStrAppendUnichars(GSStr s, const unichar *u, unsigned l); + /* * Enumeration for MacOS-X compatibility user defaults settings. * For efficiency, we save defaults information which is used by the diff --git a/Source/GSString.m b/Source/GSString.m index 8bc12479f..30ed0ea6c 100644 --- a/Source/GSString.m +++ b/Source/GSString.m @@ -180,35 +180,6 @@ instance. @end -/* - * GSMutableString - concrete mutable string, capable of changing its storage - * from holding 8-bit to 16-bit character set. - */ -@interface GSMutableString : NSMutableString -{ - union { - unichar *u; - unsigned char *c; - } _contents; - unsigned int _count; - struct { - unsigned int wide: 1; - unsigned int free: 1; - unsigned int unused: 2; - unsigned int hash: 28; - } _flags; - NSZone *_zone; - unsigned int _capacity; -} -@end - -/* - * Typedef for access to internals of concrete string objects. - */ -typedef struct { - @defs(GSMutableString) -} *ivars; - /* * Include sequence handling code with instructions to generate search * and compare functions for NSString objects. @@ -378,9 +349,9 @@ setup(void) - (id) initWithCharacters: (const unichar*)chars length: (unsigned)length { - ivars me; + GSStr me; - me = (ivars)NSAllocateObject(GSUnicodeInlineStringClass, + me = (GSStr)NSAllocateObject(GSUnicodeInlineStringClass, length*sizeof(unichar), GSObjCZone(self)); me->_contents.u = (unichar*)&((GSUnicodeInlineString*)me)[1]; me->_count = length; @@ -397,9 +368,9 @@ setup(void) length: (unsigned)length freeWhenDone: (BOOL)flag { - ivars me; + GSStr me; - me = (ivars)NSAllocateObject(GSUnicodeBufferStringClass, 0, GSObjCZone(self)); + me = (GSStr)NSAllocateObject(GSUnicodeBufferStringClass, 0, GSObjCZone(self)); me->_contents.u = chars; me->_count = length; me->_flags.wide = 1; @@ -418,9 +389,9 @@ setup(void) { if (defEnc == intEnc) { - ivars me; + GSStr me; - me = (ivars)NSAllocateObject(GSCInlineStringClass, length, + me = (GSStr)NSAllocateObject(GSCInlineStringClass, length, GSObjCZone(self)); me->_contents.c = (unsigned char*)&((GSCInlineString*)me)[1]; me->_count = length; @@ -451,9 +422,9 @@ setup(void) { if (defEnc == intEnc) { - ivars me; + GSStr me; - me = (ivars)NSAllocateObject(GSCBufferStringClass, 0, GSObjCZone(self)); + me = (GSStr)NSAllocateObject(GSCBufferStringClass, 0, GSObjCZone(self)); me->_contents.c = (unsigned char*)chars; me->_count = length; me->_flags.wide = 0; @@ -492,7 +463,7 @@ setup(void) { unsigned length; Class c; - ivars me; + GSStr me; if (string == nil) [NSException raise: NSInvalidArgumentException @@ -505,19 +476,19 @@ setup(void) length = [string length]; if (GSObjCIsKindOf(c, GSCStringClass) == YES || c == NSConstantStringClass || (GSObjCIsKindOf(c, GSMutableStringClass) == YES - && ((ivars)string)->_flags.wide == 0)) + && ((GSStr)string)->_flags.wide == 0)) { /* * For a GSCString subclass, and ??ConstantString, or an 8-bit * GSMutableString, we can copy the bytes directly into a GSCString. */ - me = (ivars)NSAllocateObject(GSCInlineStringClass, + me = (GSStr)NSAllocateObject(GSCInlineStringClass, length, GSObjCZone(self)); 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); + memcpy(me->_contents.c, ((GSStr)string)->_contents.c, length); } else if (GSObjCIsKindOf(c, GSUnicodeStringClass) == YES || GSObjCIsKindOf(c, GSMutableStringClass) == YES) @@ -526,13 +497,13 @@ setup(void) * For a GSUnicodeString subclass, or a 16-bit GSMutableString, * we can copy the bytes directly into a GSUnicodeString. */ - me = (ivars)NSAllocateObject(GSUnicodeInlineStringClass, + me = (GSStr)NSAllocateObject(GSUnicodeInlineStringClass, length*sizeof(unichar), GSObjCZone(self)); 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, + memcpy(me->_contents.u, ((GSStr)string)->_contents.u, length*sizeof(unichar)); } else @@ -541,7 +512,7 @@ setup(void) * For a string with an unknown class, we can initialise by * having the string copy its content directly into our buffer. */ - me = (ivars)NSAllocateObject(GSUnicodeInlineStringClass, + me = (GSStr)NSAllocateObject(GSUnicodeInlineStringClass, length*sizeof(unichar), GSObjCZone(self)); me->_contents.u = (unichar*)&((GSUnicodeInlineString*)me)[1]; me->_count = length; @@ -588,7 +559,7 @@ setup(void) */ static inline char* -UTF8String_c(ivars self) +UTF8String_c(GSStr self) { unsigned char *r; @@ -637,7 +608,7 @@ UTF8String_c(ivars self) } static inline char* -UTF8String_u(ivars self) +UTF8String_u(GSStr self) { unsigned c = self->_count; @@ -661,7 +632,7 @@ UTF8String_u(ivars self) } static inline BOOL -boolValue_c(ivars self) +boolValue_c(GSStr self) { if (self->_count == 0) { @@ -698,7 +669,7 @@ boolValue_c(ivars self) } static inline BOOL -boolValue_u(ivars self) +boolValue_u(GSStr self) { if (self->_count == 0) { @@ -734,7 +705,7 @@ boolValue_u(ivars self) } static inline BOOL -canBeConvertedToEncoding_c(ivars self, NSStringEncoding enc) +canBeConvertedToEncoding_c(GSStr self, NSStringEncoding enc) { if (enc == intEnc) { @@ -749,15 +720,53 @@ canBeConvertedToEncoding_c(ivars self, NSStringEncoding enc) } static inline BOOL -canBeConvertedToEncoding_u(ivars self, NSStringEncoding enc) +canBeConvertedToEncoding_u(GSStr self, NSStringEncoding enc) { - BOOL result = (*convertImp)((id)self, convertSel, enc); + BOOL result = YES; + if (enc == NSISOLatin1StringEncoding) + { + unsigned i; + + /* + * If all the unicode characters are in the 0 to 255 range + * they are all latin1. + */ + for (i = 0; i < self->_count; i++) + { + if (self->_contents.u[i] > 255) + { + result = NO; + break; + } + } + } + else if (enc == NSASCIIStringEncoding) + { + unsigned i; + + /* + * If all the unicode characters are in the 0 to 127 range + * they are all ascii. + */ + for (i = 0; i < self->_count; i++) + { + if (self->_contents.u[i] > 127) + { + result = NO; + break; + } + } + } + else + { + result = (*convertImp)((id)self, convertSel, enc); + } return result; } static inline unichar -characterAtIndex_c(ivars self, unsigned index) +characterAtIndex_c(GSStr self, unsigned index) { unichar c; @@ -772,7 +781,7 @@ characterAtIndex_c(ivars self, unsigned index) } static inline unichar -characterAtIndex_u(ivars self,unsigned index) +characterAtIndex_u(GSStr self,unsigned index) { if (index >= self->_count) [NSException raise: NSRangeException format: @"Invalid index."]; @@ -780,7 +789,7 @@ characterAtIndex_u(ivars self,unsigned index) } static inline NSComparisonResult -compare_c(ivars self, NSString *aString, unsigned mask, NSRange aRange) +compare_c(GSStr self, NSString *aString, unsigned mask, NSRange aRange) { Class c; @@ -791,18 +800,18 @@ compare_c(ivars self, NSString *aString, unsigned mask, NSRange aRange) c = GSObjCClass(aString); if (GSObjCIsKindOf(c, GSUnicodeStringClass) == YES - || (c == GSMutableStringClass && ((ivars)aString)->_flags.wide == 1)) + || (c == GSMutableStringClass && ((GSStr)aString)->_flags.wide == 1)) return strCompCsUs((id)self, aString, mask, aRange); else if (GSObjCIsKindOf(c, GSCStringClass) == YES || c == NSConstantStringClass - || (c == GSMutableStringClass && ((ivars)aString)->_flags.wide == 0)) + || (c == GSMutableStringClass && ((GSStr)aString)->_flags.wide == 0)) return strCompCsCs((id)self, aString, mask, aRange); else return strCompCsNs((id)self, aString, mask, aRange); } static inline NSComparisonResult -compare_u(ivars self, NSString *aString, unsigned mask, NSRange aRange) +compare_u(GSStr self, NSString *aString, unsigned mask, NSRange aRange) { Class c; @@ -813,18 +822,18 @@ compare_u(ivars self, NSString *aString, unsigned mask, NSRange aRange) c = GSObjCClass(aString); if (GSObjCIsKindOf(c, GSUnicodeStringClass) - || (c == GSMutableStringClass && ((ivars)aString)->_flags.wide == 1)) + || (c == GSMutableStringClass && ((GSStr)aString)->_flags.wide == 1)) return strCompUsUs((id)self, aString, mask, aRange); else if (GSObjCIsKindOf(c, GSCStringClass) || c == NSConstantStringClass - || (c == GSMutableStringClass && ((ivars)aString)->_flags.wide == 0)) + || (c == GSMutableStringClass && ((GSStr)aString)->_flags.wide == 0)) return strCompUsCs((id)self, aString, mask, aRange); else return strCompUsNs((id)self, aString, mask, aRange); } static inline char* -cString_c(ivars self) +cString_c(GSStr self) { unsigned char *r; @@ -873,7 +882,7 @@ cString_c(ivars self) } static inline char* -cString_u(ivars self) +cString_u(GSStr self) { unsigned c = self->_count; @@ -897,7 +906,7 @@ cString_u(ivars self) } static inline unsigned int -cStringLength_c(ivars self) +cStringLength_c(GSStr self) { if (defEnc == intEnc) { @@ -939,7 +948,7 @@ cStringLength_c(ivars self) } static inline unsigned int -cStringLength_u(ivars self) +cStringLength_u(GSStr self) { unsigned c = self->_count; @@ -962,7 +971,7 @@ cStringLength_u(ivars self) } static inline NSData* -dataUsingEncoding_c(ivars self, NSStringEncoding encoding, BOOL flag) +dataUsingEncoding_c(GSStr self, NSStringEncoding encoding, BOOL flag) { unsigned len = self->_count; @@ -1027,7 +1036,7 @@ dataUsingEncoding_c(ivars self, NSStringEncoding encoding, BOOL flag) } static inline NSData* -dataUsingEncoding_u(ivars self, NSStringEncoding encoding, BOOL flag) +dataUsingEncoding_u(GSStr self, NSStringEncoding encoding, BOOL flag) { unsigned len = self->_count; @@ -1064,7 +1073,7 @@ dataUsingEncoding_u(ivars self, NSStringEncoding encoding, BOOL flag) extern BOOL GSScanDouble(unichar*, unsigned, double*); static inline double -doubleValue_c(ivars self) +doubleValue_c(GSStr self) { if (self->_count == 0) { @@ -1084,7 +1093,7 @@ doubleValue_c(ivars self) } static inline double -doubleValue_u(ivars self) +doubleValue_u(GSStr self) { if (self->_count == 0) { @@ -1100,7 +1109,7 @@ doubleValue_u(ivars self) } static inline void -fillHole(ivars self, unsigned index, unsigned size) +fillHole(GSStr self, unsigned index, unsigned size) { NSCAssert(size > 0, @"size <= zero"); NSCAssert(index + size <= self->_count, @"index + size > length"); @@ -1121,7 +1130,7 @@ fillHole(ivars self, unsigned index, unsigned size) } static inline void -getCharacters_c(ivars self, unichar *buffer, NSRange aRange) +getCharacters_c(GSStr self, unichar *buffer, NSRange aRange) { unsigned len = aRange.length; @@ -1137,14 +1146,14 @@ getCharacters_c(ivars self, unichar *buffer, NSRange aRange) } static inline void -getCharacters_u(ivars self, unichar *buffer, NSRange aRange) +getCharacters_u(GSStr self, unichar *buffer, NSRange aRange) { memcpy(buffer, self->_contents.u + aRange.location, aRange.length*sizeof(unichar)); } static inline void -getCString_c(ivars self, char *buffer, unsigned int maxLength, +getCString_c(GSStr self, char *buffer, unsigned int maxLength, NSRange aRange, NSRange *leftoverRange) { int len; @@ -1177,7 +1186,7 @@ getCString_c(ivars self, char *buffer, unsigned int maxLength, } static inline void -getCString_u(ivars self, char *buffer, unsigned int maxLength, +getCString_u(GSStr self, char *buffer, unsigned int maxLength, NSRange aRange, NSRange *leftoverRange) { /* The primitive we have for converting from unicode, GSFromUnicode, @@ -1259,7 +1268,7 @@ getCString_u(ivars self, char *buffer, unsigned int maxLength, } static inline int -intValue_c(ivars self) +intValue_c(GSStr self) { if (self->_count == 0) { @@ -1277,7 +1286,7 @@ intValue_c(ivars self) } static inline int -intValue_u(ivars self) +intValue_u(GSStr self) { if (self->_count == 0) { @@ -1295,7 +1304,7 @@ intValue_u(ivars self) } static inline BOOL -isEqual_c(ivars self, id anObject) +isEqual_c(GSStr self, id anObject) { Class c; @@ -1314,16 +1323,16 @@ isEqual_c(ivars self, id anObject) c = GSObjCClass(anObject); if (c == NSConstantStringClass) { - ivars other = (ivars)anObject; + GSStr other = (GSStr)anObject; NSRange r = {0, self->_count}; if (strCompCsCs((id)self, (id)other, 0, r) == NSOrderedSame) return YES; return NO; } - else if (GSObjCIsKindOf(c, GSStringClass) == YES) + else if (GSObjCIsKindOf(c, GSStringClass) == YES || c == GSMutableStringClass) { - ivars other = (ivars)anObject; + GSStr other = (GSStr)anObject; NSRange r = {0, self->_count}; /* @@ -1362,7 +1371,7 @@ isEqual_c(ivars self, id anObject) } static inline BOOL -isEqual_u(ivars self, id anObject) +isEqual_u(GSStr self, id anObject) { Class c; @@ -1381,16 +1390,16 @@ isEqual_u(ivars self, id anObject) c = GSObjCClass(anObject); if (c == NSConstantStringClass) { - ivars other = (ivars)anObject; + GSStr other = (GSStr)anObject; NSRange r = {0, self->_count}; if (strCompUsCs((id)self, (id)other, 0, r) == NSOrderedSame) return YES; return NO; } - else if (GSObjCIsKindOf(c, GSStringClass) == YES) + else if (GSObjCIsKindOf(c, GSStringClass) == YES || c == GSMutableStringClass) { - ivars other = (ivars)anObject; + GSStr other = (GSStr)anObject; NSRange r = {0, self->_count}; /* @@ -1429,7 +1438,7 @@ isEqual_u(ivars self, id anObject) } static inline const char* -lossyCString_c(ivars self) +lossyCString_c(GSStr self) { char *r; @@ -1478,7 +1487,7 @@ lossyCString_c(ivars self) } static inline const char* -lossyCString_u(ivars self) +lossyCString_u(GSStr self) { unsigned l = 0; unsigned char *r = 0; @@ -1488,75 +1497,150 @@ lossyCString_u(ivars self) return (const char*)r; } -static inline void -makeHole(ivars self, unsigned int index, unsigned int size) +static void GSStrMakeSpace(GSStr s, unsigned size) { unsigned want; - NSCAssert(size > 0, @"size < zero"); - NSCAssert(index <= self->_count, @"index > length"); - - want = size + self->_count + 1; - if (want > self->_capacity) + want = size + s->_count + 1; + s->_capacity += s->_capacity/2; + if (want > s->_capacity) { - self->_capacity += self->_capacity/2; - if (want > self->_capacity) + s->_capacity = want; + } + if (s->_flags.free == 1) + { + /* + * If we own the character buffer, we can simply realloc. + */ + if (s->_flags.wide == 1) { - self->_capacity = want; + s->_contents.u = NSZoneRealloc(s->_zone, + s->_contents.u, s->_capacity*sizeof(unichar)); } - if (self->_flags.free == 1) + else { - /* - * If we own the character buffer, we can simply realloc. - */ - if (self->_flags.wide == 1) + s->_contents.c = NSZoneRealloc(s->_zone, + s->_contents.c, s->_capacity); + } + } + else + { + /* + * If the initial data was not to be freed, we must allocate new + * buffer, copy the data, and set up the zone we are using. + */ + if (s->_zone == 0) + { +#if GS_WITH_GC + s->_zone = GSAtomicMallocZone(); +#else + if (s->isa == 0) { - self->_contents.u = NSZoneRealloc(self->_zone, - self->_contents.u, self->_capacity*sizeof(unichar)); + s->_zone = NSDefaultMallocZone(); } else { - self->_contents.c = NSZoneRealloc(self->_zone, - self->_contents.c, self->_capacity); + s->_zone = GSObjCZone((NSString*)s); + } +#endif + } + if (s->_flags.wide == 1) + { + unichar *tmp = s->_contents.u; + + s->_contents.u = NSZoneMalloc(s->_zone, + s->_capacity*sizeof(unichar)); + if (s->_count > 0) + { + memcpy(s->_contents.u, tmp, s->_count*sizeof(unichar)); } } else { - /* - * If the initial data was not to be freed, we must allocate new - * buffer, copy the data, and set up the zone we are using. - */ - if (self->_zone == 0) - { -#if GS_WITH_GC - self->_zone = GSAtomicMallocZone(); -#else - self->_zone = GSObjCZone((NSString*)self); -#endif - } - if (self->_flags.wide == 1) - { - unichar *tmp = self->_contents.u; + unsigned char *tmp = s->_contents.c; - self->_contents.u = NSZoneMalloc(self->_zone, - self->_capacity*sizeof(unichar)); - if (self->_count > 0) - { - memcpy(self->_contents.u, tmp, self->_count*sizeof(unichar)); - } - } - else + s->_contents.c = NSZoneMalloc(s->_zone, s->_capacity); + if (s->_count > 0) { - unsigned char *tmp = self->_contents.c; - - self->_contents.c = NSZoneMalloc(self->_zone, self->_capacity); - if (self->_count > 0) - { - memcpy(self->_contents.c, tmp, self->_count); - } + memcpy(s->_contents.c, tmp, s->_count); } - self->_flags.free = 1; } + s->_flags.free = 1; + } +} + +static void GSStrWiden(GSStr s) +{ + unichar *tmp = 0; + int len = 0; + + NSCAssert(s->_flags.wide == 0, @"string is not wide"); + + /* + * As a special case, where we are ascii or latin1 and the buffer size + * is big enough, we can widen to unicode without having to allocate + * more memory or call a character conversion function. + */ + if (s->_count <= s->_capacity / 2) + { + if (intEnc == NSISOLatin1StringEncoding + || intEnc == NSASCIIStringEncoding) + { + len = s->_count; + while (len-- > 0) + { + s->_contents.u[len] = s->_contents.c[len]; + } + s->_capacity /= 2; + s->_flags.wide = 1; + return; + } + } + + if (!s->_zone) + { +#if GS_WITH_GC + s->_zone = GSAtomicMallocZone(); +#else + if (s->isa == 0) + { + s->_zone = NSDefaultMallocZone(); + } + else + { + s->_zone = GSObjCZone((NSString*)s); + } +#endif + } + + if (!GSToUnicode(&tmp, &len, s->_contents.c, s->_count, intEnc, s->_zone, 0)) + { + [NSException raise: NSInternalInconsistencyException + format: @"widen of string failed"]; + } + if (s->_flags.free == 1) + { + NSZoneFree(s->_zone, s->_contents.c); + } + else + { + s->_flags.free = 1; + } + s->_contents.u = tmp; + s->_flags.wide = 1; + s->_count = len; + s->_capacity = len; +} + +static inline void +makeHole(GSStr self, unsigned int index, unsigned int size) +{ + NSCAssert(size > 0, @"size < zero"); + NSCAssert(index <= self->_count, @"index > length"); + + if (self->_count + size + 1 >= self->_capacity) + { + GSStrMakeSpace((GSStr)self, size); } if (index < self->_count) @@ -1580,7 +1664,7 @@ makeHole(ivars self, unsigned int index, unsigned int size) } static inline NSRange -rangeOfSequence_c(ivars self, unsigned anIndex) +rangeOfSequence_c(GSStr self, unsigned anIndex) { if (anIndex >= self->_count) [NSException raise: NSRangeException format:@"Invalid location."]; @@ -1589,7 +1673,7 @@ rangeOfSequence_c(ivars self, unsigned anIndex) } static inline NSRange -rangeOfSequence_u(ivars self, unsigned anIndex) +rangeOfSequence_u(GSStr self, unsigned anIndex) { unsigned start; unsigned end; @@ -1608,7 +1692,7 @@ rangeOfSequence_u(ivars self, unsigned anIndex) } static inline NSRange -rangeOfCharacter_c(ivars self, NSCharacterSet *aSet, unsigned mask, +rangeOfCharacter_c(GSStr self, NSCharacterSet *aSet, unsigned mask, NSRange aRange) { int i; @@ -1655,7 +1739,7 @@ rangeOfCharacter_c(ivars self, NSCharacterSet *aSet, unsigned mask, } static inline NSRange -rangeOfCharacter_u(ivars self, NSCharacterSet *aSet, unsigned mask, +rangeOfCharacter_u(GSStr self, NSCharacterSet *aSet, unsigned mask, NSRange aRange) { int i; @@ -1698,7 +1782,7 @@ rangeOfCharacter_u(ivars self, NSCharacterSet *aSet, unsigned mask, } static inline NSRange -rangeOfString_c(ivars self, NSString *aString, unsigned mask, NSRange aRange) +rangeOfString_c(GSStr self, NSString *aString, unsigned mask, NSRange aRange) { Class c; @@ -1709,18 +1793,18 @@ rangeOfString_c(ivars self, NSString *aString, unsigned mask, NSRange aRange) c = GSObjCClass(aString); if (GSObjCIsKindOf(c, GSUnicodeStringClass) == YES - || (c == GSMutableStringClass && ((ivars)aString)->_flags.wide == 1)) + || (c == GSMutableStringClass && ((GSStr)aString)->_flags.wide == 1)) return strRangeCsUs((id)self, aString, mask, aRange); else if (GSObjCIsKindOf(c, GSCStringClass) == YES || c == NSConstantStringClass - || (c == GSMutableStringClass && ((ivars)aString)->_flags.wide == 0)) + || (c == GSMutableStringClass && ((GSStr)aString)->_flags.wide == 0)) return strRangeCsCs((id)self, aString, mask, aRange); else return strRangeCsNs((id)self, aString, mask, aRange); } static inline NSRange -rangeOfString_u(ivars self, NSString *aString, unsigned mask, NSRange aRange) +rangeOfString_u(GSStr self, NSString *aString, unsigned mask, NSRange aRange) { Class c; @@ -1731,18 +1815,18 @@ rangeOfString_u(ivars self, NSString *aString, unsigned mask, NSRange aRange) c = GSObjCClass(aString); if (GSObjCIsKindOf(c, GSUnicodeStringClass) == YES - || (c == GSMutableStringClass && ((ivars)aString)->_flags.wide == 1)) + || (c == GSMutableStringClass && ((GSStr)aString)->_flags.wide == 1)) return strRangeUsUs((id)self, aString, mask, aRange); else if (GSObjCIsKindOf(c, GSCStringClass) == YES || c == NSConstantStringClass - || (c == GSMutableStringClass && ((ivars)aString)->_flags.wide == 0)) + || (c == GSMutableStringClass && ((GSStr)aString)->_flags.wide == 0)) return strRangeUsCs((id)self, aString, mask, aRange); else return strRangeUsNs((id)self, aString, mask, aRange); } static inline NSString* -substring_c(ivars self, NSRange aRange) +substring_c(GSStr self, NSRange aRange) { id sub; @@ -1765,7 +1849,7 @@ substring_c(ivars self, NSRange aRange) } static inline NSString* -substring_u(ivars self, NSRange aRange) +substring_u(GSStr self, NSRange aRange) { id sub; @@ -1792,25 +1876,22 @@ substring_u(ivars self, NSRange aRange) * Function to examine the given string and see if it is one of our concrete * string classes. Converts the mutable string (self) from 8-bit to 16-bit * representation if necessary in order to contain the data in aString. - * Returns a pointer to aStrings ivars if aString is a concrete class + * Returns a pointer to aStrings GSStr if aString is a concrete class * from which contents may be copied directly without conversion. */ -static inline ivars -transmute(ivars self, NSString *aString) +static inline GSStr +transmute(GSStr self, NSString *aString) { - ivars other; - BOOL transmute; + GSStr other = (GSStr)aString; + BOOL transmute = YES; Class c = GSObjCClass(aString); // NB aString must not be nil - other = (ivars)aString; - transmute = YES; - if (self->_flags.wide == 1) { /* * This is already a unicode string, so we don't need to transmute, * but we still need to know if the other string is a unicode - * string whose ivars we can access directly. + * string whose GSStr we can access directly. */ transmute = NO; if (GSObjCIsKindOf(c, GSUnicodeStringClass) == NO @@ -1829,7 +1910,7 @@ transmute(ivars self, NSString *aString) { /* * The other string is also held in the internal 8-bit encoding, - * so we don't need to transmute, and we can use its ivars. + * so we don't need to transmute, and we can use its GSStr. */ transmute = NO; } @@ -1839,7 +1920,7 @@ transmute(ivars self, NSString *aString) /* * The other string can be converted to the internal 8-bit encoding, * via the cString method, so we don't need to transmute, but we - * can *not* use its ivars. + * can *not* use its GSStr. * NB. If 'intEnc != defEnc' the cString method of the other string * will not return data in the internal encoding. */ @@ -1852,7 +1933,7 @@ transmute(ivars self, NSString *aString) /* * The other string can not be converted to the internal 8-bit * encoding, so we need to transmute, and will then be able to - * use its ivars. + * use its GSStr. */ transmute = YES; } @@ -1861,7 +1942,7 @@ transmute(ivars self, NSString *aString) /* * The other string can not be converted to the internal 8-bit * character string, so we need to transmute, but even then we - * will not be able to use the other strings ivars because that + * will not be able to use the other strings GSStr because that * string is not a known GSString subclass. */ other = 0; @@ -1870,37 +1951,7 @@ transmute(ivars self, NSString *aString) if (transmute == YES) { - unichar *tmp = 0; - int len = 0; - - if (!self->_zone) - { -#if GS_WITH_GC - self->_zone = GSAtomicMallocZone(); -#else - self->_zone = GSObjCZone((NSString*)self); -#endif - } - - if (!GSToUnicode(&tmp, &len, self->_contents.c, self->_count, intEnc, - self->_zone, 0)) - { - [NSException raise: NSInternalInconsistencyException - format: @"transmute of string failed"]; - return 0; - } - if (self->_flags.free == 1) - { - NSZoneFree(self->_zone, self->_contents.c); - } - else - { - self->_flags.free = 1; - } - self->_contents.u = tmp; - self->_flags.wide = 1; - self->_count = len; - self->_capacity = len; + GSStrWiden((GSStr)self); } return other; @@ -1964,50 +2015,50 @@ transmute(ivars self, NSString *aString) @implementation GSCString - (const char *) UTF8String { - return UTF8String_c((ivars)self); + return UTF8String_c((GSStr)self); } - (BOOL) boolValue { - return boolValue_c((ivars)self); + return boolValue_c((GSStr)self); } - (BOOL) canBeConvertedToEncoding: (NSStringEncoding)enc { - return canBeConvertedToEncoding_c((ivars)self, enc); + return canBeConvertedToEncoding_c((GSStr)self, enc); } - (unichar) characterAtIndex: (unsigned int)index { - return characterAtIndex_c((ivars)self, index); + return characterAtIndex_c((GSStr)self, index); } - (NSComparisonResult) compare: (NSString*)aString options: (unsigned int)mask range: (NSRange)aRange { - return compare_c((ivars)self, aString, mask, aRange); + return compare_c((GSStr)self, aString, mask, aRange); } - (const char *) cString { - return cString_c((ivars)self); + return cString_c((GSStr)self); } - (unsigned int) cStringLength { - return cStringLength_c((ivars)self); + return cStringLength_c((GSStr)self); } - (NSData*) dataUsingEncoding: (NSStringEncoding)encoding allowLossyConversion: (BOOL)flag { - return dataUsingEncoding_c((ivars)self, encoding, flag); + return dataUsingEncoding_c((GSStr)self, encoding, flag); } - (double) doubleValue { - return doubleValue_c((ivars)self); + return doubleValue_c((GSStr)self); } - (void) encodeWithCoder: (NSCoder*)aCoder @@ -2029,30 +2080,30 @@ transmute(ivars self, NSString *aString) - (float) floatValue { - return doubleValue_c((ivars)self); + return doubleValue_c((GSStr)self); } - (void) getCharacters: (unichar*)buffer { - getCharacters_c((ivars)self, buffer, (NSRange){0, _count}); + getCharacters_c((GSStr)self, buffer, (NSRange){0, _count}); } - (void) getCharacters: (unichar*)buffer range: (NSRange)aRange { GS_RANGE_CHECK(aRange, _count); - getCharacters_c((ivars)self, buffer, aRange); + getCharacters_c((GSStr)self, buffer, aRange); } - (void) getCString: (char*)buffer { - getCString_c((ivars)self, buffer, NSMaximumStringLength, + getCString_c((GSStr)self, buffer, NSMaximumStringLength, (NSRange){0, _count}, 0); } - (void) getCString: (char*)buffer maxLength: (unsigned int)maxLength { - getCString_c((ivars)self, buffer, maxLength, (NSRange){0, _count}, 0); + getCString_c((GSStr)self, buffer, maxLength, (NSRange){0, _count}, 0); } - (void) getCString: (char*)buffer @@ -2061,7 +2112,7 @@ transmute(ivars self, NSString *aString) remainingRange: (NSRange*)leftoverRange { GS_RANGE_CHECK(aRange, _count); - getCString_c((ivars)self, buffer, maxLength, aRange, leftoverRange); + getCString_c((GSStr)self, buffer, maxLength, aRange, leftoverRange); } - (unsigned) hash @@ -2075,17 +2126,17 @@ transmute(ivars self, NSString *aString) - (int) intValue { - return intValue_c((ivars)self); + return intValue_c((GSStr)self); } - (BOOL) isEqual: (id)anObject { - return isEqual_c((ivars)self, anObject); + return isEqual_c((GSStr)self, anObject); } - (BOOL) isEqualToString: (NSString*)anObject { - return isEqual_c((ivars)self, anObject); + return isEqual_c((GSStr)self, anObject); } - (unsigned int) length @@ -2095,7 +2146,7 @@ transmute(ivars self, NSString *aString) - (const char*) lossyCString { - return lossyCString_c((ivars)self); + return lossyCString_c((GSStr)self); } - (id) mutableCopy @@ -2119,7 +2170,7 @@ transmute(ivars self, NSString *aString) - (NSRange) rangeOfComposedCharacterSequenceAtIndex: (unsigned)anIndex { - return rangeOfSequence_c((ivars)self, anIndex); + return rangeOfSequence_c((GSStr)self, anIndex); } - (NSRange) rangeOfCharacterFromSet: (NSCharacterSet*)aSet @@ -2127,14 +2178,14 @@ transmute(ivars self, NSString *aString) range: (NSRange)aRange { GS_RANGE_CHECK(aRange, _count); - return rangeOfCharacter_c((ivars)self, aSet, mask, aRange); + return rangeOfCharacter_c((GSStr)self, aSet, mask, aRange); } - (NSRange) rangeOfString: (NSString*)aString options: (unsigned)mask range: (NSRange)aRange { - return rangeOfString_c((ivars)self, aString, mask, aRange); + return rangeOfString_c((GSStr)self, aString, mask, aRange); } - (NSStringEncoding) smallestEncoding @@ -2145,13 +2196,13 @@ transmute(ivars self, NSString *aString) - (NSString*) substringFromRange: (NSRange)aRange { GS_RANGE_CHECK(aRange, _count); - return substring_c((ivars)self, aRange); + return substring_c((GSStr)self, aRange); } - (NSString*) substringWithRange: (NSRange)aRange { GS_RANGE_CHECK(aRange, _count); - return substring_c((ivars)self, aRange); + return substring_c((GSStr)self, aRange); } // private method for Unicode level 3 implementation @@ -2283,50 +2334,50 @@ agree, create a new GSCInlineString otherwise. @implementation GSUnicodeString - (const char *) UTF8String { - return UTF8String_u((ivars)self); + return UTF8String_u((GSStr)self); } - (BOOL) boolValue { - return boolValue_u((ivars)self); + return boolValue_u((GSStr)self); } - (BOOL) canBeConvertedToEncoding: (NSStringEncoding)enc { - return canBeConvertedToEncoding_u((ivars)self, enc); + return canBeConvertedToEncoding_u((GSStr)self, enc); } - (unichar) characterAtIndex: (unsigned int)index { - return characterAtIndex_u((ivars)self, index); + return characterAtIndex_u((GSStr)self, index); } - (NSComparisonResult) compare: (NSString*)aString options: (unsigned int)mask range: (NSRange)aRange { - return compare_u((ivars)self, aString, mask, aRange); + return compare_u((GSStr)self, aString, mask, aRange); } - (const char *) cString { - return cString_u((ivars)self); + return cString_u((GSStr)self); } - (unsigned int) cStringLength { - return cStringLength_u((ivars)self); + return cStringLength_u((GSStr)self); } - (NSData*) dataUsingEncoding: (NSStringEncoding)encoding allowLossyConversion: (BOOL)flag { - return dataUsingEncoding_u((ivars)self, encoding, flag); + return dataUsingEncoding_u((GSStr)self, encoding, flag); } - (double) doubleValue { - return doubleValue_u((ivars)self); + return doubleValue_u((GSStr)self); } - (void) encodeWithCoder: (NSCoder*)aCoder @@ -2350,30 +2401,30 @@ agree, create a new GSCInlineString otherwise. - (float) floatValue { - return doubleValue_u((ivars)self); + return doubleValue_u((GSStr)self); } - (void) getCharacters: (unichar*)buffer { - getCharacters_u((ivars)self, buffer, (NSRange){0, _count}); + getCharacters_u((GSStr)self, buffer, (NSRange){0, _count}); } - (void) getCharacters: (unichar*)buffer range: (NSRange)aRange { GS_RANGE_CHECK(aRange, _count); - getCharacters_u((ivars)self, buffer, aRange); + getCharacters_u((GSStr)self, buffer, aRange); } - (void) getCString: (char*)buffer { - getCString_u((ivars)self, buffer, NSMaximumStringLength, + getCString_u((GSStr)self, buffer, NSMaximumStringLength, (NSRange){0, _count}, 0); } - (void) getCString: (char*)buffer maxLength: (unsigned int)maxLength { - getCString_u((ivars)self, buffer, maxLength, (NSRange){0, _count}, 0); + getCString_u((GSStr)self, buffer, maxLength, (NSRange){0, _count}, 0); } - (void) getCString: (char*)buffer @@ -2383,7 +2434,7 @@ agree, create a new GSCInlineString otherwise. { GS_RANGE_CHECK(aRange, _count); - getCString_u((ivars)self, buffer, maxLength, aRange, leftoverRange); + getCString_u((GSStr)self, buffer, maxLength, aRange, leftoverRange); } - (unsigned) hash @@ -2397,17 +2448,17 @@ agree, create a new GSCInlineString otherwise. - (int) intValue { - return intValue_u((ivars)self); + return intValue_u((GSStr)self); } - (BOOL) isEqual: (id)anObject { - return isEqual_u((ivars)self, anObject); + return isEqual_u((GSStr)self, anObject); } - (BOOL) isEqualToString: (NSString*)anObject { - return isEqual_u((ivars)self, anObject); + return isEqual_u((GSStr)self, anObject); } - (unsigned int) length @@ -2417,7 +2468,7 @@ agree, create a new GSCInlineString otherwise. - (const char*) lossyCString { - return lossyCString_u((ivars)self); + return lossyCString_u((GSStr)self); } - (id) mutableCopy @@ -2441,7 +2492,7 @@ agree, create a new GSCInlineString otherwise. - (NSRange) rangeOfComposedCharacterSequenceAtIndex: (unsigned)anIndex { - return rangeOfSequence_u((ivars)self, anIndex); + return rangeOfSequence_u((GSStr)self, anIndex); } - (NSRange) rangeOfCharacterFromSet: (NSCharacterSet*)aSet @@ -2449,14 +2500,14 @@ agree, create a new GSCInlineString otherwise. range: (NSRange)aRange { GS_RANGE_CHECK(aRange, _count); - return rangeOfCharacter_u((ivars)self, aSet, mask, aRange); + return rangeOfCharacter_u((GSStr)self, aSet, mask, aRange); } - (NSRange) rangeOfString: (NSString*)aString options: (unsigned)mask range: (NSRange)aRange { - return rangeOfString_u((ivars)self, aString, mask, aRange); + return rangeOfString_u((GSStr)self, aString, mask, aRange); } - (NSStringEncoding) smallestEncoding @@ -2467,13 +2518,13 @@ agree, create a new GSCInlineString otherwise. - (NSString*) substringFromRange: (NSRange)aRange { GS_RANGE_CHECK(aRange, _count); - return substring_u((ivars)self, aRange); + return substring_u((GSStr)self, aRange); } - (NSString*) substringWithRange: (NSRange)aRange { GS_RANGE_CHECK(aRange, _count); - return substring_u((ivars)self, aRange); + return substring_u((GSStr)self, aRange); } // private method for Unicode level 3 implementation @@ -2630,73 +2681,42 @@ agree, create a new GSUnicodeInlineString otherwise. - (void) appendFormat: (NSString*)format, ... { va_list ap; + unichar buf[1024]; + unichar *fmt = buf; + size_t len; va_start(ap, format); + /* - * If this is a unicode string, we can write the formatted data directly - * into its buffer. + * Make sure we have the format string in a nul terminated array of + * unichars for passing to GSFormat. Use on-stack memory for performance + * unless the size of the format string is really big (a rare occurrance). */ - if (_flags.wide == 1) + len = [format length]; + if (len >= 1024) { - FormatBuf_t f; - unichar *fmt; - size_t len; - - len = [format length]; fmt = objc_malloc((len+1)*sizeof(unichar)); - [format getCharacters: fmt]; - fmt[len] = '\0'; - /* - * If the buffer can't be freed, replace it with one which can before - * we attempt to append to the string (and possibly extend buffer) - */ - if (_flags.free == NO) - { - unichar *u; - - if (_zone == 0) - { - _zone = [self zone]; - } - u = NSZoneMalloc(_zone, _capacity * sizeof(unichar)); - memcpy(u, _contents.u, _count * sizeof(unichar)); - _contents.u = u; - _flags.free = YES; - } - f.z = _zone; - f.buf = _contents.u; - f.len = _count; - f.size = _capacity; - GSFormat(&f, fmt, ap, nil); - _contents.u = f.buf; - _count = f.len; - _capacity = f.size; - _flags.hash = 0; - objc_free(fmt); } - else - { - NSRange aRange; - NSString *t; + [format getCharacters: fmt]; + fmt[len] = '\0'; - /* - * Get the abstract class to give us the default placeholder string. - */ - t = (NSString*)[NSStringClass allocWithZone: NSDefaultMallocZone()]; - /* - * Now initialise with the format information ... the placeholder - * can decide whether to create a concrete 8-bit character string - * or unicode string. - */ - t = [t initWithFormat: format arguments: ap]; - /* - * Now append the created string to this one ... the appending - * method will make this string wide if necessary. - */ - aRange.location = _count; - aRange.length = 0; - [self replaceCharactersInRange: aRange withString: t]; - RELEASE(t); + /* + * If no zone is set, make sure we have one so any memory mangement + * (buffer growth) is done with the correct zone. + */ + if (_zone == 0) + { +#if GS_WITH_GC + _zone = GSAtomicMallocZone(); +#else + _zone = GSObjCZone(self); +#endif + } + GSFormat((GSStr)self, fmt, ap, nil); + _flags.hash = 0; // Invalidate the hash for this string. + if (fmt != buf) + { + objc_free(fmt); } va_end(ap); } @@ -2704,25 +2724,25 @@ agree, create a new GSUnicodeInlineString otherwise. - (BOOL) boolValue { if (_flags.wide == 1) - return boolValue_u((ivars)self); + return boolValue_u((GSStr)self); else - return boolValue_c((ivars)self); + return boolValue_c((GSStr)self); } - (BOOL) canBeConvertedToEncoding: (NSStringEncoding)enc { if (_flags.wide == 1) - return canBeConvertedToEncoding_u((ivars)self, enc); + return canBeConvertedToEncoding_u((GSStr)self, enc); else - return canBeConvertedToEncoding_c((ivars)self, enc); + return canBeConvertedToEncoding_c((GSStr)self, enc); } - (unichar) characterAtIndex: (unsigned int)index { if (_flags.wide == 1) - return characterAtIndex_u((ivars)self, index); + return characterAtIndex_u((GSStr)self, index); else - return characterAtIndex_c((ivars)self, index); + return characterAtIndex_c((GSStr)self, index); } - (NSComparisonResult) compare: (NSString*)aString @@ -2730,9 +2750,9 @@ agree, create a new GSUnicodeInlineString otherwise. range: (NSRange)aRange { if (_flags.wide == 1) - return compare_u((ivars)self, aString, mask, aRange); + return compare_u((GSStr)self, aString, mask, aRange); else - return compare_c((ivars)self, aString, mask, aRange); + return compare_c((GSStr)self, aString, mask, aRange); } - (id) copyWithZone: (NSZone*)z @@ -2756,26 +2776,26 @@ agree, create a new GSUnicodeInlineString otherwise. - (const char *) cString { if (_flags.wide == 1) - return cString_u((ivars)self); + return cString_u((GSStr)self); else - return cString_c((ivars)self); + return cString_c((GSStr)self); } - (unsigned int) cStringLength { if (_flags.wide == 1) - return cStringLength_u((ivars)self); + return cStringLength_u((GSStr)self); else - return cStringLength_c((ivars)self); + return cStringLength_c((GSStr)self); } - (NSData*) dataUsingEncoding: (NSStringEncoding)encoding allowLossyConversion: (BOOL)flag { if (_flags.wide == 1) - return dataUsingEncoding_u((ivars)self, encoding, flag); + return dataUsingEncoding_u((GSStr)self, encoding, flag); else - return dataUsingEncoding_c((ivars)self, encoding, flag); + return dataUsingEncoding_c((GSStr)self, encoding, flag); } - (void) dealloc @@ -2794,16 +2814,16 @@ agree, create a new GSUnicodeInlineString otherwise. GS_RANGE_CHECK(range, _count); if (range.length > 0) { - fillHole((ivars)self, range.location, range.length); + fillHole((GSStr)self, range.location, range.length); } } - (double) doubleValue { if (_flags.wide == 1) - return doubleValue_u((ivars)self); + return doubleValue_u((GSStr)self); else - return doubleValue_c((ivars)self); + return doubleValue_c((GSStr)self); } - (void) encodeWithCoder: (NSCoder*)aCoder @@ -2841,17 +2861,17 @@ agree, create a new GSUnicodeInlineString otherwise. - (float) floatValue { if (_flags.wide == 1) - return doubleValue_u((ivars)self); + return doubleValue_u((GSStr)self); else - return doubleValue_c((ivars)self); + return doubleValue_c((GSStr)self); } - (void) getCharacters: (unichar*)buffer { if (_flags.wide == 1) - getCharacters_u((ivars)self, buffer, (NSRange){0, _count}); + getCharacters_u((GSStr)self, buffer, (NSRange){0, _count}); else - getCharacters_c((ivars)self, buffer, (NSRange){0, _count}); + getCharacters_c((GSStr)self, buffer, (NSRange){0, _count}); } - (void) getCharacters: (unichar*)buffer range: (NSRange)aRange @@ -2859,21 +2879,21 @@ agree, create a new GSUnicodeInlineString otherwise. GS_RANGE_CHECK(aRange, _count); if (_flags.wide == 1) { - getCharacters_u((ivars)self, buffer, aRange); + getCharacters_u((GSStr)self, buffer, aRange); } else { - getCharacters_c((ivars)self, buffer, aRange); + getCharacters_c((GSStr)self, buffer, aRange); } } - (void) getCString: (char*)buffer { if (_flags.wide == 1) - getCString_u((ivars)self, buffer, NSMaximumStringLength, + getCString_u((GSStr)self, buffer, NSMaximumStringLength, (NSRange){0, _count}, 0); else - getCString_c((ivars)self, buffer, NSMaximumStringLength, + getCString_c((GSStr)self, buffer, NSMaximumStringLength, (NSRange){0, _count}, 0); } @@ -2881,9 +2901,9 @@ agree, create a new GSUnicodeInlineString otherwise. maxLength: (unsigned int)maxLength { if (_flags.wide == 1) - getCString_u((ivars)self, buffer, maxLength, (NSRange){0, _count}, 0); + getCString_u((GSStr)self, buffer, maxLength, (NSRange){0, _count}, 0); else - getCString_c((ivars)self, buffer, maxLength, (NSRange){0, _count}, 0); + getCString_c((GSStr)self, buffer, maxLength, (NSRange){0, _count}, 0); } - (void) getCString: (char*)buffer @@ -2894,11 +2914,11 @@ agree, create a new GSUnicodeInlineString otherwise. GS_RANGE_CHECK(aRange, _count); if (_flags.wide == 1) { - getCString_u((ivars)self, buffer, maxLength, aRange, leftoverRange); + getCString_u((GSStr)self, buffer, maxLength, aRange, leftoverRange); } else { - getCString_c((ivars)self, buffer, maxLength, aRange, leftoverRange); + getCString_c((GSStr)self, buffer, maxLength, aRange, leftoverRange); } } @@ -3007,25 +3027,25 @@ agree, create a new GSUnicodeInlineString otherwise. - (int) intValue { if (_flags.wide == 1) - return intValue_u((ivars)self); + return intValue_u((GSStr)self); else - return intValue_c((ivars)self); + return intValue_c((GSStr)self); } - (BOOL) isEqual: (id)anObject { if (_flags.wide == 1) - return isEqual_u((ivars)self, anObject); + return isEqual_u((GSStr)self, anObject); else - return isEqual_c((ivars)self, anObject); + return isEqual_c((GSStr)self, anObject); } - (BOOL) isEqualToString: (NSString*)anObject { if (_flags.wide == 1) - return isEqual_u((ivars)self, anObject); + return isEqual_u((GSStr)self, anObject); else - return isEqual_c((ivars)self, anObject); + return isEqual_c((GSStr)self, anObject); } - (unsigned int) length @@ -3036,9 +3056,9 @@ agree, create a new GSUnicodeInlineString otherwise. - (const char*) lossyCString { if (_flags.wide == 1) - return lossyCString_u((ivars)self); + return lossyCString_u((GSStr)self); else - return lossyCString_c((ivars)self); + return lossyCString_c((GSStr)self); } - (id) makeImmutableCopyOnFail: (BOOL)force @@ -3090,9 +3110,9 @@ agree, create a new GSUnicodeInlineString otherwise. - (NSRange) rangeOfComposedCharacterSequenceAtIndex: (unsigned)anIndex { if (_flags.wide == 1) - return rangeOfSequence_u((ivars)self, anIndex); + return rangeOfSequence_u((GSStr)self, anIndex); else - return rangeOfSequence_c((ivars)self, anIndex); + return rangeOfSequence_c((GSStr)self, anIndex); } - (NSRange) rangeOfCharacterFromSet: (NSCharacterSet*)aSet @@ -3101,9 +3121,9 @@ agree, create a new GSUnicodeInlineString otherwise. { GS_RANGE_CHECK(aRange, _count); if (_flags.wide == 1) - return rangeOfCharacter_u((ivars)self, aSet, mask, aRange); + return rangeOfCharacter_u((GSStr)self, aSet, mask, aRange); else - return rangeOfCharacter_c((ivars)self, aSet, mask, aRange); + return rangeOfCharacter_c((GSStr)self, aSet, mask, aRange); } - (NSRange) rangeOfString: (NSString*)aString @@ -3111,15 +3131,15 @@ agree, create a new GSUnicodeInlineString otherwise. range: (NSRange)aRange { if (_flags.wide == 1) - return rangeOfString_u((ivars)self, aString, mask, aRange); + return rangeOfString_u((GSStr)self, aString, mask, aRange); else - return rangeOfString_c((ivars)self, aString, mask, aRange); + return rangeOfString_c((GSStr)self, aString, mask, aRange); } - (void) replaceCharactersInRange: (NSRange)aRange withString: (NSString*)aString { - ivars other = 0; + GSStr other = 0; int offset; unsigned length = 0; @@ -3145,16 +3165,16 @@ agree, create a new GSUnicodeInlineString otherwise. */ if (length > 0) { - other = transmute((ivars)self, aString); + other = transmute((GSStr)self, aString); } if (offset < 0) { - fillHole((ivars)self, NSMaxRange(aRange) + offset, -offset); + fillHole((GSStr)self, NSMaxRange(aRange) + offset, -offset); } else if (offset > 0) { - makeHole((ivars)self, NSMaxRange(aRange), (unsigned int)offset); + makeHole((GSStr)self, NSMaxRange(aRange), (unsigned int)offset); } if (length > 0) @@ -3228,17 +3248,17 @@ agree, create a new GSUnicodeInlineString otherwise. - (void) setString: (NSString*)aString { unsigned int len = (aString == nil) ? 0 : [aString length]; - ivars other; + GSStr other; if (len == 0) { _count = 0; return; } - other = transmute((ivars)self, aString); + other = transmute((GSStr)self, aString); if (_count < len) { - makeHole((ivars)self, _count, (unsigned int)(len - _count)); + makeHole((GSStr)self, _count, (unsigned int)(len - _count)); } else { @@ -3543,53 +3563,53 @@ agree, create a new GSUnicodeInlineString otherwise. - (BOOL) canBeConvertedToEncoding: (NSStringEncoding)enc { - if (((ivars)_parent)->_flags.wide == 1) - return canBeConvertedToEncoding_u((ivars)_parent, enc); + if (((GSStr)_parent)->_flags.wide == 1) + return canBeConvertedToEncoding_u((GSStr)_parent, enc); else - return canBeConvertedToEncoding_c((ivars)_parent, enc); + return canBeConvertedToEncoding_c((GSStr)_parent, enc); } - (unichar) characterAtIndex: (unsigned int)index { - if (((ivars)_parent)->_flags.wide == 1) - return characterAtIndex_u((ivars)_parent, index); + if (((GSStr)_parent)->_flags.wide == 1) + return characterAtIndex_u((GSStr)_parent, index); else - return characterAtIndex_c((ivars)_parent, index); + return characterAtIndex_c((GSStr)_parent, index); } - (NSComparisonResult) compare: (NSString*)aString options: (unsigned int)mask range: (NSRange)aRange { - if (((ivars)_parent)->_flags.wide == 1) - return compare_u((ivars)_parent, aString, mask, aRange); + if (((GSStr)_parent)->_flags.wide == 1) + return compare_u((GSStr)_parent, aString, mask, aRange); else - return compare_c((ivars)_parent, aString, mask, aRange); + return compare_c((GSStr)_parent, aString, mask, aRange); } - (const char *) cString { - if (((ivars)_parent)->_flags.wide == 1) - return cString_u((ivars)_parent); + if (((GSStr)_parent)->_flags.wide == 1) + return cString_u((GSStr)_parent); else - return cString_c((ivars)_parent); + return cString_c((GSStr)_parent); } - (unsigned int) cStringLength { - if (((ivars)_parent)->_flags.wide == 1) - return cStringLength_u((ivars)_parent); + if (((GSStr)_parent)->_flags.wide == 1) + return cStringLength_u((GSStr)_parent); else - return cStringLength_c((ivars)_parent); + return cStringLength_c((GSStr)_parent); } - (NSData*) dataUsingEncoding: (NSStringEncoding)encoding allowLossyConversion: (BOOL)flag { - if (((ivars)_parent)->_flags.wide == 1) - return dataUsingEncoding_u((ivars)_parent, encoding, flag); + if (((GSStr)_parent)->_flags.wide == 1) + return dataUsingEncoding_u((GSStr)_parent, encoding, flag); else - return dataUsingEncoding_c((ivars)_parent, encoding, flag); + return dataUsingEncoding_c((GSStr)_parent, encoding, flag); } - (void) encodeWithCoder: (NSCoder*)aCoder @@ -3599,7 +3619,7 @@ agree, create a new GSUnicodeInlineString otherwise. - (NSStringEncoding) fastestEncoding { - if (((ivars)_parent)->_flags.wide == 1) + if (((GSStr)_parent)->_flags.wide == 1) return NSUnicodeStringEncoding; else return intEnc; @@ -3607,101 +3627,101 @@ agree, create a new GSUnicodeInlineString otherwise. - (void) getCharacters: (unichar*)buffer { - if (((ivars)_parent)->_flags.wide == 1) + if (((GSStr)_parent)->_flags.wide == 1) { - getCharacters_u((ivars)_parent, buffer, - (NSRange){0, ((ivars)_parent)->_count}); + getCharacters_u((GSStr)_parent, buffer, + (NSRange){0, ((GSStr)_parent)->_count}); } else { - getCharacters_c((ivars)_parent, buffer, - (NSRange){0, ((ivars)_parent)->_count}); + getCharacters_c((GSStr)_parent, buffer, + (NSRange){0, ((GSStr)_parent)->_count}); } } - (void) getCharacters: (unichar*)buffer range: (NSRange)aRange { - GS_RANGE_CHECK(aRange, ((ivars)_parent)->_count); - if (((ivars)_parent)->_flags.wide == 1) + GS_RANGE_CHECK(aRange, ((GSStr)_parent)->_count); + if (((GSStr)_parent)->_flags.wide == 1) { - getCharacters_u((ivars)_parent, buffer, aRange); + getCharacters_u((GSStr)_parent, buffer, aRange); } else { - getCharacters_c((ivars)_parent, buffer, aRange); + getCharacters_c((GSStr)_parent, buffer, aRange); } } - (unsigned) hash { - if (((ivars)_parent)->_flags.hash == 0) + if (((GSStr)_parent)->_flags.hash == 0) { - ((ivars)_parent)->_flags.hash = (*hashImp)((id)_parent, hashSel); + ((GSStr)_parent)->_flags.hash = (*hashImp)((id)_parent, hashSel); } - return ((ivars)_parent)->_flags.hash; + return ((GSStr)_parent)->_flags.hash; } - (BOOL) isEqual: (id)anObject { - if (((ivars)_parent)->_flags.wide == 1) - return isEqual_u((ivars)_parent, anObject); + if (((GSStr)_parent)->_flags.wide == 1) + return isEqual_u((GSStr)_parent, anObject); else - return isEqual_c((ivars)_parent, anObject); + return isEqual_c((GSStr)_parent, anObject); } - (BOOL) isEqualToString: (NSString*)anObject { - if (((ivars)_parent)->_flags.wide == 1) - return isEqual_u((ivars)_parent, anObject); + if (((GSStr)_parent)->_flags.wide == 1) + return isEqual_u((GSStr)_parent, anObject); else - return isEqual_c((ivars)_parent, anObject); + return isEqual_c((GSStr)_parent, anObject); } - (unsigned int) length { - return ((ivars)_parent)->_count; + return ((GSStr)_parent)->_count; } - (const char*) lossyCString { - if (((ivars)_parent)->_flags.wide == 1) - return lossyCString_u((ivars)_parent); + if (((GSStr)_parent)->_flags.wide == 1) + return lossyCString_u((GSStr)_parent); else - return lossyCString_c((ivars)_parent); + return lossyCString_c((GSStr)_parent); } - (NSRange) rangeOfComposedCharacterSequenceAtIndex: (unsigned)anIndex { - if (((ivars)_parent)->_flags.wide == 1) - return rangeOfSequence_u((ivars)_parent, anIndex); + if (((GSStr)_parent)->_flags.wide == 1) + return rangeOfSequence_u((GSStr)_parent, anIndex); else - return rangeOfSequence_c((ivars)_parent, anIndex); + return rangeOfSequence_c((GSStr)_parent, anIndex); } - (NSRange) rangeOfCharacterFromSet: (NSCharacterSet*)aSet options: (unsigned)mask range: (NSRange)aRange { - GS_RANGE_CHECK(aRange, ((ivars)_parent)->_count); - if (((ivars)_parent)->_flags.wide == 1) - return rangeOfCharacter_u((ivars)_parent, aSet, mask, aRange); + GS_RANGE_CHECK(aRange, ((GSStr)_parent)->_count); + if (((GSStr)_parent)->_flags.wide == 1) + return rangeOfCharacter_u((GSStr)_parent, aSet, mask, aRange); else - return rangeOfCharacter_c((ivars)_parent, aSet, mask, aRange); + return rangeOfCharacter_c((GSStr)_parent, aSet, mask, aRange); } - (NSRange) rangeOfString: (NSString*)aString options: (unsigned)mask range: (NSRange)aRange { - if (((ivars)_parent)->_flags.wide == 1) - return rangeOfString_u((ivars)_parent, aString, mask, aRange); + if (((GSStr)_parent)->_flags.wide == 1) + return rangeOfString_u((GSStr)_parent, aString, mask, aRange); else - return rangeOfString_c((ivars)_parent, aString, mask, aRange); + return rangeOfString_c((GSStr)_parent, aString, mask, aRange); } - (NSStringEncoding) smallestEncoding { - if (((ivars)_parent)->_flags.wide == 1) + if (((GSStr)_parent)->_flags.wide == 1) { return NSUnicodeStringEncoding; } @@ -3718,7 +3738,7 @@ agree, create a new GSUnicodeInlineString otherwise. * strings, as such its ivar layout is determined by the compiler * and consists of a pointer (_contents.c) and a character count * (_count). So, while this class inherits GSCString behavior, - * the code must make sure not to use any other GSCString ivars + * the code must make sure not to use any other GSCString GSStr * when accesssing an NXConstantString.

*/ @implementation NXConstantString @@ -3736,7 +3756,7 @@ agree, create a new GSUnicodeInlineString otherwise. * Access instance variables of NXConstantString class consistently * with other concrete NSString subclasses. */ -#define _self ((ivars)self) +#define _self ((GSStr)self) - (id) initWithCharacters: (unichar*)byteString length: (unsigned int)length @@ -3864,9 +3884,9 @@ agree, create a new GSUnicodeInlineString otherwise. if (GSObjCIsKindOf(c, GSCStringClass) == YES || c == NSConstantStringClass - || (c == GSMutableStringClass && ((ivars)anObject)->_flags.wide == 0)) + || (c == GSMutableStringClass && ((GSStr)anObject)->_flags.wide == 0)) { - ivars other = (ivars)anObject; + GSStr other = (GSStr)anObject; if (_self->_count != other->_count) return NO; @@ -3914,9 +3934,9 @@ agree, create a new GSUnicodeInlineString otherwise. if (GSObjCIsKindOf(c, GSCStringClass) == YES || c == NSConstantStringClass - || (c == GSMutableStringClass && ((ivars)anObject)->_flags.wide == 0)) + || (c == GSMutableStringClass && ((GSStr)anObject)->_flags.wide == 0)) { - ivars other = (ivars)anObject; + GSStr other = (GSStr)anObject; if (_self->_count != other->_count) return NO; @@ -4074,3 +4094,113 @@ agree, create a new GSUnicodeInlineString otherwise. } @end + +/** + * Append characters to a string. + */ +void GSStrAppendUnichars(GSStr s, const unichar *u, unsigned l) +{ + /* + * Make the string wide if necessary. + */ + if (s->_flags.wide == 0) + { + BOOL widen = NO; + + if (intEnc == NSISOLatin1StringEncoding) + { + unsigned i; + + for (i = 0; i < l; i++) + { + if (u[i] > 255) + { + widen = YES; + break; + } + } + } + else + { + unsigned i; + + for (i = 0; i < l; i++) + { + if (u[i] > 127) + { + widen = YES; + break; + } + } + } + if (widen == YES) + { + GSStrWiden(s); + } + } + + /* + * Make room for the characters we are appending. + */ + if (s->_count + l + 1 >= s->_capacity) + { + GSStrMakeSpace(s, l); + } + + /* + * Copy the characters into place. + */ + if (s->_flags.wide == 1) + { + unsigned i; + + for (i = 0; i < l; i++) + { + s->_contents.u[s->_count++] = u[i]; + } + } + else + { + unsigned i; + + for (i = 0; i < l; i++) + { + s->_contents.c[s->_count++] = u[i]; + } + } +} + +void GSStrAppendUnichar(GSStr s, unichar u) +{ + /* + * Make the string wide if necessary. + */ + if (s->_flags.wide == 0) + { + if (u > 255 || (u > 127 && intEnc != NSISOLatin1StringEncoding)) + { + GSStrWiden(s); + } + } + + /* + * Make room for the characters we are appending. + */ + if (s->_count + 2 >= s->_capacity) + { + GSStrMakeSpace(s, 1); + } + + /* + * Copy the characters into place. + */ + if (s->_flags.wide == 1) + { + s->_contents.u[s->_count++] = u; + } + else + { + s->_contents.c[s->_count++] = u; + } +} + diff --git a/Source/GSeq.h b/Source/GSeq.h index 49f627fae..34513e929 100644 --- a/Source/GSeq.h +++ b/Source/GSeq.h @@ -290,14 +290,14 @@ static inline void GSeq_uppercase(GSeq seq) * Set up macros for dealing with 'self' on the basis of GSQ_S */ #if GSEQ_S == GSEQ_US -#define GSEQ_ST ivars +#define GSEQ_ST GSStr #define GSEQ_SLEN s->_count #define GSEQ_SGETC(I) s->_contents.u[I] #define GSEQ_SGETR(B,R) memcpy(B, &s->_contents.u[R.location], 2*(R).length) #define GSEQ_SRANGE(I) (*srImp)((id)s, ranSel, I) #else #if GSEQ_S == GSEQ_CS -#define GSEQ_ST ivars +#define GSEQ_ST GSStr #define GSEQ_SLEN s->_count #define GSEQ_SGETC(I) (unichar)s->_contents.c[I] #define GSEQ_SGETR(B,R) ( { \ @@ -322,14 +322,14 @@ static inline void GSeq_uppercase(GSeq seq) * Set up macros for dealing with 'other' string on the basis of GSQ_O */ #if GSEQ_O == GSEQ_US -#define GSEQ_OT ivars +#define GSEQ_OT GSStr #define GSEQ_OLEN o->_count #define GSEQ_OGETC(I) o->_contents.u[I] #define GSEQ_OGETR(B,R) memcpy(B, &o->_contents.u[R.location], 2*(R).length) #define GSEQ_ORANGE(I) (*orImp)((id)o, ranSel, I) #else #if GSEQ_O == GSEQ_CS -#define GSEQ_OT ivars +#define GSEQ_OT GSStr #define GSEQ_OLEN o->_count #define GSEQ_OGETC(I) (unichar)o->_contents.c[I] #define GSEQ_OGETR(B,R) ( { \ diff --git a/Source/NSString.m b/Source/NSString.m index 105ac98fc..4b7716433 100644 --- a/Source/NSString.m +++ b/Source/NSString.m @@ -69,6 +69,7 @@ // For private method _decodePropertyListForKey: #include "Foundation/NSKeyedArchiver.h" #include "GNUstepBase/GSMime.h" +#include "GSPrivate.h" #include "GSFormat.h" #include #include @@ -1153,321 +1154,70 @@ handle_printf_atsign (FILE *stream, locale: (NSDictionary*)locale arguments: (va_list)argList { - FormatBuf_t f; - unichar *fmt; + unsigned char buf[2048]; + GSStr_t f; + unichar fbuf[1024]; + unichar *fmt = fbuf; size_t len; + /* + * First we provide an array of unichar characters containing the + * format string. For performance reasons we try to use an on-stack + * buffer if the format string is small enough ... it almost always + * will be. + */ len = [format length]; - fmt = objc_malloc((len+1)*sizeof(unichar)); + if (len >= 1024) + { + fmt = objc_malloc((len+1)*sizeof(unichar)); + } [format getCharacters: fmt]; fmt[len] = '\0'; - f.z = NSDefaultMallocZone(); - f.buf = NSZoneMalloc(f.z, 100*sizeof(unichar)); - f.len = 0; - f.size = 100; + + /* + * Now set up 'f' as a GSMutableString object whose initial buffer is + * allocated on the stack. The GSFormat function can write into it. + */ + f.isa = GSMutableStringClass; + f._zone = NSDefaultMallocZone(); + f._contents.c = buf; + f._capacity = sizeof(buf); + f._count = 0; + f._flags.wide = 0; + f._flags.free = 0; GSFormat(&f, fmt, argList, locale); - objc_free(fmt); - // don't use noCopy because f.size > f.len! - self = [self initWithCharacters: f.buf length: f.len]; - NSZoneFree(f.z, f.buf); - return self; -} - -#if 0 -/* xxx Change this when we have non-CString classes */ -- (id) initWithFormat: (NSString*)format - locale: (NSDictionary*)locale - arguments: (va_list)argList -{ -#if defined(HAVE_VSPRINTF) || defined(HAVE_VASPRINTF) - const char *format_cp = [format lossyCString]; - int format_len = strlen (format_cp); -#ifdef HAVE_VASPRINTF - char *buf; - int printed_len = 0; - NSString *ret; - -#ifndef HAVE_REGISTER_PRINTF_FUNCTION - NSZone *z = GSObjCZone(self); - - /* If the available libc doesn't have `register_printf_function()', then - the `%@' printf directive isn't available with printf() and friends. - Here we make a feable attempt to handle it. */ - { - /* We need a local copy since we change it. (Changing and undoing - the change doesn't work because some format strings are constant - strings, placed in a non-writable section of the executable, and - writing to them will cause a segfault.) */ - char format_cp_copy[format_len+1]; - char *atsign_pos; /* points to a location inside format_cp_copy */ - char *format_to_go = format_cp_copy; - char *buf_l; -#define _PRINTF_BUF_LEN 256 - int printed_local_len, avail_len = _PRINTF_BUF_LEN; - int cstring_len; - - buf = NSZoneMalloc(z, _PRINTF_BUF_LEN); - strcpy (format_cp_copy, format_cp); - /* Loop once for each `%@' in the format string. */ - while ((atsign_pos = strstr (format_to_go, "%@"))) - { - const char *cstring; - char *formatter_pos; // Position for formatter. - - /* If there is a "%%@", then do the right thing: print it literally. */ - if ((*(atsign_pos-1) == '%') - && atsign_pos != format_cp_copy) - continue; - /* Temporarily terminate the string before the `%@'. */ - *atsign_pos = '\0'; - /* Print the part before the '%@' */ - printed_local_len = VASPRINTF_LENGTH (vasprintf (&buf_l, - format_to_go, argList)); - if(buf_l) - { - if(avail_len < printed_local_len+1) - { - NS_DURING - { - buf = NSZoneRealloc(z, buf, - printed_len+printed_local_len+_PRINTF_BUF_LEN); - avail_len += _PRINTF_BUF_LEN; - } - NS_HANDLER - { - free(buf_l); - [localException raise]; - } - NS_ENDHANDLER - } - memcpy(&buf[printed_len], buf_l, printed_local_len+1); - avail_len -= printed_local_len; - printed_len += printed_local_len; - free(buf_l); - } - else - { - [NSException raise: NSMallocException - format: @"No available memory"]; - } - /* Skip arguments used in last vsprintf(). */ - while ((formatter_pos = strchr(format_to_go, '%'))) - { - char *spec_pos; // Position of conversion specifier. - - if (*(formatter_pos+1) == '%') - { - format_to_go = formatter_pos+2; - continue; - } - spec_pos = strpbrk(formatter_pos+1, "dioxXucsfeEgGpn\0"); - switch (*spec_pos) - { -#ifndef powerpc - /* FIXME: vsprintf on powerpc apparently advances the arg list - so this doesn't need to be done. Make a more general check - for this */ - case 'd': case 'i': case 'o': - case 'x': case 'X': case 'u': case 'c': - va_arg(argList, int); - break; - case 's': - if (*(spec_pos - 1) == '*') - va_arg(argList, int*); - va_arg(argList, char*); - break; - case 'f': case 'e': case 'E': case 'g': case 'G': - va_arg(argList, double); - break; - case 'p': - va_arg(argList, void*); - break; - case 'n': - va_arg(argList, int*); - break; -#endif /* NOT powerpc */ - case '\0': - spec_pos--; - break; - } - format_to_go = spec_pos+1; - } - /* Get a C-string (char*) from the String object, and print it. */ - cstring = [[(id) va_arg (argList, id) description] lossyCString]; - if (!cstring) - cstring = ""; - cstring_len = strlen(cstring); - - if(cstring_len) - { - if(avail_len < cstring_len+1) - { - buf = NSZoneRealloc(z, buf, - printed_len+cstring_len+_PRINTF_BUF_LEN); - avail_len += _PRINTF_BUF_LEN; - } - memcpy(&buf[printed_len], cstring, cstring_len+1); - avail_len -= cstring_len; - printed_len += cstring_len; - } - /* Skip over this `%@', and look for another one. */ - format_to_go = atsign_pos + 2; - } - /* Print the rest of the string after the last `%@'. */ - printed_local_len = VASPRINTF_LENGTH (vasprintf (&buf_l, - format_to_go, argList)); - if(buf_l) - { - if(avail_len < printed_local_len+1) - { - NS_DURING - { - buf = NSZoneRealloc(z, buf, - printed_len+printed_local_len+_PRINTF_BUF_LEN); - avail_len += _PRINTF_BUF_LEN; - } - NS_HANDLER - { - free(buf_l); - [localException raise]; - } - NS_ENDHANDLER - } - memcpy(&buf[printed_len], buf_l, printed_local_len+1); - avail_len -= printed_local_len; - printed_len += printed_local_len; - free(buf_l); - } - else - { - [NSException raise: NSMallocException - format: @"No available memory"]; - } - } -#else /* HAVE_VSPRINTF */ - /* The available libc has `register_printf_function()', so the `%@' - printf directive is handled by printf and friends. */ - printed_len = VASPRINTF_LENGTH (vasprintf (&buf, format_cp, argList)); - - if(!buf) + if (fmt != fbuf) { - [NSException raise: NSMallocException - format: @"No available memory"]; + objc_free(fmt); } -#endif /* !HAVE_REGISTER_PRINTF_FUNCTION */ - ret = [self initWithCString: buf]; -#ifndef HAVE_REGISTER_PRINTF_FUNCTION - NSZoneFree(z, buf); -#else - free(buf); -#endif - return ret; -#else - /* xxx horrible disgusting BUFFER_EXTRA arbitrary limit; fix this! */ - #define BUFFER_EXTRA 1024*500 - char buf[format_len + BUFFER_EXTRA]; - int printed_len = 0; + /* + * Don't use noCopy because f._contents.u may be memory on the stack, + * and even if it wasn't f._capacity may be greater than f._count so + * we could be wasting quite a bit of space. Better to accept a + * performance hit due to copying data (and allocating/deallocating + * the temporary buffer) for large strings. For most strings, the + * on-stack memory will have been used, so we will get better performance. + */ + if (f._flags.wide == 1) + { + self = [self initWithCharacters: f._contents.u length: f._count]; + } + else + { + self = [self initWithCString: f._contents.c length: f._count]; + } -#ifndef HAVE_REGISTER_PRINTF_FUNCTION - /* If the available libc doesn't have `register_printf_function()', then - the `%@' printf directive isn't available with printf() and friends. - Here we make a feable attempt to handle it. */ - { - /* We need a local copy since we change it. (Changing and undoing - the change doesn't work because some format strings are constant - strings, placed in a non-writable section of the executable, and - writing to them will cause a segfault.) */ - char format_cp_copy[format_len+1]; - char *atsign_pos; /* points to a location inside format_cp_copy */ - char *format_to_go = format_cp_copy; - strcpy (format_cp_copy, format_cp); - /* Loop once for each `%@' in the format string. */ - while ((atsign_pos = strstr (format_to_go, "%@"))) - { - const char *cstring; - char *formatter_pos; // Position for formatter. - - /* If there is a "%%@", then do the right thing: print it literally. */ - if ((*(atsign_pos-1) == '%') - && atsign_pos != format_cp_copy) - continue; - /* Temporarily terminate the string before the `%@'. */ - *atsign_pos = '\0'; - /* Print the part before the '%@' */ - printed_len += VSPRINTF_LENGTH (vsprintf (buf+printed_len, - format_to_go, argList)); - /* Skip arguments used in last vsprintf(). */ - while ((formatter_pos = strchr(format_to_go, '%'))) - { - char *spec_pos; // Position of conversion specifier. - - if (*(formatter_pos+1) == '%') - { - format_to_go = formatter_pos+2; - continue; - } - spec_pos = strpbrk(formatter_pos+1, "dioxXucsfeEgGpn\0"); - switch (*spec_pos) - { -#ifndef powerpc - /* FIXME: vsprintf on powerpc apparently advances the arg list - so this doesn't need to be done. Make a more general check - for this */ - case 'd': case 'i': case 'o': - case 'x': case 'X': case 'u': case 'c': - (void)va_arg(argList, int); - break; - case 's': - if (*(spec_pos - 1) == '*') - (void)va_arg(argList, int*); - (void)va_arg(argList, char*); - break; - case 'f': case 'e': case 'E': case 'g': case 'G': - (void)va_arg(argList, double); - break; - case 'p': - (void)va_arg(argList, void*); - break; - case 'n': - (void)va_arg(argList, int*); - break; -#endif /* NOT powerpc */ - case '\0': - spec_pos--; - break; - } - format_to_go = spec_pos+1; - } - /* Get a C-string (char*) from the String object, and print it. */ - cstring = [[(id) va_arg (argList, id) description] lossyCString]; - if (!cstring) - cstring = ""; - strcat (buf+printed_len, cstring); - printed_len += strlen (cstring); - /* Skip over this `%@', and look for another one. */ - format_to_go = atsign_pos + 2; - } - /* Print the rest of the string after the last `%@'. */ - printed_len += VSPRINTF_LENGTH (vsprintf (buf+printed_len, - format_to_go, argList)); - } -#else - /* The available libc has `register_printf_function()', so the `%@' - printf directive is handled by printf and friends. */ - printed_len = VSPRINTF_LENGTH (vsprintf (buf, format_cp, argList)); -#endif /* !HAVE_REGISTER_PRINTF_FUNCTION */ - - /* Raise an exception if we overran our buffer. */ - NSParameterAssert (printed_len < format_len + BUFFER_EXTRA - 1); - return [self initWithCString: buf]; -#endif /* HAVE_VASPRINTF */ -#else /* HAVE_VSPRINTF || HAVE_VASPRINTF */ - [self notImplemented: _cmd]; + /* + * If the string had to grow beyond the initial buffer size, we must + * release any allocated memory. + */ + if (f._flags.free == 1) + { + NSZoneFree(f._zone, f._contents.c); + } return self; -#endif } -#endif /** * Initialises the receiver with the supplied data, using the diff --git a/Testing/benchmark.m b/Testing/benchmark.m index 7ce8a5a23..66fc4edbd 100755 --- a/Testing/benchmark.m +++ b/Testing/benchmark.m @@ -371,6 +371,7 @@ bench_str() { int i; NSString *str; + NSMutableString *ms; id plist; NSString *plstr; Class arc = [NSArchiver class]; @@ -408,6 +409,25 @@ bench_str() plstr = [plist description]; printf("NSString\n"); + + START_TIMER; + for (i = 0; i < MAX_COUNT; i++) + { + str = [[stringClass alloc] initWithFormat: @"Hello %d", i]; + RELEASE(str); + } + END_TIMER; + PRINT_TIMER("NSString (1 initWithFormat:) \t"); + + ms = [NSMutableString stringWithCapacity: 0]; + START_TIMER; + for (i = 0; i < MAX_COUNT; i++) + { + [ms appendFormat: @"%d", i]; + } + END_TIMER; + PRINT_TIMER("NSString (1 appendFormat:) \t\t"); + START_TIMER; for (i = 0; i < MAX_COUNT; i++) {