4 Copyright (C) 1995, 1996 Ovidiu Predescu and Mircea Oancea.
7 Author: Mircea Oancea <mircea@jupiter.elcom.pub.ro>
9 This file is part of libFoundation.
11 Permission to use, copy, modify, and distribute this software and its
12 documentation for any purpose and without fee is hereby granted, provided
13 that the above copyright notice appear in all copies and that both that
14 copyright notice and this permission notice appear in supporting
17 We disclaim all warranties with regard to this software, including all
18 implied warranties of merchantability and fitness, in no event shall
19 we be liable for any special, indirect or consequential damages or any
20 damages whatsoever resulting from loss of use, data or profits, whether in
21 an action of contract, negligence or other tortious action, arising out of
22 or in connection with the use or performance of this software.
29 #include <Foundation/common.h>
30 #include <Foundation/NSArray.h>
31 #include <Foundation/NSDictionary.h>
32 #include <Foundation/NSData.h>
33 #include <Foundation/NSCoder.h>
34 #include <Foundation/NSAutoreleasePool.h>
35 #include <Foundation/NSException.h>
36 #include <Foundation/exceptions/StringExceptions.h>
37 #include <Foundation/exceptions/GeneralExceptions.h>
39 #include <Foundation/NSCharacterSet.h>
40 #include <Foundation/NSString.h>
41 #include <Foundation/NSConcreteString.h>
43 #include <extensions/objc-runtime.h>
45 #define COLLECT_STRING_CLUSTER_STATISTICS 0
46 #define PERF_8BIT_USE_OPT_COMPARE 1
47 #define PERF_SHTIN_USE_OWN_HASH 1
48 #define PERF_SHTIN_USE_OWN_EQUAL 1
49 #define PERF_SHTIN_USE_OWN_GETCHARS 1
51 static Class NS8BitStringClass = Nil;
52 static Class NSMutable8BitStringClass = Nil;
53 static Class NSShtInline8BitStringClass = Nil;
54 static Class NSInline8BitStringClass = Nil;
55 static Class NSDataClass = Nil;
56 static Class NSStringClass = Nil;
58 #if COLLECT_STRING_CLUSTER_STATISTICS
59 static unsigned int NS8BitString_dealloc_count = 0;
60 static unsigned int NSInline8BitString_dealloc_count = 0;
61 static unsigned int NSInline8BitString_total_len = 0;
62 static unsigned int NSShortInline8BitString_dealloc_count = 0;
63 static unsigned int NSShortInline8BitString_total_len = 0;
64 static unsigned int NSNonOwned8BitString_dealloc_count = 0;
65 static unsigned int NSNonOwned8BitString_total_len = 0;
66 static unsigned int NSOwned8BitString_dealloc_count = 0;
67 static unsigned int NSOwned8BitString_total_len = 0;
68 static unsigned int NSNonOwnedOpen8BitString_dealloc_count = 0;
69 static unsigned int NSNonOwnedOpen8BitString_total_len = 0;
70 static unsigned int NSOwnedOpen8BitString_dealloc_count = 0;
71 static unsigned int NSOwnedOpen8BitString_total_len = 0;
72 static unsigned int NSRange8BitString_dealloc_count = 0;
73 static unsigned int NSRange8BitString_total_len = 0;
75 @implementation NSString(ClusterStatistics)
77 + (void)printStatistics
80 "NSString class cluster statistics:\n"
83 " NSInline8BitString: %d\n"
84 " NSShortInline8BitString: %d\n"
85 " NSNonOwned8BitString: %d\n"
86 " NSOwned8BitString: %d\n"
87 " NSOwnedOpen8BitString: %d\n"
88 " NSNonOwnedOpen8BitString: %d\n"
89 " NSRange8BitString: %d\n"
90 " avg len (dealloc statistics):\n"
92 " NSInline8BitString: %d\n"
93 " NSShortInline8BitString: %d\n"
94 " NSNonOwned8BitString: %d\n"
95 " NSOwned8BitString: %d\n"
96 " NSOwnedOpen8BitString: %d\n"
97 " NSNonOwnedOpen8BitString: %d\n"
98 " NSRange8BitString: %d\n"
100 NS8BitString_dealloc_count,
101 NSInline8BitString_dealloc_count,
102 NSShortInline8BitString_dealloc_count,
103 NSNonOwned8BitString_dealloc_count,
104 NSOwned8BitString_dealloc_count,
105 NSOwnedOpen8BitString_dealloc_count,
106 NSNonOwnedOpen8BitString_dealloc_count,
107 NSRange8BitString_dealloc_count,
108 NSInline8BitString_dealloc_count
109 ? NSInline8BitString_total_len / NSInline8BitString_dealloc_count
111 NSShortInline8BitString_dealloc_count
112 ? NSShortInline8BitString_total_len /
113 NSShortInline8BitString_dealloc_count
115 NSNonOwned8BitString_dealloc_count
116 ? NSNonOwned8BitString_total_len/NSNonOwned8BitString_dealloc_count
118 NSOwned8BitString_dealloc_count
119 ? NSOwned8BitString_total_len / NSOwned8BitString_dealloc_count
121 NSOwnedOpen8BitString_dealloc_count
122 ? NSOwnedOpen8BitString_total_len /
123 NSOwnedOpen8BitString_dealloc_count
125 NSNonOwnedOpen8BitString_dealloc_count
126 ? NSNonOwnedOpen8BitString_total_len /
127 NSNonOwnedOpen8BitString_dealloc_count
129 NSRange8BitString_dealloc_count
130 ? NSRange8BitString_total_len / NSRange8BitString_dealloc_count
134 - (void)printStatistics
136 [NSString printStatistics];
140 #endif /* COLLECT_STRING_CLUSTER_STATISTICS */
142 @implementation NS8BitString
146 NS8BitStringClass = [NS8BitString class];
147 NSMutable8BitStringClass = [NSMutable8BitStringClass class];
148 NSShtInline8BitStringClass = [NSShortInline8BitString class];
149 NSInline8BitStringClass = [NSInline8BitStringClass class];
150 NSDataClass = [NSData class];
151 NSStringClass = [NSString class];
154 #if COLLECT_STRING_CLUSTER_STATISTICS
157 NS8BitString_dealloc_count++;
162 /* Accessing characters */
164 - (void)getCharacters:(unichar *)buffer
166 register unsigned int i = 0, l;
167 register unsigned char *bytes;
169 if ((l = [self cStringLength]) == 0)
172 bytes = (unsigned char *)[self __compact8BitBytes];
173 for (i = 0; i < l; i++)
174 buffer[i] = (unichar)bytes[i];
176 - (void)getCharacters:(unichar *)buffer range:(NSRange)aRange
178 register unsigned int i = 0;
179 unsigned char *bytes;
181 if (aRange.location + aRange.length > [self cStringLength]) {
182 [[[IndexOutOfRangeException alloc]
183 initWithFormat:@"range (%d,%d) in string %x of length %d",
184 aRange.location, aRange.length, self, [self cStringLength]]
188 bytes = (unsigned char *)[self __compact8BitBytes];
189 for (i = 0; i < aRange.length; i++)
190 buffer[i] = bytes[i];
193 /* Dividing strings */
195 - (NSString *)substringWithRange:(NSRange)aRange
197 [self subclassResponsibility:_cmd];
201 /* Finding characters and substrings */
203 - (NSRange)rangeOfCharacterFromSet:(NSCharacterSet*)aSet
204 options:(unsigned int)mask range:(NSRange)aRange
206 // ENCODINGS - this code applies to the system's default encoding
209 IMP imp = [aSet methodForSelector:@selector(characterIsMember:)];
210 unsigned char *bytes = (unsigned char *)[self __compact8BitBytes];
212 if (NSMaxRange(aRange) > [self cStringLength]) {
213 [[[IndexOutOfRangeException alloc]
214 initWithFormat:@"range %@ not in string 0x%08x of length %d",
215 NSStringFromRange(aRange), self, [self cStringLength]]
219 if (mask & NSBackwardsSearch) {
220 for (i = aRange.length - 1; i >= aRange.location; i--) {
221 unichar c = bytes[i];
223 if ((*imp)(aSet, @selector(characterIsMember:), c) ||
224 ((mask & NSCaseInsensitiveSearch) &&
226 (*imp)(aSet, @selector(characterIsMember:), toupper(c))) ||
228 (*imp)(aSet, @selector(characterIsMember:), tolower(c))))
230 return NSMakeRange(i, 1);
235 unsigned max = NSMaxRange(aRange);
236 for (i = aRange.location; i < max; i++) {
237 unichar c = bytes[i];
239 if ((*imp)(aSet, @selector(characterIsMember:), c) ||
240 ((mask & NSCaseInsensitiveSearch) &&
242 (*imp)(aSet, @selector(characterIsMember:), toupper(c))) ||
244 (*imp)(aSet, @selector(characterIsMember:), tolower(c))))
246 return NSMakeRange(i, 1);
251 return NSMakeRange(NSNotFound, 0);
254 - (NSRange)rangeOfString:(NSString*)aString
255 options:(unsigned int)mask range:(NSRange)aRange
257 // ENCODINGS - this code applies to the system's default encoding
259 unsigned char *mbytes;
260 unsigned char *abytes;
263 if (![aString isKindOfClass:NS8BitStringClass] &&
264 ![aString isKindOfClass:NSMutable8BitStringClass])
265 return [super rangeOfString:aString options:mask range:aRange];
267 if ((aRange.location + aRange.length) > [self cStringLength]) {
268 [[[IndexOutOfRangeException alloc]
269 initWithFormat:@"range (%d,%d) in string %x of length %d",
270 aRange.location, aRange.length, self, [self cStringLength]]
274 mbytes = (unsigned char *)[self __compact8BitBytes] + aRange.location;
275 abytes = (unsigned char *)[(id)aString __compact8BitBytes];
276 a = [aString cStringLength];
278 if ((a == 0) || (aRange.length < a))
279 return NSMakeRange(0, 0);
281 if (mask & NSAnchoredSearch) {
282 range.location = aRange.location +
283 ((mask & NSBackwardsSearch) ? aRange.length - a : 0);
286 if ([self compare:aString options:mask range:range] == NSOrderedSame)
289 return NSMakeRange(0,0);
292 if (mask & NSBackwardsSearch) {
293 if (mask & NSCaseInsensitiveSearch) {
294 /* Backward case insensitive */
298 cf = islower(abytes[0]) ? toupper(abytes[0]) : abytes[0];
300 for (n = aRange.length-a; n >= 0; n--) {
302 islower(mbytes[n]) ? toupper(mbytes[n]) : mbytes[n];
303 unsigned char ca = cf;
308 for (i = 1; i < a; i++) {
309 cm = islower(mbytes[n+i]) ?
310 toupper(mbytes[n+i]) : mbytes[n+i];
311 ca = islower(abytes[i]) ? toupper(abytes[i]) : abytes[i];
316 range.location = aRange.location + n;
323 /* Backward case sensitive */
325 for (n = (aRange.length - a); n >= 0; n--) {
328 if (mbytes[n] != abytes[0])
330 for (i = 1; i < a; i++)
331 if (mbytes[n+i] != abytes[i])
334 range.location = aRange.location + n;
342 if (mask & NSCaseInsensitiveSearch) {
343 /* Forward case insensitive */
347 cf = islower(abytes[0]) ? toupper(abytes[0]) : abytes[0];
349 for (n = 0; n + a <= aRange.length; n++) {
350 unsigned char cm, ca;
353 cm = islower(mbytes[n]) ? toupper(mbytes[n]) : mbytes[n];
358 for (i = 1; i < a; i++) {
359 cm = islower(mbytes[n+i]) ?
360 toupper(mbytes[n+i]) : mbytes[n+i];
361 ca = islower(abytes[i]) ? toupper(abytes[i]) : abytes[i];
366 range.location = aRange.location + n;
373 /* Forward case sensitive */
376 for (n = 0; (n + a) <= aRange.length; n++) {
379 if (mbytes[n] != abytes[0])
381 for (i = 1; i < a; i++)
382 if (mbytes[n+i] != abytes[i])
385 range.location = aRange.location + n;
393 range.location = range.length = 0;
397 - (NSComparisonResult)compare:(NSString *)aString
398 options:(unsigned int)mask range:(NSRange)aRange
400 // ENCODINGS - this code applies to the system's default encoding
401 register unsigned char *mbytes;
402 register unsigned char *abytes;
403 unsigned int i, n, a;
405 #if PERF_8BIT_USE_OPT_COMPARE /* optimized */
406 register Class clazz;
408 if (aString == nil) /* TODO: hh: AFAIK nil is not allowed in Cocoa? */
409 return NSOrderedDescending;
410 else if (aString == self)
411 return NSOrderedSame;
413 for (clazz = *(id *)aString; clazz; clazz = class_get_super_class(clazz)) {
414 if (clazz == NS8BitStringClass || clazz == NSMutable8BitStringClass) {
420 return [super compare:aString options:mask range:aRange];
422 if (![aString isKindOfClass:NS8BitStringClass] &&
423 ![aString isKindOfClass:NSMutable8BitStringClass]) {
424 return [super compare:aString options:mask range:aRange];
428 if (aRange.location + aRange.length > [self cStringLength]) {
429 [[[IndexOutOfRangeException alloc]
430 initWithFormat:@"range (%d,%d) in string %x of length %d",
431 aRange.location, aRange.length, self, [self cStringLength]]
435 mbytes = (unsigned char *)[self __compact8BitBytes] + aRange.location;
436 abytes = (unsigned char *)[(id)aString __compact8BitBytes];
438 a = [aString cStringLength];
439 n = MIN(a, aRange.length);
441 if (mask & NSCaseInsensitiveSearch) {
442 for (i = 0; i < n; i++) {
443 register unsigned char cm =
444 islower(mbytes[i]) ? toupper(mbytes[i]):mbytes[i];
445 register unsigned char ca =
446 islower(abytes[i]) ? toupper(abytes[i]):abytes[i];
449 return NSOrderedAscending;
451 return NSOrderedDescending;
455 for (i = 0; i < n; i++) {
456 if (mbytes[i] < abytes[i])
457 return NSOrderedAscending;
458 if (mbytes[i] > abytes[i])
459 return NSOrderedDescending;
463 if (aRange.length < a)
464 return NSOrderedAscending;
465 if (aRange.length > a)
466 return NSOrderedDescending;
468 return NSOrderedSame;
473 static Class LastClass = Nil;
474 static unsigned char *(*compact)(id, SEL) = NULL;
475 static unsigned int (*cstrlen)(id, SEL) = NULL;
476 register unsigned char *bytes;
477 register unsigned hash = 0, hash2;
480 #if GNU_RUNTIME /* selector caching */
481 if (LastClass != *(id *)self) {
482 LastClass = *(id *)self;
483 compact = (void *)method_get_imp(class_get_instance_method(LastClass,
484 @selector(__compact8BitBytes)));
485 cstrlen = (void *)method_get_imp(class_get_instance_method(LastClass,
486 @selector(cStringLength)));
488 bytes = compact(self, NULL /* dangerous? */);
489 n = cstrlen(self, NULL /* dangerous? */);
491 bytes = [self __compact8BitBytes];
492 n = [self cStringLength];
495 for (i = 0; i < n; i++) {
497 // UNICODE - must use a for independent of composed characters
499 if ((hash2 = hash & 0xf0000000))
500 hash ^= (hash2 >> 24) ^ hash2;
506 /* Getting a shared prefix */
508 - (NSString *)commonPrefixWithString:(NSString*)aString
509 options:(unsigned int)mask
511 // ENCODINGS - this code applies to the system's default encoding
512 NSRange range = {0, 0};
513 unsigned char *mbytes;
514 unsigned char *abytes;
517 if (![aString isKindOfClass:NS8BitStringClass] &&
518 ![aString isKindOfClass:NSMutable8BitStringClass]) {
519 return [super commonPrefixWithString:aString options:mask];
522 mLen = [self cStringLength];
523 aLen = [aString length];
524 mbytes = (unsigned char *)[self __compact8BitBytes];
525 abytes = (unsigned char *)[(NS8BitString *)aString __compact8BitBytes];
527 for (i = 0; (i < mLen) && (i < aLen); i++) {
528 unsigned char c1 = mbytes[i];
529 unsigned char c2 = abytes[i];
531 if (mask & NSCaseInsensitiveSearch) {
540 return [self substringWithRange:range];
545 - (NSString *)capitalizedString
547 // ENCODINGS - this code applies to the system's default encoding
550 int length = [self cStringLength];
551 unsigned char* bytes = (unsigned char *)[self __compact8BitBytes];
552 unsigned char* chars = MallocAtomic(sizeof(unichar)*(length+1));
554 for (i = 0; i < length; i++) {
555 unsigned char c = bytes[i];
561 chars[i] = islower(c) ? toupper(c) : c;
565 chars[i] = isupper(c) ? tolower(c) : c;
569 return AUTORELEASE([[NSOwned8BitString alloc]
570 initWithCString:(char *)chars length:length
574 - (NSString *)lowercaseString
576 // ENCODINGS - this code applies to the system's default encoding
578 int length = [self cStringLength];
579 unsigned char *bytes = (unsigned char *)[self __compact8BitBytes];
580 unsigned char *chars = (unsigned char *)MallocAtomic(sizeof(unichar)*(length+1));
582 for (i = 0; i < length; i++) {
583 register unsigned char c = bytes[i];
584 chars[i] = isupper(c) ? tolower(c) : c;
588 return AUTORELEASE([[NSOwned8BitString alloc]
589 initWithCString:(char *)chars length:length copy:NO]);
592 - (NSString *)uppercaseString
594 // ENCODINGS - this code applies to the system's default encoding
596 int length = [self cStringLength];
597 unsigned char *bytes = (unsigned char *)[self __compact8BitBytes];
598 unsigned char *chars = (unsigned char *)MallocAtomic(sizeof(unichar)*(length+1));
600 for (i = 0; i < length; i++) {
601 register unsigned char c = bytes[i];
602 chars[i] = islower(c) ? toupper(c) : c;
607 return AUTORELEASE([[NSOwned8BitString alloc]
608 initWithCString:(char *)chars length:length copy:NO]);
611 /* Working with C strings */
613 - (void)getCString:(char *)buffer maxLength:(unsigned int)maxLength
614 range:(NSRange)aRange remainingRange:(NSRange*)leftoverRange
616 unsigned char* bytes = (unsigned char *)[self __compact8BitBytes];
617 unsigned int toMove = MIN(maxLength, aRange.length);
618 unsigned int cLength = [self cStringLength];
620 if (aRange.location + aRange.length > cLength) {
621 [[[IndexOutOfRangeException alloc]
622 initWithFormat:@"range (%d,%d) in string %x of length %d",
623 aRange.location, aRange.length, self, cLength] raise];
627 leftoverRange->location = aRange.location + toMove;
628 leftoverRange->length = cLength - leftoverRange->location;
630 memcpy(buffer, bytes + aRange.location, toMove);
631 if (toMove < maxLength)
632 buffer[toMove] = '\0';
635 - (BOOL)writeToFile:(NSString *)path atomically:(BOOL)flag
637 // UNICODE - remove this
639 data = [self dataUsingEncoding:[NSString defaultCStringEncoding]];
640 return writeToFile(path, data, flag);
643 - (Class)classForCoder
645 return NS8BitStringClass;
648 - (void)encodeWithCoder:(NSCoder*)aCoder
650 const unsigned char* bytes = (unsigned char *)[self __compact8BitBytes];
651 int length = [self cStringLength];
653 [aCoder encodeValueOfObjCType:@encode(int) at:&length];
654 [aCoder encodeArrayOfObjCType:@encode(char) count:length at:bytes];
657 - (id)initWithCoder:(NSCoder*)aDecoder
659 unsigned char *bytes;
662 RELEASE(self); self = nil;
664 [aDecoder decodeValueOfObjCType:@encode(int) at:&length];
665 bytes = MallocAtomic (length + 1);
666 [aDecoder decodeArrayOfObjCType:@encode(char) count:length at:bytes];
667 bytes[length] = '\0';
668 return [[NSOwned8BitString alloc]
669 initWithCString:(char *)bytes length:length copy:NO];
672 - (NSString *)stringRepresentation
674 const unsigned char *cString;
677 cString = (unsigned char *)[self __compact8BitBytes];
678 length = [self cStringLength];
680 if (cString == NULL) return @"\"\"";
681 if (length == 0) return @"\"\"";
682 if (cString[0] == '\0') return @"\"\"";
684 /* Check if the string can be parsed as a STRING token by the property list
685 parser. Otherwise we must enclose it in double quotes. */
686 if (lf_isPlistBreakChar(cString[0])) {
687 return lf_quoteString((char *)cString, length);
690 for(i = 1; i < length; i++) {
691 if (lf_isPlistBreakChar(cString[i]))
692 return lf_quoteString((char *)cString, length);
698 - (id)copyWithZone:(NSZone*)zone
700 register Class clazz;
703 if (NSShouldRetainWithZone(self, zone))
706 length = [self cStringLength];
709 ? NSShtInline8BitStringClass
710 : NSInline8BitStringClass;
712 return [[clazz allocForCapacity:length zone:zone]
713 initWithCString:[self __compact8BitBytes] length:length];
716 - (NSData *)dataUsingEncoding:(NSStringEncoding)encoding
717 allowLossyConversion:(BOOL)flag;
720 if (encoding == [NSStringClass defaultCStringEncoding]) {
722 unsigned char *buf = NULL;
724 len = [self cStringLength];
725 buf = NSZoneMalloc(NULL, sizeof(unsigned char) * len + 1);
726 [self getCString:(char *)buf];
728 return [NSDataClass dataWithBytesNoCopy:(char *)buf
729 length:strlen((char *)buf)];
731 if (encoding == NSASCIIStringEncoding) {
732 register unsigned len = [self cStringLength];
734 register unsigned char *buf;
736 buf = NSZoneMalloc(NULL, sizeof(char) * len + 1);
739 [self getCString:(char *)buf];
741 /* check for strict ASCII */
742 for (i = 0; i < len; i++)
743 if (buf[i] > 127) return nil;
745 return [NSDataClass dataWithBytesNoCopy:buf length:len];
748 return [super dataUsingEncoding:encoding allowLossyConversion:flag];
751 - (id)mutableCopyWithZone:(NSZone*)zone
753 return [[NSMutableSimple8BitString allocWithZone:zone]
754 initWithCString:[self __compact8BitBytes]
755 length:[self cStringLength] copy:YES];
758 @end /* NS8BitString */
761 * Null terminated CString containing characters inline
764 @implementation NSInline8BitString /* final */
766 + (id)allocForCapacity:(unsigned int)capacity zone:(NSZone *)zone
768 NSInline8BitString *str = (NSInline8BitString *)
769 NSAllocateObject(self, capacity, zone);
776 if (self->cLength != -1) {
777 [[[InvalidUseOfMethodException alloc] initWithFormat:
778 @"cannot send %s to non-mutable instance", sel_get_name(_cmd)]
782 self->cString[0] = 0;
786 - (id)initWithCString:(const char*)byteString length:(unsigned int)length
788 if (self->cLength != -1) {
789 [[[InvalidUseOfMethodException alloc] initWithFormat:
790 @"cannot send %s to non-mutable instance", sel_get_name(_cmd)]
793 self->cLength = length;
794 memcpy(self->cString, byteString, length);
795 self->cString[length] = 0;
798 - (id)initWithCharacters:(const unichar *)chars length:(unsigned int)length
800 /* it must be ensured that char values are below 256 by the cluster ! */
802 if (self->cLength != -1) {
803 [[[InvalidUseOfMethodException alloc] initWithFormat:
804 @"cannot send %s to non-mutable instance", sel_get_name(_cmd)]
808 for (i = 0; i < length; i++)
809 self->cString[i] = chars[i];
810 self->cString[i] = '\0';
815 #if COLLECT_STRING_CLUSTER_STATISTICS
818 NSInline8BitString_dealloc_count++;
819 NSInline8BitString_total_len += self->cLength == -1 ? 0 : self->cLength;
824 - (const char *)cString
826 return (const char *)self->cString;
829 - (unsigned int)cStringLength
831 return (self->cLength == -1) ? 0 : self->cLength;
834 - (unsigned int)length
836 return (self->cLength == -1) ? 0 : self->cLength;
839 - (unichar)characterAtIndex:(unsigned int)index
841 if (self->cLength == -1 || (int)index >= self->cLength) {
842 [[[IndexOutOfRangeException alloc]
843 initWithFormat:@"index %d out of range in string %x of length %d",
844 index, self, self->cLength] raise];
847 return self->cString[index];
850 - (NSString *)substringWithRange:(NSRange)aRange
852 if (aRange.location + aRange.length > (unsigned)self->cLength) {
853 [[[IndexOutOfRangeException alloc]
854 initWithFormat:@"range (%d,%d) in string %x of length %d",
855 aRange.location, aRange.length, self, cLength] raise];
857 if (aRange.length == 0)
860 return AUTORELEASE([[NSRange8BitString alloc]
862 bytes:((char *)self->cString + aRange.location)
863 length:aRange.length]);
866 - (char *)__compact8BitBytes
868 return (char *)self->cString;
873 register unsigned char *bytes;
874 register unsigned hash = 0, hash2;
877 bytes = self->cString;
878 n = (self->cLength == -1) ? 0 : self->cLength;
880 for (i = 0; i < n; i++) {
882 // UNICODE - must use a for independent of composed characters
884 if ((hash2 = hash & 0xf0000000))
885 hash ^= (hash2 >> 24) ^ hash2;
891 @end /* NSInline8BitString */
893 @implementation NSShortInline8BitString /* final */
895 + (id)allocForCapacity:(unsigned int)capacity zone:(NSZone*)zone
897 NSShortInline8BitString *str = (NSShortInline8BitString *)
898 NSAllocateObject(self, capacity, zone);
905 if (self->cLength != 255) {
906 [[[InvalidUseOfMethodException alloc] initWithFormat:
907 @"cannot send %s to non-mutable instance", sel_get_name(_cmd)]
911 self->cString[0] = 0;
915 - (id)initWithCString:(const char*)byteString length:(unsigned int)length
917 if (self->cLength != 255) {
918 [[[InvalidUseOfMethodException alloc] initWithFormat:
919 @"cannot send %s to non-mutable instance", sel_get_name(_cmd)]
922 self->cLength = length;
923 memcpy(self->cString, byteString, length);
924 self->cString[length] = 0;
927 - (id)initWithCharacters:(const unichar *)chars length:(unsigned int)length
929 /* it must be ensured that char values are below 256 by the cluster ! */
931 if (self->cLength != 255) {
932 [[[InvalidUseOfMethodException alloc] initWithFormat:
933 @"cannot send %s to non-mutable instance", sel_get_name(_cmd)]
937 for (i = 0; i < length; i++)
938 self->cString[i] = chars[i];
939 self->cString[i] = '\0';
944 #if COLLECT_STRING_CLUSTER_STATISTICS
947 NSShortInline8BitString_dealloc_count++;
948 NSShortInline8BitString_total_len +=
949 self->cLength == 255 ? 0 : self->cLength;
954 - (const char *)cString
956 return (const char *)self->cString;
959 - (unsigned int)cStringLength
961 return (self->cLength == 255) ? 0 : self->cLength;
964 - (unsigned int)length
966 return (self->cLength == 255) ? 0 : self->cLength;
969 - (unichar)characterAtIndex:(unsigned int)index
971 if ((self->cLength == 255) || (index >= self->cLength)) {
972 [[[IndexOutOfRangeException alloc]
973 initWithFormat:@"index %d out of range in string %x of length %d",
974 index, self, self->cLength] raise];
977 return self->cString[index];
980 #if PERF_SHTIN_USE_OWN_GETCHARS
981 - (void)getCharacters:(unichar *)buffer
983 register signed short i;
986 if ((i == 255) || (i == 0)) /* empty string */
989 for (i--; i >= 0; i--)
990 buffer[i] = (unichar)(self->cString[i]);
992 - (void)getCharacters:(unichar *)buffer range:(NSRange)aRange
994 register unsigned int i = 0, l;
997 l = i + aRange.length;
998 if (l > ((self->cLength == 255) ? 0 : self->cLength)) {
999 [[[IndexOutOfRangeException alloc]
1000 initWithFormat:@"range (%d,%d) in string %x of length %d",
1001 aRange.location, aRange.length, self, [self length]] raise];
1005 buffer[i] = (unichar)(self->cString[i]);
1009 - (NSString *)substringWithRange:(NSRange)aRange
1011 if (aRange.location + aRange.length > self->cLength)
1012 [[[IndexOutOfRangeException alloc]
1013 initWithFormat:@"range (%d,%d) in string %x of length %d",
1014 aRange.location, aRange.length, self, cLength] raise];
1016 if (aRange.length == 0)
1019 return AUTORELEASE([[NSRange8BitString alloc]
1021 bytes:((char *)self->cString + aRange.location)
1022 length:aRange.length]);
1025 - (char *)__compact8BitBytes
1027 return (char *)self->cString;
1030 - (NSComparisonResult)compare:(NSString *)aString
1031 options:(unsigned int)mask range:(NSRange)aRange
1033 // ENCODINGS - this code applies to the system's default encoding
1034 register unsigned char *mbytes, *abytes;
1035 register Class clazz;
1036 unsigned int i, n, a;
1038 #if 1 /* optimized */
1039 if (aString == nil) /* TODO: hh: AFAIK nil is not allowed in Cocoa? */
1040 return NSOrderedDescending;
1041 else if (aString == self)
1042 return NSOrderedSame;
1044 for (clazz = *(id *)aString; clazz; clazz = class_get_super_class(clazz)) {
1045 if (clazz == NS8BitStringClass || clazz == NSMutable8BitStringClass) {
1051 return [super compare:aString options:mask range:aRange];
1053 if (![aString isKindOfClass:NS8BitStringClass] &&
1054 ![aString isKindOfClass:NSMutable8BitStringClass]) {
1055 return [super compare:aString options:mask range:aRange];
1059 if ((aRange.location + aRange.length) >
1060 ((self->cLength == 255) ? 0 : self->cLength)) {
1061 [[[IndexOutOfRangeException alloc]
1062 initWithFormat:@"range (%d,%d) in string %x of length %d",
1063 aRange.location, aRange.length, self, [self cStringLength]]
1067 mbytes = self->cString + aRange.location;
1068 abytes = (unsigned char *)[(id)aString __compact8BitBytes];
1070 a = [aString cStringLength];
1071 n = MIN(a, aRange.length);
1073 if (mask & NSCaseInsensitiveSearch) {
1074 for (i = 0; i < n; i++) {
1075 register unsigned char cm =
1076 islower(mbytes[i]) ? toupper(mbytes[i]):mbytes[i];
1077 register unsigned char ca =
1078 islower(abytes[i]) ? toupper(abytes[i]):abytes[i];
1081 return NSOrderedAscending;
1083 return NSOrderedDescending;
1087 for (i = 0; i < n; i++) {
1088 if (mbytes[i] < abytes[i])
1089 return NSOrderedAscending;
1090 if (mbytes[i] > abytes[i])
1091 return NSOrderedDescending;
1095 if (aRange.length < a)
1096 return NSOrderedAscending;
1097 if (aRange.length > a)
1098 return NSOrderedDescending;
1100 return NSOrderedSame;
1103 #if PERF_SHTIN_USE_OWN_HASH
1107 according to Valgrind this takes 3.13% of the runtime, can this be
1108 further optimized without breaking dictionary performance due to
1111 register unsigned char *bytes;
1112 register unsigned hash = 0, hash2;
1115 bytes = self->cString;
1116 n = (self->cLength == 255) ? 0 : self->cLength;
1118 for (i = 0; i < n; i++) {
1120 // UNICODE - must use a for independent of composed characters
1122 if ((hash2 = hash & 0xf0000000))
1123 hash ^= (hash2 >> 24) ^ hash2;
1130 #if PERF_SHTIN_USE_OWN_EQUAL
1131 - (BOOL)isEqual:(id)aString
1133 register unsigned char *mbytes, *abytes;
1134 register Class clazz;
1135 unsigned int i, n, a;
1138 if (self == aString)
1140 else if (aString == nil)
1144 if (*(id *)aString == *(id *)self) { /* exactly the same class */
1145 i = 1; /* is NSString subclass */
1146 n = 1; /* is 8-bit string subclass */
1147 a = 1; /* is exactly the same string class */
1151 for (clazz=*(id *)aString; clazz; clazz=class_get_super_class(clazz)) {
1152 if (clazz==NS8BitStringClass || clazz==NSMutable8BitStringClass) {
1153 i = 1; // is NSString subclass
1154 n = 1; // is 8-bit string subclass
1157 if (clazz == NSStringClass) {
1158 i = 1; // is NSString subclass
1159 n = 0; // is not an 8-bit string subclass
1163 if (i == 0) // not a NSString subclass
1166 range.length = (self->cLength == 255) ? 0 : self->cLength;
1167 if (n == 0) { // is not an 8-bit string subclass, use compare
1169 return [self compare:aString options:0 range:range] == NSOrderedSame;
1172 /* other string is 8 bit */
1174 if (a == 1) { /* exact string class, do not call method */
1175 a = (((NSShortInline8BitString *)aString)->cLength == 255)
1177 : ((NSShortInline8BitString *)aString)->cLength;
1178 if (a != range.length)
1179 /* strings differ in length */
1182 else if ((a = [aString cStringLength]) != range.length)
1183 /* strings differ in length */
1188 mbytes = self->cString;
1189 abytes = (unsigned char *)[(id)aString __compact8BitBytes];
1191 /* using memcmp is probably faster than looping on our own */
1192 return memcmp(self->cString, abytes, a) == 0 ? YES : NO;
1196 @end /* NSShortInline8BitString */
1198 @implementation NSCharacter8BitString /* final */
1207 - (id)initWithCString:(const char*)byteString length:(unsigned int)_length
1210 [[[InvalidUseOfMethodException alloc] initWithFormat:
1211 @"%@ can only handle a singe char", [self class]] raise];
1213 self->c[0] = byteString[0];
1218 - (id)initWithCharacters:(const unichar *)chars length:(unsigned int)_length
1221 [[[InvalidUseOfMethodException alloc] initWithFormat:
1222 @"%@ can only handle a singe char", [self class]] raise];
1224 self->c[0] = (char)chars[0];
1230 - (const char *)cString
1232 return (const char *)&(self->c[0]);
1235 - (unsigned int)cStringLength
1240 - (unsigned int)length
1245 - (unichar)characterAtIndex:(unsigned int)index
1248 [[[IndexOutOfRangeException alloc]
1249 initWithFormat:@"index %d out of range in string %x of length 1",
1250 index, self] raise];
1256 - (NSString *)substringWithRange:(NSRange)aRange
1258 if (aRange.location == 0 && aRange.length == 1)
1260 if (aRange.length == 0)
1263 [[[IndexOutOfRangeException alloc]
1264 initWithFormat:@"range (%d,%d) in string %x of length 1",
1265 aRange.location, aRange.length, self] raise];
1269 - (char *)__compact8BitBytes
1271 return (char *)&(self->c[0]);
1276 register unsigned hash = 0, hash2;
1279 // UNICODE - must use a for independent of composed characters
1281 if ((hash2 = hash & 0xf0000000))
1282 hash ^= (hash2 >> 24) ^ hash2;
1287 @end /* NSCharacter8BitString */
1291 * String containing non-owned, zero termintated c-string
1294 @implementation NSNonOwned8BitString
1298 if (self->cLength || self->cString) {
1299 [[[InvalidUseOfMethodException alloc] initWithFormat:
1300 @"cannot send %s to non-mutable instance", sel_get_name(_cmd)]
1304 self->cString = (unsigned char *)"";
1308 - (id)initWithCString:(char *)byteString
1309 length:(unsigned int)length copy:(BOOL)flag
1311 if (self->cLength || self->cString) {
1312 [[[InvalidUseOfMethodException alloc] initWithFormat:
1313 @"cannot send %s to non-mutable instance", sel_get_name(_cmd)]
1317 [[[InvalidUseOfMethodException alloc] initWithFormat:
1318 @"cannot send %s with flag set to YES to %@ to instance",
1319 sel_get_name(_cmd), NSStringFromClass([self class])] raise];
1322 cString = (unsigned char *)byteString;
1326 #if COLLECT_STRING_CLUSTER_STATISTICS
1329 NSNonOwned8BitString_dealloc_count++;
1330 NSNonOwned8BitString_total_len += self->cLength;
1335 - (const char *)cString
1337 return (const char *)self->cString;
1340 - (unsigned int)cStringLength
1342 return self->cLength;
1345 - (unsigned int)length
1347 return self->cLength;
1350 - (unichar)characterAtIndex:(unsigned int)index
1352 if ((int)self->cLength == -1 || (index >= (unsigned)self->cLength)) {
1353 [[[IndexOutOfRangeException alloc]
1354 initWithFormat:@"index %d out of range in string %x of length %d",
1355 index, self, cLength] raise];
1358 return self->cString[index];
1361 - (NSString *)substringWithRange:(NSRange)aRange
1363 if (aRange.location + aRange.length > cLength) {
1364 [[[IndexOutOfRangeException alloc]
1365 initWithFormat:@"range (%d,%d) in string %x of length %d",
1366 aRange.location, aRange.length, self, cLength] raise];
1369 if (aRange.length == 0)
1372 return AUTORELEASE([[NSRange8BitString alloc]
1374 bytes:(char *)cString + aRange.location
1375 length:aRange.length]);
1378 - (char *)__compact8BitBytes
1380 return (char *)self->cString;
1383 @end /* NSNonOwned8BitString */
1385 @implementation NSOwned8BitString
1389 if (self->cLength || self->cString) {
1390 [[[InvalidUseOfMethodException alloc] initWithFormat:
1391 @"cannot send %s to non-mutable instance", sel_get_name(_cmd)]
1396 self->cString = NSZoneMallocAtomic([self zone], sizeof(char));
1397 self->cString[0] = 0;
1401 - (id)initWithCString:(char*)byteString
1402 length:(unsigned int)length
1405 if (self->cLength != 0 || self->cString != NULL) {
1406 [[[InvalidUseOfMethodException alloc] initWithFormat:
1407 @"cannot send %s to non-mutable instance", sel_get_name(_cmd)]
1411 self->cLength = length;
1413 self->cString = NSZoneMallocAtomic([self zone], sizeof(char)*(length+1));
1414 memcpy(self->cString, byteString, length);
1415 self->cString[self->cLength] = 0;
1418 self->cString = (unsigned char *)byteString;
1424 #if COLLECT_STRING_CLUSTER_STATISTICS
1425 NSOwned8BitString_dealloc_count++;
1426 NSOwned8BitString_total_len += self->cLength;
1428 lfFree(self->cString);
1432 @end /* NSOwned8BitString */
1435 #define USE_LIB_FOUNDATION_CUSTOM_CONSTSTR 1
1436 #define USE_LIB_FOUNDATION_CONSTSTR 1
1438 #if USE_LIB_FOUNDATION_CONSTSTR
1440 #if USE_LIB_FOUNDATION_CUSTOM_CONSTSTR
1441 # define NXConstantString NSConstantString
1444 /* this requires that we use the special libFoundation libobjc */
1445 @implementation NXConstantString
1447 - (oneway void)release
1456 - (unsigned)retainCount
1468 [self shouldNotImplement:_cmd];
1470 /* this is to please gcc 4.1 which otherwise issues a warning (and we
1471 don't know the -W option to disable it, let me know if you do ;-)*/
1472 if (0) [super dealloc];
1475 @end /* NXConstantString */
1477 #if USE_LIB_FOUNDATION_CUSTOM_CONSTSTR
1478 # undef NXConstantString
1483 #warning NOT USING BUILTIN NXConstantString
1485 @interface DummyNXConstantString : NSNonOwned8BitString
1488 @implementation DummyNXConstantString
1489 - (oneway void)release
1498 - (unsigned)retainCount
1510 [self shouldNotImplement:_cmd];
1515 static BOOL didLoad = NO;
1518 printf("LOAD DUMMY\n");
1522 Class constantStringClass;
1523 Class constantStringMetaClass;
1527 metaClass = ((DummyNXConstantString*)self)->isa;
1528 constantStringClass = objc_lookup_class ("NXConstantString");
1529 constantStringMetaClass = constantStringClass->class_pointer;
1531 memcpy(constantStringClass, self, sizeof (struct objc_class));
1532 memcpy(constantStringMetaClass, metaClass, sizeof (struct objc_class));
1534 constantStringClass->name
1535 = constantStringMetaClass->name
1536 = "NXConstantString";
1539 Note: this doesn't work for dynamically loaded NSString categories,
1540 that is categories either contained in bundles OR in libraries
1541 which are loaded on-demand in case a bundle is loaded.
1543 class_add_behavior(constantStringClass, self);
1547 @end /* DummyNXConstantString */
1550 @implementation NSNonOwnedOpen8BitString
1552 #if COLLECT_STRING_CLUSTER_STATISTICS
1555 NSNonOwnedOpen8BitString_dealloc_count++;
1556 NSNonOwnedOpen8BitString_total_len += self->cLength;
1561 - (const char *)cString
1565 str = NSZoneMallocAtomic([self zone], sizeof(char)*(self->cLength + 1));
1566 memcpy(str, self->cString, self->cLength);
1568 #if !LIB_FOUNDATION_BOEHM_GC
1569 [NSAutoreleasedPointer autoreleasePointer:str];
1571 return (const char *)str;
1574 @end /* NSNonOwnedOpen8BitString */
1576 @implementation NSOwnedOpen8BitString /* final */
1578 #if COLLECT_STRING_CLUSTER_STATISTICS
1581 NSOwnedOpen8BitString_dealloc_count++;
1582 NSOwnedOpen8BitString_total_len += self->cLength;
1587 - (id)initWithCString:(char *)byteString
1588 length:(unsigned int)length copy:(BOOL)flag
1590 if (self->cLength || self->cString) {
1591 [[[InvalidUseOfMethodException alloc] initWithFormat:
1592 @"cannot send %s to non-mutable instance", sel_get_name(_cmd)]
1595 if (length != 0 && byteString == NULL) {
1596 [[[InvalidUseOfMethodException alloc] initWithFormat:
1597 @"passed NULL cstring to %s with a non-null length (%i)!",
1598 sel_get_name(_cmd), length]
1602 if (strlen(byteString) < length) {
1603 printf("LENGTH DIFFERS: %ld vs %d\n",
1604 (unsigned long)strlen(byteString), length);
1608 self->cLength = length;
1610 /* TODO: this is not tracked in dealloc? */
1612 NSZoneMallocAtomic([self zone], sizeof(char)*(length + 1));
1613 memcpy(cString, byteString, length);
1614 cString[length] = 0;
1617 self->cString = (unsigned char *)byteString;
1621 - (const char *)cString
1623 unsigned char *str = MallocAtomic(sizeof(char)*(self->cLength + 1));
1625 memcpy(str, self->cString, self->cLength);
1626 str[self->cLength] = 0;
1627 #if !LIB_FOUNDATION_BOEHM_GC
1628 [NSAutoreleasedPointer autoreleasePointer:str];
1630 return (const char *)str;
1633 @end /* NSOwnedOpen8BitString */
1636 @implementation NSRange8BitString /* final */
1638 - (id)initWithString:(NSString *)aParent
1639 bytes:(char *)bytes length:(unsigned int)length
1641 if (self->cLength != 0 || self->cString != NULL) {
1642 [[[InvalidUseOfMethodException alloc] initWithFormat:
1643 @"cannot send %s to non-mutable instance", sel_get_name(_cmd)]
1647 self->cString = (unsigned char *)bytes;
1648 self->cLength = length;
1649 self->parent = RETAIN(aParent);
1655 #if COLLECT_STRING_CLUSTER_STATISTICS
1656 NSRange8BitString_dealloc_count++;
1657 NSRange8BitString_total_len += self->cLength;
1659 RELEASE(self->parent);
1663 - (NSString *)substringWithRange:(NSRange)aRange
1665 if (aRange.location + aRange.length > cLength) {
1666 [[[IndexOutOfRangeException alloc]
1667 initWithFormat:@"range (%d,%d) in string %x of length %d",
1668 aRange.location, aRange.length, self, cLength] raise];
1671 if (aRange.length == 0)
1674 return AUTORELEASE([[NSRange8BitString alloc]
1675 initWithString:parent
1676 bytes:((char *)cString + aRange.location)
1677 length:aRange.length]);
1682 register unsigned char *bytes;
1683 register unsigned hash = 0, hash2;
1686 bytes = self->cString;
1689 for (i = 0; i < n; i++) {
1691 // UNICODE - must use a for independent of composed characters
1693 if ((hash2 = hash & 0xf0000000))
1694 hash ^= (hash2 >> 24) ^ hash2;
1700 @end /* NSRange8BitString */