/* NSString.m Copyright (C) 1995, 1996 Ovidiu Predescu and Mircea Oancea. All rights reserved. Author: Mircea Oancea This file is part of libFoundation. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. We disclaim all warranties with regard to this software, including all implied warranties of merchantability and fitness, in no event shall we be liable for any special, indirect or consequential damages or any damages whatsoever resulting from loss of use, data or profits, whether in an action of contract, negligence or other tortious action, arising out of or in connection with the use or performance of this software. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "PrivateThreadData.h" #include "NSObject+PropLists.h" #include #include int NSConvertUTF16toUTF8(unichar **sourceStart, const unichar *sourceEnd, unsigned char **targetStart, const unsigned char *targetEnd); int NSConvertUTF8toUTF16(unsigned char **sourceStart, unsigned char *sourceEnd, unichar **targetStart, const unichar *targetEnd); id NSInitStringWithData(NSString *self, NSData *data, NSStringEncoding encoding); NSData *dataUsingEncoding(NSString *self, NSStringEncoding encoding, BOOL flag); /* General ideas in NSString class cluster implementation The NSString class is an abstract class that uses only unicode representation and handles in an inefficient manner since it operates on abstract strings accessing characters one by one using characterAtIndex: method. Currently there is only one abstract subclass of classes designed to implement ANSI C strings in the default encoding provided by the operating system (assumed ASCII or ASCII extension like NEXTSTEP). This type of strings are called NS8BitString since they uses characters (usually represented on 8 bits) for immutable classes and NSMutable8BitString for mutable ones respectively. This tree of classes is implemented based on `__compact8BitBytes' method that returns a pointer to the internal representation of the string as char[], with all characters compact, with no gap. Unicode and encodings will be supported in the future when I will know how to handle the following (If you know about these please send me a message) - convert between different encodings - convert characters to upper/lower cases - handle composed character sequences So for the time being only NS8BitString/NSMutable8BitString are used but their use in the future will be limited to ...WithCString... methods and the strings generated by the compiler with @"" construct (which should generate unicode ones some day in the future). There is another problem: methods like initWithCharacters... and initWithCString assume that the underlying support is 16Bit respectively 8Bit but it is sent to an instance allocated with [NSString alloc]. So we make NSString alloc method to return a temporary string that will allocate and initialize the concrete class that will hold the character in initWith... methods. This class is NSTemporaryString. Thus one must use constructs like var = [[NSString alloc] initWith...] var = [NSString stringWith...] but not var = [NSString alloc] [var initWith...] since the second line will return an instance different from the one in var. */ #define USE_SINGLE8BITSTRING 1 // #define HAS_UNICODE_STRING 1 /*************************** * NSString abstract class ***************************/ #if USE_SINGLE8BITSTRING static id Single8BitStringStore[256]; static Class Single8BitStringClass = Nil; static inline id _getSingle8BitString(unsigned char _byte) { register id str; if (Single8BitStringClass == Nil) { Single8BitStringClass = [NSCharacter8BitString class]; memset(Single8BitStringStore, 0, sizeof(Single8BitStringStore)); } if ((str = Single8BitStringStore[(int)_byte]) == nil) { str = [[Single8BitStringClass allocWithZone:nil] initWithCString:(char *)&_byte length:1]; Single8BitStringStore[(unsigned int)_byte] = str; #if DEBUG && 0 printf("%s: add str[%p] for char %c\n", __PRETTY_FUNCTION__, str, _byte); #endif } return str; } #endif @implementation NSString static Class NSStringClass = Nil; + (void)initialize { const char *cstr; if (NSStringClass == Nil) NSStringClass = [NSString class]; #ifndef __WIN32__ /* required for toupper/tolower to work with umlauts */ if ((cstr = getenv("LC_CTYPE"))) setlocale(LC_CTYPE, cstr); if ((cstr = getenv("LC_COLLATE"))) setlocale(LC_COLLATE, cstr); #endif NSUserName(); } /* Getting a string's length */ - (unsigned int)length { [self subclassResponsibility:_cmd]; return 0; } /* Accessing characters */ - (unichar)characterAtIndex:(unsigned int)index { [self subclassResponsibility:_cmd]; return 0; } - (void)getCharacters:(unichar*)buffer { NSRange range = {0, [self length]}; [self getCharacters:buffer range:range]; } - (void)getCharacters:(unichar *)buffer range:(NSRange)aRange { register unsigned int i = 0; IMP imp = [self methodForSelector:@selector(characterAtIndex:)]; if (aRange.location + aRange.length > [self length]) { [[[IndexOutOfRangeException alloc] initWithFormat:@"range (%d,%d) in string %x of length %d", aRange.location, aRange.length, self, [self length]] raise]; } for (i = 0; i < aRange.length; i++) { buffer[i] = (unichar)(long)(*imp)(self, @selector(characterAtIndex:), aRange.location + i); } } /* Combining strings */ - (NSString *)stringByAppendingString:(NSString *)aString { unichar *buf; unsigned len1, len2; NSString *s; len1 = [self length]; len2 = [aString length]; if (len2 == 0) return [[self copy] autorelease]; buf = calloc(len1 + len2 + 1, sizeof(unichar)); [self getCharacters:buf]; [aString getCharacters:&(buf[len1])]; buf[len1 + len2] = 0; s = [NSStringClass stringWithCharacters:buf length:(len1 + len2)]; free(buf); return s; } - (NSString*)stringByAppendingFormat:(NSString*)format,... { NSMutableString *string = [self mutableCopy]; va_list va; va_start(va, format); [string appendFormat:format arguments:va]; va_end(va); return AUTORELEASE(string); } - (NSString*)stringByAppendingFormat:(NSString*)format arguments:(va_list)argList { NSMutableString *string = [self mutableCopy]; [string appendFormat:format arguments:argList]; return AUTORELEASE(string); } - (NSString*)stringByPrependingString:(NSString*)aString { return [aString stringByAppendingString:self]; } - (NSString*)stringByPrependingFormat:(NSString*)format,... { NSString *string; va_list va; va_start(va, format); string = Avsprintf(format, va); va_end(va); return [string stringByAppendingString:self]; } - (NSString*)stringByPrependingFormat:(NSString*)format arguments:(va_list)argList { NSString *string = Avsprintf(format, argList); return [string stringByAppendingString:self]; } /* Dividing strings */ - (NSArray *)componentsSeparatedByString:(NSString *)separator { unsigned int first = 0, last = 0; unsigned int slen; NSMutableArray *components; if ((slen = [separator length]) == 0) return [NSArray arrayWithObject:self]; if ([self length] == 0) return [NSArray arrayWithObject:self]; components = [NSMutableArray arrayWithCapacity:16]; while ((first = [self indexOfString:separator fromIndex:last]) !=NSNotFound) { NSRange range; NSString *substring; range.location = last; range.length = (first - last); substring = [self substringWithRange:range]; [components addObject:substring]; last = first + slen; } if ([self length] >= last) { NSString *lastComponent; lastComponent = [self substringFromIndex:last]; [components addObject:lastComponent]; } return components; } - (NSString *)substringWithRange:(NSRange)aRange { unichar * buf; id rc; if (aRange.location + aRange.length > [self length] ) [[[IndexOutOfRangeException alloc] initWithFormat:@"range (%d,%d) in string %x of length %d", aRange.location, aRange.length, self, [self length]] raise]; if (aRange.length == 0) return @""; buf = MallocAtomic(sizeof(unichar) * (aRange.length + 1)); [self getCharacters:buf range:aRange]; rc = AUTORELEASE([[NSStringClass alloc] initWithCharactersNoCopy:buf length:aRange.length freeWhenDone:YES]); return rc; } - (NSString *)substringFromRange:(NSRange)aRange { return [self substringWithRange:aRange]; } - (NSString *)substringFromIndex:(unsigned int)index { NSRange range; range.location = index; range.length = [self length]; //- index if (range.length < index) { [[[IndexOutOfRangeException alloc] initWithFormat:@"index %d in string %x of length %d", index, self, [self length]] raise]; } range.length -= index; return [self substringWithRange:range]; } - (NSString*)substringToIndex:(unsigned int)index { NSRange range = {0, index}; return [self substringWithRange:range]; } - (NSString *)stringByTrimmingCharactersInSet:(NSCharacterSet *)_set { unsigned len; unichar *buf; unsigned idx; unsigned ridx; BOOL (*isMem)(id,SEL,unichar); if ((len = [self length]) == 0) return [[self copy] autorelease]; buf = calloc(len + 1, sizeof(unichar)); [self getCharacters:buf]; isMem = (void *)[_set methodForSelector:@selector(characterIsMember:)]; for (idx = 0; (idx < len) && isMem(_set,@selector(characterIsMember:),buf[idx]); idx++) ; for (ridx = len-1; (ridx >= idx) && isMem(_set,@selector(characterIsMember:),buf[ridx]); ridx--) ; self = [NSStringClass stringWithCharacters:&(buf[idx]) length:((ridx+1) - idx)]; free(buf); return self; } /* Finding characters and substrings */ - (NSRange)rangeOfCharacterFromSet:(NSCharacterSet*)aSet { NSRange range = {0, [self length]}; return [self rangeOfCharacterFromSet:aSet options:0 range:range]; } - (NSRange)rangeOfCharacterFromSet:(NSCharacterSet*)aSet options:(unsigned int)mask { NSRange range = {0, [self length]}; return [self rangeOfCharacterFromSet:aSet options:mask range:range]; } - (NSRange)rangeOfCharacterFromSet:(NSCharacterSet*)aSet options:(unsigned int)mask range:(NSRange)aRange { // UNICODE // ENCODINGS - this code applies to the system's default encoding SEL characterIsMemberSel = @selector(characterIsMember:); IMP imp = [aSet methodForSelector:characterIsMemberSel]; if (NSMaxRange(aRange) > [self length]) [[[IndexOutOfRangeException alloc] initWithFormat:@"range %@ not in string 0x%08x of length %d", NSStringFromRange(aRange), self, [self length]] raise]; if (mask & NSBackwardsSearch) { int i; for (i = aRange.length - 1; i >= aRange.location; i--) { unichar c = [self characterAtIndex:i]; if ((*imp)(aSet, characterIsMemberSel, c) || ((mask & NSCaseInsensitiveSearch) && ((islower(c) && (*imp)(aSet, characterIsMemberSel, toupper(c))) || (isupper(c) && (*imp)(aSet, characterIsMemberSel, tolower(c)))))) { return NSMakeRange(i, 1); } } } else { unsigned int i, max; max = NSMaxRange(aRange); for (i = aRange.location; i < max; i++) { unichar c = [self characterAtIndex:i]; if ((*imp)(aSet, characterIsMemberSel, c) || ((mask & NSCaseInsensitiveSearch) && ((islower(c) && (*imp)(aSet, characterIsMemberSel, toupper(c))) || (isupper(c) && (*imp)(aSet, characterIsMemberSel, tolower(c)))))) { return NSMakeRange(i, 1); } } } return NSMakeRange(NSNotFound, 0); } - (NSRange)rangeOfString:(NSString*)string { NSRange range = {0, [self length]}; return [self rangeOfString:string options:0 range:range]; } - (NSRange)rangeOfString:(NSString*)string options:(unsigned int)mask { NSRange range = {0, [self length]}; return [self rangeOfString:string options:mask range:range]; } - (NSRange)rangeOfString:(NSString*)aString options:(unsigned int)mask range:(NSRange)aRange { // UNICODE // ENCODINGS - this code applies to the system's default encoding // islower needs to be replaced with proper NSCharacterSet calls (speed?) unsigned int i, a; NSRange range; if (aRange.location + aRange.length > [self length]) { [[[IndexOutOfRangeException alloc] initWithFormat:@"range (%d,%d) in string %x of length %d", aRange.location, aRange.length, self, [self length]] raise]; } a = [aString length]; if (a == 0 || aRange.length < a) return NSMakeRange(0,0); if (mask & NSAnchoredSearch) { range.location = aRange.location + ((mask & NSBackwardsSearch) ? aRange.length - a : 0); range.length = a; if ([self compare:aString options:mask range:range] == NSOrderedSame) return range; else return NSMakeRange(NSNotFound, 0); } if (mask & NSBackwardsSearch) { if (mask & NSCaseInsensitiveSearch) { // Backward case insensitive unichar cf; int n; cf = [aString characterAtIndex: 0 ]; cf = islower(cf) ? toupper(cf) : cf; for (n = aRange.length - a; n >= 0; n--) { unichar ca = cf; unichar cm = [self characterAtIndex:n + aRange.location ]; cm = islower(cm) ? toupper(cm) : cm; if (cm != ca) continue; for (i = 1; i < a; i++) { cm = [self characterAtIndex:n + i + aRange.location ]; ca = [aString characterAtIndex:i]; cm = islower(cm) ? toupper(cm) : cm; ca = islower(ca) ? toupper(ca) : ca; if (cm != ca) break; } if (i == a) { range.location = aRange.location + n; range.length = a; return range; } } } else { // Backward case sensitive int n; for (n = aRange.length - a; n >= 0; n--) { if ([self characterAtIndex:n + aRange.location] != [aString characterAtIndex:0]) continue; for (i = 1; i < a; i++) if ([self characterAtIndex:(n + i + aRange.location)] != [aString characterAtIndex: i] ) break; if (i == a) { range.location = aRange.location + n; range.length = a; return range; } } } } else { if (mask & NSCaseInsensitiveSearch) { // Forward case insensitive unsigned int n; unichar cf; cf = [aString characterAtIndex: n]; cf = islower(cf) ? toupper(cf) : cf; for (n = 0; n + a <= aRange.length; n++) { unichar ca = cf; unichar cm = [self characterAtIndex: n + aRange.location ]; cm = islower(cm) ? toupper(cm) : cm; if (cm != ca) continue; for (i = 1; i < a; i++) { cm = [self characterAtIndex:n + i + aRange.location]; ca = [aString characterAtIndex:i]; cm = islower(cm) ? toupper(cm) : cm; ca = islower(ca) ? toupper(ca) : ca; if (cm != ca) break; } if (i == a) { range.location = aRange.location + n; range.length = a; return range; } } } else { // Forward case sensitive unsigned int n; for (n = 0; n + a <= aRange.length; n++) { if ([self characterAtIndex:n + aRange.location] != [aString characterAtIndex: 0] ) continue; for (i = 1; i < a; i++) if ([self characterAtIndex:n + i + aRange.location] != [aString characterAtIndex:i]) break; if (i == a) { range.location = aRange.location + n; range.length = a; return range; } } } } range.location = NSNotFound; range.length = 0; return range; } - (unsigned int)indexOfString:(NSString *)substring { NSRange range = {0, [self length]}; range = [self rangeOfString:substring options:0 range:range]; return range.length ? range.location : NSNotFound; } - (unsigned int)indexOfString:(NSString*)substring fromIndex:(unsigned)index { NSRange range = {index, [self length]-index}; range = [self rangeOfString:substring options:0 range:range]; return range.length ? range.location : NSNotFound; } - (unsigned int)indexOfString:(NSString*)substring range:(NSRange)range { range = [self rangeOfString:substring options:0 range:range]; return range.length ? range.location : NSNotFound; } /* Determining composed character sequences */ - (NSRange)rangeOfComposedCharacterSequenceAtIndex:(unsigned int)anIndex { NSRange range; unsigned int length = [self length]; if (anIndex >= length) { [[[IndexOutOfRangeException alloc] initWithFormat:@"index %d out of range in string %x of length %d", anIndex, self, length] raise]; } // UNICODE range.location = anIndex; range.length = 1; return range; } /* Converting string contents into a property list */ - (id)propertyList { return NSParsePropertyListFromString(self); } - (NSMutableDictionary*)propertyListFromStringsFileFormat { return NSParseStringsFromString(self); } /* Identifying and comparing strings */ - (NSComparisonResult)caseInsensitiveCompare:(NSString*)aString { NSRange range = {0, [self length]}; return [self compare:aString options:NSCaseInsensitiveSearch range:range]; } - (NSComparisonResult)compare:(id)aString { NSRange range = {0, [self length]}; return [self compare:aString options:0 range:range]; } - (NSComparisonResult)compare:(NSString*)aString options:(unsigned int)mask { NSRange range = {0, [self length]}; return [self compare:aString options:mask range:range]; } - (NSComparisonResult)compare:(NSString*)aString options:(unsigned int)mask range:(NSRange)aRange { // UNICODE // ENCODINGS - this code applies to the system's default encoding unsigned int i, n, a; if (NSMaxRange(aRange) > [self length]) { [[[IndexOutOfRangeException alloc] initWithFormat:@"range %@ in string %x of length %d", NSStringFromRange(aRange), self, [self length]] raise]; } a = [aString length]; n = MIN(a, aRange.length); if (mask & NSCaseInsensitiveSearch) { for (i = 0; i < n; i++) { unichar cm = [self characterAtIndex:i + aRange.location]; unichar ca = [aString characterAtIndex:i]; cm = islower(cm) ? toupper(cm) : cm; ca = islower(ca) ? toupper(ca) : ca; if (cm < ca) return NSOrderedAscending; if (cm > ca) return NSOrderedDescending; } } else { for (i = 0; i < n; i++) { if ([self characterAtIndex:i + aRange.location] < [aString characterAtIndex:i]) return NSOrderedAscending; if ([self characterAtIndex:i + aRange.location] > [aString characterAtIndex:i]) return NSOrderedDescending; } } if (aRange.length < a) return NSOrderedAscending; if (aRange.length > a) return NSOrderedDescending; return NSOrderedSame; } - (BOOL)hasPrefix:(NSString *)aString { int mLen = [self length]; int aLen = [aString length]; NSRange range = {0, aLen}; #if DEBUG if (aString == nil) { NSLog(@"NOTE: [%@ hasPrefix:nil] does not work on MacOSX !", aString); #if 0 && DEBUG # warning ABORT CODE ENABLED - do not deploy! abort(); #endif } #endif if (aLen == 0) return NO; if (aLen > mLen) return NO; return [self compare:aString options:0 range:range] == NSOrderedSame; } - (BOOL)hasSuffix:(NSString*)aString { int mLen = [self length]; int aLen = [aString length]; NSRange range = {mLen-aLen, aLen}; if (aLen == 0) return NO; if (aLen > mLen) return NO; return [self compare:aString options:0 range:range] == NSOrderedSame; } - (BOOL)isEqual:(id)aString { register Class clazz; register BOOL i; NSRange range; if (self == aString) return YES; else if (aString == nil) return NO; if (NSStringClass == Nil) NSStringClass = [NSString class]; i = NO; for (clazz = *(id *)aString; clazz; clazz = class_get_super_class(clazz)) { if (clazz == NSStringClass) { i = YES; break; } } if (i == NO) return NO; range.location = 0; range.length = [self length]; return [self compare:aString options:0 range:range] == NSOrderedSame; } - (BOOL)isEqualToString:(NSString*)aString { NSRange range; if (self == aString) return YES; else if (aString == nil) return NO; range.location = 0; range.length = [self length]; return [self compare:aString options:0 range:range] == NSOrderedSame; } - (unsigned)hash { unsigned hash = 0, hash2; int i, n = [self length]; for(i = 0; i < n; i++) { hash <<= 4; // UNICODE - must use a for independent of composed characters hash += [self characterAtIndex:i]; if((hash2 = hash & 0xf0000000)) hash ^= (hash2 >> 24) ^ hash2; } return hash; } /* Getting a shared prefix */ - (NSString *)commonPrefixWithString:(NSString*)aString options:(unsigned int)mask { NSRange range = {0, 0}; int mLen; int aLen; int i; mLen = [self length]; aLen = [aString length]; if ((mLen == 0) || (aLen == 0)) return @""; for (i = 0; (i < mLen) && (i < aLen); i++) { unichar c1, c2; c1 = [self characterAtIndex:i]; c2 = [self characterAtIndex:i]; if (mask & NSCaseInsensitiveSearch) { // ENCODINGS - this code applies to the system's default encoding c1 = tolower(c1); c2 = tolower(c2); } if (c1 != c2) break; } range.length = i; return [self substringWithRange:range]; } /* Changing case */ - (NSString *)capitalizedString { // UNICODE // ENCODINGS - this code applies to the system's default encoding int i; BOOL f = YES; int length = [self cStringLength]; unichar* buf = MallocAtomic(sizeof(unichar) * (length + 1)); for (i = 0; i < length; i++) { unichar c = [self characterAtIndex:i]; if (isspace(c)) f = YES; if (f) { buf[i] = islower(c) ? toupper(c) : c; f = NO; } else buf[i] = isupper(c) ? tolower(c) : c; } buf[i] = 0; return AUTORELEASE([[NSStringClass alloc] initWithCharactersNoCopy:buf length:length freeWhenDone:YES]); } - (NSString *)lowercaseString { // UNICODE // ENCODINGS - this code applies to the system's default encoding int i; int length = [self cStringLength]; unichar* buf = MallocAtomic(sizeof(unichar) * (length+1)); for (i = 0; i < length; i++) { unichar c = [self characterAtIndex:i]; buf[i] = isupper(c) ? tolower(c) : c; } buf[i] = 0; return AUTORELEASE([[NSStringClass alloc] initWithCharactersNoCopy:buf length:length freeWhenDone:YES]); } - (NSString *)uppercaseString { // UNICODE // ENCODINGS - this code applies to the system's default encoding int i; int length = [self cStringLength]; unichar* buf = MallocAtomic(sizeof(unichar) * (length+1)); for (i = 0; i < length; i++) { unichar c = [self characterAtIndex:i]; buf[i] = islower(c) ? toupper(c) : c; } buf[i] = 0; return AUTORELEASE([[NSStringClass alloc] initWithCharactersNoCopy:buf length:length freeWhenDone:YES]); } /* Getting C strings */ - (const char *)cString { // UNICODE // ENCODINGS [self subclassResponsibility:_cmd]; return NULL; } - (unsigned int)cStringLength { // UNICODE // ENCODINGS [self subclassResponsibility:_cmd]; return 0; } - (const char *)UTF8String { NSData *d; unsigned dlen; unsigned char *cstr; if ((d = [self dataUsingEncoding:NSUTF8StringEncoding]) == nil) return NULL; if ((dlen = [d length]) == 0) { static unsigned char c = '\0'; return (const char *)&c; } cstr = NSZoneMallocAtomic([self zone], sizeof(unsigned char) * (dlen + 1)); [d getBytes:cstr]; cstr[dlen] = '\0'; #if !LIB_FOUNDATION_BOEHM_GC [NSAutoreleasedPointer autoreleasePointer:cstr]; #endif return (const char *)cstr; } - (void)getCString:(char *)buffer { NSRange range = {0, [self length]}; [self getCString:buffer maxLength:NSMaximumStringLength range:range remainingRange:NULL]; buffer[range.length] = '\0'; } - (void)getCString:(char*)buffer maxLength:(unsigned int)maxLength { NSRange range = {0, [self length]}; [self getCString:buffer maxLength:maxLength range:range remainingRange:NULL]; buffer[range.length] = '\0'; } - (void)getCString:(char*)buffer maxLength:(unsigned int)maxLength range:(NSRange)aRange remainingRange:(NSRange*)leftoverRange { unsigned int toMove, i, cLength; unichar (*charAtIndex)(id,SEL,int); charAtIndex = (void*)[self methodForSelector:@selector(characterAtIndex:)]; toMove = MIN(maxLength, aRange.length); cLength = [self cStringLength]; if (aRange.location + aRange.length > cLength) { [[[IndexOutOfRangeException alloc] initWithFormat:@"range (%d,%d) in string %x of length %d", aRange.location, aRange.length, self, cLength] raise]; } if (leftoverRange) { leftoverRange->location = aRange.location + toMove; leftoverRange->length = cLength - leftoverRange->location; } for (i = 0; i < toMove; i++) { buffer[i] = charAtIndex(self, @selector(characterAtIndex:), aRange.location + i); } if (toMove < maxLength) buffer[toMove] = '\0'; } /* Getting numeric values */ - (double)doubleValue { // UNICODE // ENCODINGS double val = 0.0; unsigned len; char *str; len = [self cStringLength]; if (len == 0) return 0.0; if ((str = malloc(len + 1)) == NULL) return 0.0; [self getCString:str]; str[len] = '\0'; sscanf(str ? str : "", " %lf ", &val); free(str); return val; } - (float)floatValue { // UNICODE // ENCODINGS float val = 0; unsigned len; char *str; if ((len = [self cStringLength]) == 0) return 0.0; if ((str = malloc(len + 1)) == NULL) return 0.0; [self getCString:str]; str[len] = '\0'; sscanf(str ? str : "", " %f ", &val); free(str); return val; } - (int)intValue { // UNICODE // ENCODINGS int val = 0; unsigned len; char *str; if ((len = [self cStringLength]) == 0) return 0; if ((str = malloc(len + 1)) == NULL) return 0; [self getCString:str]; str[len] = '\0'; sscanf(str ? str : "", " %d ", &val); free(str); return val; } /* Working with encodings */ + (NSStringEncoding *)availableStringEncodings { // UNICODE // ENCODINGS static NSStringEncoding availableEncodings[] = { NSASCIIStringEncoding, NSISOLatin1StringEncoding, NSISOLatin9StringEncoding, NSUTF8StringEncoding, NSUnicodeStringEncoding, 0 }; return availableEncodings; } + (NSStringEncoding)defaultCStringEncoding { // UNICODE // ENCODINGS #if USE_LATIN9 return NSISOLatin9StringEncoding; #else return NSISOLatin1StringEncoding; #endif } + (NSString *)localizedNameOfStringEncoding:(NSStringEncoding)encoding { switch(encoding) { case NSASCIIStringEncoding: return @"NSASCIIStringEncoding"; case NSNEXTSTEPStringEncoding: return @"NSNEXTSTEPStringEncoding"; case NSUTF8StringEncoding: return @"NSUTF8StringEncoding"; case NSISOLatin1StringEncoding: return @"NSISOLatin1StringEncoding"; case NSISOLatin9StringEncoding: return @"NSISOLatin9StringEncoding"; case NSSymbolStringEncoding: return @"NSSymbolStringEncoding"; case NSShiftJISStringEncoding: return @"NSShiftJISStringEncoding"; case NSISOLatin2StringEncoding: return @"NSISOLatin2StringEncoding"; case NSUnicodeStringEncoding: return @"NSUnicodeStringEncoding"; case NSWinLatin1StringEncoding: return @"NSWinLatin1StringEncoding"; case NSISO2022JPStringEncoding: return @"NSISO2022JPStringEncoding"; case NSWindowsCP1251StringEncoding: return @"NSWindowsCP1251StringEncoding"; case NSWindowsCP1252StringEncoding: return @"NSWindowsCP1252StringEncoding"; case NSWindowsCP1253StringEncoding: return @"NSWindowsCP1253StringEncoding"; case NSWindowsCP1254StringEncoding: return @"NSWindowsCP1254StringEncoding"; case NSWindowsCP1250StringEncoding: return @"NSWindowsCP1250StringEncoding"; case NSNonLossyASCIIStringEncoding: return @"NSNonLossyASCIIStringEncoding"; case NSJapaneseEUCStringEncoding: return @"NSJapaneseEUCStringEncoding"; case NSAdobeStandardCyrillicStringEncoding: return @"NSAdobeStandardCyrillicStringEncoding"; default: return @"Invalid encoding"; } } - (BOOL)canBeConvertedToEncoding:(NSStringEncoding)encoding { id data; data = [self dataUsingEncoding:encoding allowLossyConversion:NO]; return data ? YES : NO; } - (NSData *)dataUsingEncoding:(NSStringEncoding)encoding { return [self dataUsingEncoding:encoding allowLossyConversion:NO]; } - (NSData *)dataUsingEncoding:(NSStringEncoding)encoding allowLossyConversion:(BOOL)flag { return dataUsingEncoding(self, encoding, flag); } - (NSStringEncoding)fastestEncoding { // UNICODE // ENCODINGS return NSISOLatin1StringEncoding; } - (NSStringEncoding)smallestEncoding { // UNICODE // ENCODINGS return NSISOLatin1StringEncoding; } - (BOOL)getBytes:(void*)bytes maxLength:(unsigned int)maxLength inEncoding:(NSStringEncoding)encoding allowLossesInConversion:(BOOL)allowLossesInConversion fromRange:(NSRange)fromRange usedRange:(NSRange*)usedRange remainingRange:(NSRange*)remainingRange { // UNICODE // ENCODINGS [self notImplemented:_cmd]; return NO; } /* Writing to a file */ - (BOOL)writeToFile:(NSString *)path atomically:(BOOL)flag { // UNICODE - remove this NSData *data; data = [self dataUsingEncoding:[NSStringClass defaultCStringEncoding]]; //data = [NSData dataWithBytes:[self cString] length:[self length]]; return writeToFile(path, data, flag); } /* Encoding methods */ - (void)encodeWithCoder:(NSCoder *)aCoder { int length; unichar *buf; length = [self length]; buf = MallocAtomic(sizeof(unichar) * length); [self getCharacters:buf]; [aCoder encodeValueOfObjCType:@encode(int) at:&length]; [aCoder encodeArrayOfObjCType:@encode(unichar) count:length at:buf]; lfFree(buf); } - (id)initWithCoder:(NSCoder *)aDecoder { unichar* buf; int length; [aDecoder decodeValueOfObjCType:@encode(int) at:&length]; buf = MallocAtomic (sizeof(unichar) * length); [aDecoder decodeArrayOfObjCType:@encode(unichar) count:length at:buf]; self = AUTORELEASE(self); return [self initWithCharactersNoCopy:buf length:length freeWhenDone:YES]; } /* NSCopying methods */ - (id)copyWithZone:(NSZone *)zone { if (NSStringClass == Nil) NSStringClass = [NSString class]; return RETAIN(self); // return [[NSStringClass allocWithZone:zone] initWithString:self]; } /* NSMutableCopying methods */ - (id)mutableCopyWithZone:(NSZone*)zone { return [[NSMutableString allocWithZone:zone] initWithString:self]; } /* NSObject protocol */ - (NSString *)stringRepresentation { /* an implementation of this method must quote the string for use in property lists. */ return [self subclassResponsibility:_cmd]; } - (NSString *)description { return self; } @end /* NSString */ /********************************* * NSMutableString abstract class *********************************/ @implementation NSMutableString /* * Modifying a string */ - (void)appendFormat:(NSString*)format,... { va_list va; va_start(va, format); [self appendFormat:format arguments:va]; va_end(va); } - (void)appendFormat:(NSString*)format arguments:(va_list)argList; { // FIX : Vsprinf(self, format, argList) [self appendString:Avsprintf(format, argList)]; } - (void)appendString:(NSString*)aString { NSRange range = {[self length], 0}; [self replaceCharactersInRange:range withString:aString]; } - (void)prependFormat:(NSString*)format,... { NSRange range = {0, 0}; va_list va; va_start(va, format); [self replaceCharactersInRange:range withString:Avsprintf(format, va)]; va_end(va); } - (void)prependFormat:(NSString*)format arguments:(va_list)argList { NSRange range = {0, 0}; [self replaceCharactersInRange:range withString:Avsprintf(format, argList)]; } - (void)prependString:(NSString*)aString { NSRange range = {0, 0}; [self replaceCharactersInRange:range withString:aString]; } - (void)deleteCharactersInRange:(NSRange)range { [self replaceCharactersInRange:range withString:nil]; } - (void)insertString:(NSString*)aString atIndex:(unsigned)index { NSRange range = {index, 0}; [self replaceCharactersInRange:range withString:aString]; } - (void)setString:(NSString*)aString { NSRange range = {0, [self length]}; [self replaceCharactersInRange:range withString:aString]; } - (void)replaceCharactersInRange:(NSRange)aRange withString:(NSString*)aString { [self subclassResponsibility:_cmd]; } - (id)copyWithZone:(NSZone*)zone { if (NSStringClass == Nil) NSStringClass = [NSString class]; return [[NSStringClass allocWithZone:zone] initWithString:self]; } @end /* NSMutableString */ /********************************* * NSString creation methods *********************************/ @implementation NSString(NSStringCreation) + (id)allocWithZone:(NSZone *)zone { static Class NSTemporaryStringClass = Nil; if (NSStringClass == Nil) NSStringClass = [NSString class]; if (NSTemporaryStringClass == Nil) NSTemporaryStringClass = [NSTemporaryString class]; return (self == NSStringClass) ? [NSTemporaryStringClass allocWithZone:zone] : (id)NSAllocateObject(self, 0, zone); } + (id)localizedStringWithFormat:(NSString*)format,... { va_list va; NSString* string; va_start(va, format); string = AUTORELEASE([[self alloc] initWithFormat:format arguments:va]); va_end(va); return string; } + (id)string { return @""; } + (id)stringWithCharacters:(const unichar*)chars length:(unsigned int)length { return AUTORELEASE([[self alloc] initWithCharacters:chars length:length]); } + (id)stringWithCharactersNoCopy:(unichar*)chars length:(unsigned int)length freeWhenDone:(BOOL)flag { return AUTORELEASE([[self alloc] initWithCharactersNoCopy:chars length:length freeWhenDone:flag]); } + (id)stringWithString:(NSString*)aString { return [self stringWithCString:[aString cString]]; } + (id)stringWithCString:(const char*)byteString { return AUTORELEASE([[self alloc] initWithCString:byteString]); } + (id)stringWithUTF8String:(const char *)bytes { register unsigned char *p; for (p = (unsigned char *)bytes; *p; p++) { if (*p > 127) return AUTORELEASE([[self alloc] initWithUTF8String:bytes]); } return [self stringWithCString:bytes]; } + (NSString *)stringWithCString:(const char*)byteString length:(unsigned int)length { return AUTORELEASE([[self alloc] initWithCString:byteString length:length]); } + (NSString *)stringWithCStringNoCopy:(char*)byteString length:(unsigned int)length freeWhenDone:(BOOL)flag { return AUTORELEASE([[self alloc] initWithCStringNoCopy:byteString length:length freeWhenDone:flag]); } + (id)stringWithCStringNoCopy:(char*)byteString freeWhenDone:(BOOL)flag { return AUTORELEASE([[self alloc] initWithCStringNoCopy:byteString freeWhenDone:flag]); } + (id)stringWithFormat:(NSString*)format,... { va_list va; NSString* string; va_start(va, format); string = AUTORELEASE([[self alloc] initWithFormat:format arguments:va]); va_end(va); return string; } + (id)stringWithFormat:(NSString *)format arguments:(va_list)argList { return AUTORELEASE([[self alloc] initWithFormat:format arguments:argList]); } + (id)stringWithContentsOfFile:(NSString *)path { return AUTORELEASE([[self alloc] initWithContentsOfFile:path]); } + (id)stringWithContentsOfURL:(NSURL *)_url { return AUTORELEASE([[self alloc] initWithContentsOfURL:_url]); } @end /* NSString(NSStringCreation) */ @implementation NSString(GSAdditions) - (NSString *)stringWithoutPrefix:(NSString *)_prefix { return ([self hasPrefix:_prefix]) ? [self substringFromIndex:[_prefix length]] : (NSString *)[[self copy] autorelease]; } - (NSString *)stringWithoutSuffix:(NSString *)_suffix { return ([self hasSuffix:_suffix]) ? [self substringToIndex:([self length] - [_suffix length])] : (NSString *)[[self copy] autorelease]; } - (NSString *)stringByReplacingString:(NSString *)_orignal withString:(NSString *)_replacement { /* very slow solution .. */ if ([self indexOfString:_orignal] == NSNotFound) return [[self copy] autorelease]; return [[self componentsSeparatedByString:_orignal] componentsJoinedByString:_replacement]; } static NSCharacterSet *whiteSpaceSet = nil; static BOOL (*isWhiteSpaceSetMem)(id, SEL, unichar); - (NSString *)stringByTrimmingLeadWhiteSpaces { /* can't we share the implementation of lead and tail?! It is 98% similiar */ unsigned len; unichar *buf; unsigned idx; if (whiteSpaceSet == nil) { whiteSpaceSet = [[NSCharacterSet whitespaceAndNewlineCharacterSet] retain]; isWhiteSpaceSetMem = (void *) [whiteSpaceSet methodForSelector:@selector(characterIsMember:)]; } if ((len = [self length]) == 0) return @""; buf = calloc(len + 1, sizeof(unichar)); [self getCharacters:buf]; for (idx = 0; (idx < len) && isWhiteSpaceSetMem(whiteSpaceSet, @selector(characterIsMember:), buf[idx]); idx++) ; self = [NSStringClass stringWithCharacters:&(buf[idx]) length:(len - idx)]; free(buf); return self; } - (NSString *)stringByTrimmingTailWhiteSpaces { unichar *buf; unsigned int idx; unsigned len; if (whiteSpaceSet == nil) { whiteSpaceSet = [[NSCharacterSet whitespaceAndNewlineCharacterSet] retain]; isWhiteSpaceSetMem = (void *) [whiteSpaceSet methodForSelector:@selector(characterIsMember:)]; } if ((len = [self length]) == 0) return @""; buf = calloc(len + 2, sizeof(unichar)); [self getCharacters:buf]; for (idx = (len - 1); (idx >= 0) && isWhiteSpaceSetMem(whiteSpaceSet, @selector(characterIsMember:), buf[idx]); idx--) ; self = [NSStringClass stringWithCharacters:buf length:(idx + 1)]; free(buf); return self; } - (NSString *)stringByTrimmingLeadSpaces { unsigned int len; unsigned int idx; unichar *buf; if ((len = [self length]) == 0) return @""; buf = calloc(len + 2, sizeof(unichar)); [self getCharacters:buf]; for (idx = 0; (idx < len) && isspace(buf[idx]); idx++) ; self = [NSStringClass stringWithCharacters:&(buf[idx]) length:(len - idx)]; if (buf) free(buf); return self; } - (NSString *)stringByTrimmingTailSpaces { unsigned len; unichar *buf; int idx; if ((len = [self length]) == 0) return @""; buf = calloc(len + 2, sizeof(unichar)); [self getCharacters:buf]; for (idx = (len - 1); (idx >= 0) && isspace(buf[idx]); idx--) ; self = [NSStringClass stringWithCharacters:buf length:(idx + 1)]; if (buf) free(buf); return self; } - (NSString *)stringByTrimmingWhiteSpaces { return [[self stringByTrimmingTailWhiteSpaces] stringByTrimmingLeadWhiteSpaces]; } - (NSString *)stringByTrimmingSpaces { return [[self stringByTrimmingTailSpaces] stringByTrimmingLeadSpaces]; } @end /* NSString(GSAdditions) */ @implementation NSMutableString(GNUstepCompatibility) - (void)trimLeadSpaces { [self setString:[self stringByTrimmingLeadSpaces]]; } - (void)trimTailSpaces { [self setString:[self stringByTrimmingTailSpaces]]; } - (void)trimSpaces { [self setString:[self stringByTrimmingSpaces]]; } @end /* NSMutableString(GNUstepCompatibility) */ @implementation NSMutableString(NSStringCreation) + (id)allocWithZone:(NSZone *)zone { return (self == [NSMutableString class]) ? [NSMutableTemporaryString allocWithZone:zone] : (id)NSAllocateObject(self, 0, zone); } + (id)stringWithCapacity:(unsigned int)capacity { return AUTORELEASE([[self alloc] initWithCapacity:capacity]); } + (id)string { return [self stringWithCapacity:0]; } @end /* NSMutableString(NSStringCreation) */ /**************************** * Initializing strings ****************************/ @implementation NSMutableString(NSStringInitialization) - (id)initWithCapacity:(unsigned int)capacity { [self subclassResponsibility:_cmd]; return nil; } - (id)initWithCharacters:(const unichar *)chars length:(unsigned int)length { unsigned char *buffer; unsigned int i, j; BOOL isLower8BitEqual = NO; BOOL isLower7BitEqual = NO; unsigned int convertCount = 0; switch ([NSStringClass defaultCStringEncoding]) { case NSISOLatin1StringEncoding: isLower8BitEqual = YES; break; case NSASCIIStringEncoding: case NSUTF8StringEncoding: isLower7BitEqual = YES; break; } /* scan whether this is a non-8-bit- string ... */ for (i = 0; i < length; i++) { //NSMutableString *s; #if !HAS_UNICODE_STRING /* allow Euro char code (8364) */ if (chars[i] == 8364) { convertCount++; continue; } #endif if (isLower8BitEqual && (chars[i] < 256)) /* valid 8-bit character in default encoding */ continue; if (isLower7BitEqual && (chars[i] < 128)) /* valid 7-bit character in default encoding */ continue; #if HAS_UNICODE_STRING s = [[NSMutableUnicodeString alloc] initWithCharacters:chars length:length]; if (flag) { lfFree(chars); chars = NULL; } #endif return [self notImplemented:_cmd]; } buffer = MallocAtomic(length + (convertCount*3) + 1); for (i = 0, j = 0; i < length; i++, j++) { #if !HAS_UNICODE_STRING if (chars[i] == 8364) { buffer[j++] = 'E'; buffer[j++] = 'U'; buffer[j] = 'R'; } #endif buffer[j] = chars[i]; } buffer[j] = '\0'; self = [self initWithCString:(char *)buffer length:j]; if (buffer) lfFree(buffer); return self; } - (id)initWithCharactersNoCopy:(unichar*)chars length:(unsigned int)length freeWhenDone:(BOOL)flag { unsigned char *buffer; unsigned int i, j; BOOL isLower8BitEqual = NO; BOOL isLower7BitEqual = NO; unsigned int convertCount = 0; switch ([NSStringClass defaultCStringEncoding]) { case NSISOLatin1StringEncoding: isLower8BitEqual = YES; break; case NSASCIIStringEncoding: case NSUTF8StringEncoding: case NSISOLatin9StringEncoding: isLower7BitEqual = YES; break; } /* scan whether this is a non-8-bit- string ... */ for (i = 0; i < length; i++) { //NSMutableString *s; #if !HAS_UNICODE_STRING /* allow Euro char code (8364) */ if (chars[i] == 8364) { convertCount++; continue; } #endif if (isLower8BitEqual && (chars[i] < 256)) /* valid 8-bit character in default encoding */ continue; if (isLower7BitEqual && (chars[i] < 128)) /* valid 7-bit character in default encoding */ continue; #if HAS_UNICODE_STRING s = [[NSMutableUTF16String alloc] initWithCharacters:chars length:length]; if (flag) { lfFree(chars); chars = NULL; } #endif return [self notImplemented:_cmd]; } buffer = MallocAtomic(length + (convertCount*3) + 1); for (i = 0, j = 0; i < length; i++, j++) { #if !HAS_UNICODE_STRING if (chars[i] == 8364) { buffer[j++] = 'E'; buffer[j++] = 'U'; buffer[j] = 'R'; } #endif buffer[j] = chars[i]; } buffer[j] = '\0'; if (flag) { lfFree(chars); chars = NULL; } self = [self initWithCString:(char *)buffer length:j]; if (buffer) lfFree(buffer); return self; } - (id)initWithCString:(const char*)byteString { if (NSStringClass == Nil) NSStringClass = [NSString class]; return [self initWithString: AUTORELEASE([[NSStringClass alloc] initWithCStringNoCopy:(char*)byteString freeWhenDone:NO])]; } - (id)initWithCString:(const char*)byteString length:(unsigned int)length { if (NSStringClass == Nil) NSStringClass = [NSString class]; return [self initWithString: AUTORELEASE([[NSStringClass alloc] initWithCStringNoCopy:(char*)byteString length:length freeWhenDone:NO])]; } - (id)initWithCStringNoCopy:(char*)byteString freeWhenDone:(BOOL)flag { if (NSStringClass == Nil) NSStringClass = [NSString class]; return [self initWithString: AUTORELEASE([[NSStringClass alloc] initWithCStringNoCopy:byteString freeWhenDone:flag])]; } - (id)initWithCStringNoCopy:(char*)byteString length:(unsigned int)length freeWhenDone:(BOOL)flag { if (NSStringClass == Nil) NSStringClass = [NSString class]; return [self initWithString: AUTORELEASE([[NSStringClass alloc] initWithCStringNoCopy:byteString length:length freeWhenDone:flag])]; } - (id)initWithString:(NSString*)aString { [self subclassResponsibility:_cmd]; return nil; } - (id)initWithFormat:(NSString*)format, ... { id str; va_list va; va_start(va, format); str = [self initWithFormat:format arguments:va]; va_end(va); return str; } - (id)initWithFormat:(NSString*)format arguments:(va_list)argList { return [self initWithString:Avsprintf(format, argList)]; } - (id)initWithFormat:(NSString*)format locale:(NSDictionary*)dictionary, ... { id str; va_list va; va_start(va, dictionary); str = [self initWithFormat:format arguments:va]; va_end(va); return str; } - (id)initWithFormat:(NSString *)format locale:(NSDictionary*)dictionary arguments:(va_list)argList { return [self initWithFormat:format arguments:argList]; } - (id)initWithData:(NSData *)data encoding:(NSStringEncoding)encoding { /* NSMutableString */ return NSInitStringWithData(self, data, encoding); } - (id)initWithContentsOfFile:(NSString *)path { // UNICODE // ENCODINGS [self notImplemented:_cmd]; return nil; } @end /* NSMutableString(NSStringInitialization) */ /**************************** * Allocate concrete strings ****************************/ /* * Classes used for allocation of NSString concrete instances */ @implementation NSTemporaryString static BOOL isMultithreaded = NO; static NSTemporaryString *temporaryStringsPool = nil; static Class NSInline8BitStringClass = Nil; static Class NSShortInline8BitStringClass = Nil; + (void)initialize { static BOOL initialized = NO; if (!initialized) { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskNowMultiThreaded:) name:NSWillBecomeMultiThreadedNotification object:nil]; initialized = YES; } } + (void)taskNowMultiThreaded:notification { PrivateThreadData* threadData = [[NSThread currentThread] _privateThreadData]; [threadData setTemporaryStringsPool:temporaryStringsPool]; temporaryStringsPool = nil; isMultithreaded = YES; } + (id)allocWithZone:(NSZone*)zone { NSTemporaryString* obj = nil; PrivateThreadData* threadData = nil; if (isMultithreaded) { threadData = [[NSThread currentThread] _privateThreadData]; obj = [threadData temporaryString]; } else { if (temporaryStringsPool) { obj = temporaryStringsPool; temporaryStringsPool = temporaryStringsPool->next; obj->next = nil; } } if (!obj) obj = (id)NSAllocateObject(self, 0, zone); obj->_zone = zone; return obj; } - (void)dealloc { fprintf (stderr, "ERROR: NSTemporaryString %s to be deallocated!\n", isa->name); abort (); /* this is to please gcc 4.1 which otherwise issues a warning (and we don't know the -W option to disable it, let me know if you do ;-)*/ if (0) [super dealloc]; } static inline void _collectTemporary(NSTemporaryString *self) { if (isMultithreaded) { [[[NSThread currentThread] _privateThreadData] addTemporaryString:self]; } else { if (self != temporaryStringsPool) self->next = temporaryStringsPool; temporaryStringsPool = self; } } - (NSZone *)zone { return _zone; } /* * Methods that return strings */ - (id)init { id str = @""; _collectTemporary(self); return str; } - (id)initWithCharacters:(const unichar *)chars length:(unsigned int)length { unsigned char *buffer; register unsigned int i; BOOL isLower8BitEqual = NO; BOOL isLower7BitEqual = NO; if (length == 0) { _collectTemporary(self); return @""; } switch ([NSStringClass defaultCStringEncoding]) { case NSISOLatin1StringEncoding: isLower8BitEqual = YES; break; case NSASCIIStringEncoding: case NSUTF8StringEncoding: isLower7BitEqual = YES; break; } /* scan whether this is a non-8-bit- string ... */ for (i = 0; i < length; i++) { NSString *s; if (isLower8BitEqual && (chars[i] < 256)) /* valid 8-bit character in default encoding */ continue; if (isLower7BitEqual && (chars[i] < 128)) /* valid 7-bit character in default encoding */ continue; s = [[NSInlineUTF16String allocForCapacity:(length + 1) zone:NULL] initWithCharacters:chars length:length]; _collectTemporary(self); return s; } buffer = MallocAtomic(length + 1); for (i = 0; i < length; i++) buffer[i] = chars[i]; buffer[length] = '\0'; self = [self initWithCString:(char *)buffer length:length]; if (buffer) lfFree(buffer); return self; } - (id)initWithCharactersNoCopy:(unichar *)chars length:(unsigned int)length freeWhenDone:(BOOL)flag { unsigned char *buffer; register unsigned int i; BOOL isLower8BitEqual = NO; BOOL isLower7BitEqual = NO; if (length == 0) { _collectTemporary(self); if (flag && chars != NULL) { lfFree(chars); chars = NULL; } return @""; } switch ([NSStringClass defaultCStringEncoding]) { case NSISOLatin1StringEncoding: isLower8BitEqual = YES; break; case NSASCIIStringEncoding: case NSUTF8StringEncoding: isLower7BitEqual = YES; break; } /* scan whether this is a non-8-bit- string ... */ for (i = 0; i < length; i++) { NSString *s; #if 0 printf("chars[%d] %c %d\n", i, chars[i], chars[i]); #endif if (isLower8BitEqual && (chars[i] < 256)) /* valid 8-bit character in default encoding */ continue; if (isLower7BitEqual && (chars[i] < 128)) /* valid 7-bit character in default encoding */ continue; s = [[NSInlineUTF16String allocForCapacity:(length + 1) zone:NULL] initWithCharacters:chars length:length]; if (flag && (chars != NULL)) { lfFree(chars); chars = NULL; } _collectTemporary(self); #if 0 printf("%s: return s[%s]\n", __PRETTY_FUNCTION__, [[[s class] description] cString]); #endif return s; } buffer = MallocAtomic(length + 1); for (i = 0; i < length; i++) buffer[i] = chars[i]; buffer[length] = '\0'; if (flag && (chars != NULL)) { lfFree(chars); chars = NULL; } self = [self initWithCString:(char *)buffer length:length]; if (buffer != NULL) lfFree(buffer); return self; } - (id)initWithCString:(const char *)byteString { Class clazz; int length; id str; if (NSInline8BitStringClass == Nil) NSInline8BitStringClass = [NSInline8BitString class]; if (NSShortInline8BitStringClass == Nil) NSShortInline8BitStringClass = [NSShortInline8BitString class]; length = Strlen(byteString); if (length == 0) return @""; #if USE_SINGLE8BITSTRING if (length == 1) { if ((str = _getSingle8BitString(*(unsigned char *)byteString))) { _collectTemporary(self); return RETAIN(str); } } #endif clazz = length < 255 ? NSShortInline8BitStringClass : NSInline8BitStringClass; str = [[clazz allocForCapacity:length zone:_zone] initWithCString:byteString length:length]; _collectTemporary(self); return str; } - (id)initWithUTF8String:(const char *)byteString { NSData *d; d = byteString ? [NSData dataWithBytes:byteString length:strlen(byteString)] : nil; return [self initWithData:d encoding:NSUTF8StringEncoding]; } - (id)initWithBytes:(void *)_bytes length:(unsigned)_length encoding:(NSStringEncoding)_enc { NSData *d; d = _bytes ? [NSData dataWithBytes:_bytes length:_length] : nil; return [self initWithData:d encoding:_enc]; } - (id)initWithBytesNoCopy:(void *)_bytes length:(unsigned)_length encoding:(NSStringEncoding)_enc freeWhenDone:(BOOL)_fwd { NSData *d; d = (_bytes != nil) ? [[NSData alloc] initWithBytesNoCopy:_bytes length:_length freeWhenDone:_fwd] : nil; self = [self initWithData:d encoding:_enc]; [d release]; return self; } - (id)initWithCString:(const char *)byteString length:(unsigned int)length { Class clazz; id str; if (length == 0) { _collectTemporary(self); return @""; } #if USE_SINGLE8BITSTRING if (length == 1) { if ((str = _getSingle8BitString(*(unsigned char *)byteString))) { _collectTemporary(self); return RETAIN(str); } } #endif if (NSInline8BitStringClass == Nil) NSInline8BitStringClass = [NSInline8BitString class]; if (NSShortInline8BitStringClass == Nil) NSShortInline8BitStringClass = [NSShortInline8BitString class]; clazz = length < 255 ? NSShortInline8BitStringClass : NSInline8BitStringClass; str = [[clazz allocForCapacity:length zone:_zone] initWithCString:byteString length:length]; _collectTemporary(self); return str; } - (id)initWithCStringNoCopy:(char*)byteString freeWhenDone:(BOOL)flag { id str = flag ? [NSOwned8BitString allocWithZone:_zone] : [NSNonOwned8BitString allocWithZone:_zone]; str = [str initWithCString:byteString length:Strlen(byteString) copy:NO]; _collectTemporary(self); return str; } - (id)initWithCStringNoCopy:(char *)byteString length:(unsigned int)length freeWhenDone:(BOOL)flag { id str; str = flag ? [NSOwnedOpen8BitString allocWithZone:_zone] : [NSNonOwnedOpen8BitString allocWithZone:_zone]; str = [str initWithCString:byteString length:length copy:NO]; _collectTemporary(self); return str; } - (id)initWithString:(NSString *)aString { id str = [aString copyWithZone:_zone]; _collectTemporary(self); return str; } - (id)initWithFormat:(NSString *)format, ... { id str; va_list va; va_start(va, format); str = [self initWithFormat:format arguments:va]; va_end(va); return str; } - (id)initWithFormat:(NSString *)format arguments:(va_list)argList { id str = Avsprintf(format, argList); str = [str copyWithZone:_zone]; _collectTemporary(self); return str; } - (id)initWithFormat:(NSString*)format locale:(NSDictionary*)dictionary, ... { id str; va_list va; va_start(va, dictionary); str = [self initWithFormat:format arguments:va]; va_end(va); return str; } - (id)initWithFormat:(NSString*)format locale:(NSDictionary*)dictionary arguments:(va_list)argList { return [self initWithFormat:format arguments:argList]; } - (id)initWithData:(NSData *)data encoding:(NSStringEncoding)encoding { /* NSTemporaryString */ // UNICODE Class clazz; id str; unsigned len; len = [data length]; clazz = Nil; if (encoding == NSUnicodeStringEncoding) { /* scan for characters > 255 ... (everything below is covered by L1) */ const unichar *s; unsigned int i, slen; s = [data bytes]; slen = len / 2; for (i = 0; i < slen; i++) { if (s[i] > 255) { /* use wide string ... */ str = [NSInlineUTF16String allocForCapacity:slen zone:_zone]; str = [str initWithCharacters:s length:slen]; _collectTemporary(self); return str; } } } else if (encoding == NSISOLatin9StringEncoding) { const unsigned char *buf; unichar *newBuf; unsigned int i; NSString *str; buf = [data bytes]; newBuf = MallocAtomic((len + 1)* sizeof(unichar)); for (i = 0; i < len; i++) { /* convert Euro sign */ /* missing conversions for other latin9 signs */ if (buf[i] == 164) { newBuf[i] = 8364; } else { newBuf[i] = buf[i]; } } str = [NSInlineUTF16String allocForCapacity:len zone:[self zone]]; str = [str initWithCharacters:newBuf length:len]; lfFree(newBuf); _collectTemporary(self); return str; } else if (encoding == NSUTF8StringEncoding) { unichar *buf16; unsigned char *start, *end; unichar *start16, *end16; int result; buf16 = MallocAtomic((len + 1) * sizeof(unichar)); #if DEBUG NSCAssert(buf16, @"couldn't allocate proper buffer of len %i", len); #endif start = (void *)[data bytes]; /* should I allocate a buffer ?? */ end = start + len; start16 = &(buf16[0]); end16 = start16 + len; result = NSConvertUTF8toUTF16(&start, end, &start16, end16); if (result == 2) { /* target exhausted */ if (buf16) { lfFree(buf16); buf16 = NULL; } [NSException raise:@"UTFConversionException" format: @"couldn't convert UTF8 to UTF16, " @"target buffer is to small !"]; } else if (result == 1) { /* source exhausted */ if (buf16) { lfFree(buf16); buf16 = NULL; } [NSException raise:@"UTFConversionException" format: @"couldn't convert UTF8 to UTF16, " @"source buffer is to small " @"(probably invalid input) !, data: %@", data]; } else { /* length correct ? */ unsigned int i, slen; const unichar *s; s = buf16; slen = (start16 - buf16); for (i = 0; i < slen; i++) { if (s[i] > 255) { /* use wide string ... */ str = [NSInlineUTF16String allocForCapacity:slen zone:_zone]; str = [str initWithCharacters:s length:slen]; if (buf16) { lfFree(buf16); buf16 = NULL; } _collectTemporary(self); return str; } } if (buf16) { lfFree(buf16); buf16 = NULL; } } } if (clazz == Nil) { if (NSInline8BitStringClass == Nil) NSInline8BitStringClass = [NSInline8BitString class]; if (NSShortInline8BitStringClass == Nil) NSShortInline8BitStringClass = [NSShortInline8BitString class]; clazz = len < 255 ? NSShortInline8BitStringClass : NSInline8BitStringClass; } len = [data length]; str = [clazz allocForCapacity:len zone:_zone]; str = NSInitStringWithData(str, data, encoding); _collectTemporary(self); return str; } - (id)initWithContentsOfFile:(NSString *)_path { unsigned char *bytes = NULL; unsigned len; if ((bytes = NSReadContentsOfFile(_path, 1, &len))) { NSString *str; bytes[len] = '\0'; if (len >= 2) { BOOL isUnicode; enum NSByteOrder fbo; if (bytes[0] == 0xFF && bytes[1] == 0xFE) { isUnicode = YES; fbo = NS_LittleEndian; } else if (bytes[0] == 0xFE && bytes[1] == 0xFF) { isUnicode = YES; fbo = NS_BigEndian; } else isUnicode = NO; if (isUnicode) { unichar *chars; chars = (unichar *)&(bytes[2]); len = ((len - 2) / 2); if (fbo != NSHostByteOrder()) { unsigned i; /* IMPROVE THAT JUNK !!! */ for (i = 0; i < len; i++) { unsigned char buf[2]; unsigned char t; memcpy(buf, &(chars[i]), 2); t = buf[0]; buf[0] = buf[1]; buf[1] = t; memcpy(&(chars[i]), buf, 2); } } str = [self initWithCharacters:chars length:len]; if (bytes) free(bytes); return str; } } str = [[NSOwned8BitString allocWithZone:_zone] initWithCString:(char *)bytes length:len copy:NO]; _collectTemporary(self); return str; } else { _collectTemporary(self); return nil; } } - (id)initWithContentsOfURL:(NSURL *)_url { if ([_url isFileURL]) { return [self initWithContentsOfFile:[_url path]]; } else if (_url == nil) { _collectTemporary(self); return nil; } else { NSURLHandle *handle; NSData *data; NSString *mimeType; NSStringEncoding enc; handle = [_url URLHandleUsingCache:NO]; data = [handle resourceData]; mimeType = [[handle propertyForKey:@"content-type"] description]; if ([mimeType length] > 0) { unsigned idx; enc = [NSStringClass defaultCStringEncoding]; if ([mimeType hasPrefix:@"text/xml"]) enc = NSUTF8StringEncoding; if ((idx = [mimeType indexOfString:@"charset="]) != NSNotFound) { NSString *cs; cs = [mimeType substringFromIndex:(idx + 8)]; cs = [cs lowercaseString]; if ([cs hasPrefix:@"utf-8"]) enc = NSUTF8StringEncoding; else if ([cs hasPrefix:@"iso-8859-1"]) enc = NSISOLatin1StringEncoding; else if ([cs hasPrefix:@"utf-16"]) enc = NSUnicodeStringEncoding; else if ([cs hasPrefix:@"ascii"]) enc = NSASCIIStringEncoding; else NSLog(@"WARNING: cannot process content charset " @"'%@' of URL '%@'", cs, [_url absoluteString]); } } else enc = [NSStringClass defaultCStringEncoding]; return [self initWithData:data encoding:enc]; } } @end /* NSTemporaryString */ @implementation NSMutableTemporaryString static NSMutableTemporaryString *temporaryMutableStringsPool = nil; + (void)initialize { static BOOL initialized = NO; if (!initialized) { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskNowMultiThreaded:) name:NSWillBecomeMultiThreadedNotification object:nil]; initialized = YES; } } + (void)taskNowMultiThreaded:notification { PrivateThreadData* threadData = [[NSThread currentThread] _privateThreadData]; [threadData setTemporaryMutableStringsPool:temporaryMutableStringsPool]; temporaryMutableStringsPool = nil; isMultithreaded = YES; } + (id)allocWithZone:(NSZone*)zone { NSMutableTemporaryString *obj = nil; PrivateThreadData *threadData = nil; if (isMultithreaded) { threadData = [[NSThread currentThread] _privateThreadData]; obj = [threadData temporaryMutableString]; } else { if (temporaryMutableStringsPool) { obj = temporaryMutableStringsPool; temporaryMutableStringsPool = temporaryMutableStringsPool->next; obj->next = nil; } } if (!obj) obj = (id)NSAllocateObject(self, 0, zone); obj->_zone = zone; return obj; } - (void)dealloc { fprintf(stderr,"NSMutableTemporaryString %s to be deallocated!\n",isa->name); abort(); /* this is to please gcc 4.1 which otherwise issues a warning (and we don't know the -W option to disable it, let me know if you do ;-)*/ if (0) [super dealloc]; } static inline void _collectMutableTemporary(NSMutableTemporaryString *self) { if (isMultithreaded) { [[[NSThread currentThread] _privateThreadData] addTemporaryMutableString:self]; } else { if (self != temporaryMutableStringsPool) self->next = temporaryMutableStringsPool; temporaryMutableStringsPool = self; } } /* * Methods that return strings */ - (id)initWithCapacity:(unsigned int)capacity { id str = [[NSMutableSimple8BitString allocWithZone:_zone] initWithCapacity:capacity]; _collectMutableTemporary(self); return str; } - (id)init { id str = [[NSMutableSimple8BitString allocWithZone:_zone] init]; _collectMutableTemporary(self); return str; } - (id)initWithCString:(const char*)byteString { int length = Strlen(byteString); id str = [[NSMutableSimple8BitString allocWithZone:_zone] initWithCString:(char*)byteString length:length copy:YES]; _collectMutableTemporary(self); return str; } - (id)initWithCString:(const char*)byteString length:(unsigned int)length { id str = [[NSMutableSimple8BitString allocWithZone:_zone] initWithCString:(char*)byteString length:length copy:YES]; _collectMutableTemporary(self); return str; } - (id)initWithCStringNoCopy:(char*)byteString freeWhenDone:(BOOL)flag { id str = [NSMutableSimple8BitString allocWithZone:_zone]; str = [str initWithCString:byteString length:Strlen(byteString) copy:!flag]; _collectMutableTemporary(self); return str; } - (id)initWithCStringNoCopy:(char*)byteString length:(unsigned int)length freeWhenDone:(BOOL)flag { id str = [NSMutableSimple8BitString allocWithZone:_zone]; str = [str initWithCString:byteString length:Strlen(byteString) copy:!flag]; _collectMutableTemporary(self); return str; } - (id)initWithString:(NSString*)aString { id str = [[NSMutableSimple8BitString allocWithZone:_zone] initWithString:aString]; _collectMutableTemporary(self); return str; } - (id)initWithFormat:(NSString*)format, ... { id str; va_list va; va_start(va, format); str = [self initWithFormat:format arguments:va]; va_end(va); return str; } - (id)initWithFormat:(NSString*)format arguments:(va_list)argList { id str = [[NSMutableSimple8BitString allocWithZone:_zone] init]; [str appendFormat:format arguments:argList]; _collectMutableTemporary(self); return str; } - (id)initWithFormat:(NSString *)format locale:(NSDictionary *)dictionary, ... { id str; va_list va; va_start(va, dictionary); str = [self initWithFormat:format arguments:va]; va_end(va); return str; } - (id)initWithFormat:(NSString *)format locale:(NSDictionary *)dictionary arguments:(va_list)argList { return [self initWithFormat:format arguments:argList]; } - (id)initWithData:(NSData *)data encoding:(NSStringEncoding)encoding { /* NSMutableTemporaryString */ // UNICODE id str; str = [NSMutableSimple8BitString allocWithZone:_zone]; str = NSInitStringWithData(str, data, encoding); _collectMutableTemporary(self); return str; } - (id)initWithContentsOfFile:(NSString *)_path { unsigned char *bytes = NULL; unsigned len; if ((bytes = NSReadContentsOfFile(_path, 1, &len))) { NSMutableString *str; bytes[len] = '\0'; str = [[NSMutableSimple8BitString allocWithZone:_zone] initWithCString:(char *)bytes length:len copy:NO]; _collectMutableTemporary(self); return str; } else { _collectMutableTemporary(self); return nil; } } @end /* NSMutableTemporaryString */ #include /* * Used for forcing linking of this category */ static void __dummyNSStringfile () { extern void __dummyNSStringFilePathfile(); __dummyNSStringFilePathfile(); __dummyNSStringfile(); } /* Local Variables: c-basic-offset: 4 tab-width: 8 End: */