|
|
1.1 root 1: /*
2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7: * Reserved. This file contains Original Code and/or Modifications of
8: * Original Code as defined in and that are subject to the Apple Public
9: * Source License Version 1.0 (the 'License'). You may not use this file
10: * except in compliance with the License. Please obtain a copy of the
11: * License at http://www.apple.com/publicsource and read it before using
12: * this file.
13: *
14: * The Original Code and all software distributed under the License are
15: * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19: * License for the specific language governing rights and limitations
20: * under the License."
21: *
22: * @APPLE_LICENSE_HEADER_END@
23: */
24: /*
25: NXString.m
26: Copyright 1991 NeXT, Inc.
27: Responsibility: Ali Ozer
28: */
29:
30: #ifndef KERNEL
31: #ifdef SHLIB
32: #import "shlib.h"
33: #endif SHLIB
34:
35: #import <mach/mach.h>
36:
37: #import "NXStringPrivate.h"
38: #import <streams/streams.h>
39: #import <streams/streamsextra.h>
40: #import <stdio.h>
41: #import "error.h"
42:
43: /* Separate zone for ref counted strings... */
44: static NXZone *theStringZone = NULL;
45:
46: NXZone *_NXStringZone (void)
47: {
48: return theStringZone;
49: }
50:
51: static void objectPrintfProc(NXStream *stream, void *item, void *procData)
52: {
53: if ([(id)item respondsTo:@selector(writeToStream:)]) {
54: [(id)item writeToStream:stream];
55: } else {
56: NXPutc(stream, '%'); NXPutc(stream, '@');
57: }
58: }
59:
60: void _NXStringErrorRaise (int errorCode, const char *errorMsg)
61: {
62: extern void _NXLogError(const char *format, ...);
63: _NXLogError( "NXString error %d: %s", errorCode, errorMsg);
64: NX_RAISE (errorCode, errorMsg, 0);
65: }
66:
67:
68: @implementation NXString
69:
70: + initialize
71: {
72: if (theStringZone == NULL) {
73: #if 1
74: /* Using a separate string zone seems to hurt more than it helps now.
75: ??? However, we need to solve the copyFromZone: problem... */
76: theStringZone = NXDefaultMallocZone();
77: #else
78: theStringZone = NXCreateZone(vm_page_size, vm_page_size, YES);
79: NXNameZone (theStringZone, "String Zone");
80: #endif
81: NXRegisterPrintfProc('@', objectPrintfProc, NULL);
82: }
83: return self;
84: }
85:
86: - (unichar *)allocateCharacterBuffer:(unsigned)nChars
87: {
88: return NX_CHARALLOC([self zone], nChars);
89: }
90:
91: - initFromCharactersNoCopy:(unichar *)chars length:(unsigned)len
92: {
93: return [super init];
94: }
95:
96: - init
97: {
98: return [self initFromCharactersNoCopy:NULL length:0];
99: }
100:
101: - initFromCharacters:(const unichar *)chars length:(unsigned)len;
102: {
103: unichar *buffer = [self allocateCharacterBuffer:len];
104: NX_CHARCOPY (chars, buffer, len);
105: return [self initFromCharactersNoCopy:buffer length:len];
106: }
107:
108: - initFromString:string
109: {
110: NXRange range = {0, [string length]};
111: return [self initFromString:string range:range];
112: }
113:
114: - initFromString:string range:(NXRange)range
115: {
116: unichar *buffer = [self allocateCharacterBuffer:RNGLEN(range)];
117: [string getCharacters:buffer range:range];
118: return [self initFromCharactersNoCopy:buffer length:RNGLEN(range)];
119: }
120:
121: // The starting size for the buffer when reading from a stream, if no other clues about the desired size
122: // or the stream size are provided...
123:
124: #define INITIALLENGTH 16
125:
126: - initFromStream:(NXStream *)stream untilOneOf:(NXCharacterSet *)set maxLength:(unsigned int)maxLen
127: {
128: #if CHARS_ARE_EIGHT_BIT
129: return [self initFromCStringStream:stream untilOneOf:set maxLength:maxLen];
130: #else
131: #warning initFromStream:untilOneOf:maxLength: not implemented for Unicode
132: _NXStringErrorRaise (NXStringInternalError, "initFromStream:untilOneOf:maxLength: not implemented for Unicode");
133: return nil;
134: #endif
135: }
136:
137: - initFromCStringStream:(NXStream *)stream untilOneOf:(NXCharacterSet *)set maxLength:(unsigned int)maxLen
138: {
139: volatile unsigned int readLen = 0;
140: volatile unsigned int charsLen = MIN(maxLen, INITIALLENGTH);
141: unichar *volatile chars = NULL;
142: int chRead;
143:
144: if (stream->flags & NX_CANSEEK) { // Try to fine tune the initial buffer size
145:
146: long curLoc = NXTell(stream);
147: if (!set) { // If no set, then length is determined by maxLen or stream size
148: NXSeek (stream, 0, NX_FROMEND);
149: charsLen = (NXTell(stream) - curLoc);
150: if (charsLen > maxLen) charsLen = maxLen;
151: } else { // Otherwise we have to look at every character to determine how many we'll read
152: charsLen = 0;
153: while (!NXAtEOS(stream) && (charsLen < maxLen) && ![set characterIsMember:(unichar)NXGetc(stream)]) charsLen++;
154: }
155: NXSeek (stream, curLoc, NX_FROMSTART);
156:
157: }
158:
159: if ((chars = [self allocateCharacterBuffer:charsLen])) {
160:
161: NX_DURING
162:
163: // Seems like AtEOS is set after we attempt to read the last character.
164: // ??? Thus the !NXAtEOS is pretty useless down here...
165:
166: while (!NXAtEOS(stream) && (readLen < maxLen) && ((chRead = NXGetc(stream)) != EOF)) {
167: unichar ch = (unichar)chRead;
168: if (set && [set characterIsMember:ch]) {
169: NXUngetc(stream);
170: break;
171: }
172: if (charsLen == readLen) {
173: unichar *newChars;
174: if ((charsLen *= 2) > maxLen) charsLen = maxLen;
175: newChars = [self allocateCharacterBuffer:charsLen];
176: NX_CHARCOPY ((unichar *)chars, newChars, readLen);
177: free ((unichar *)chars);
178: chars = newChars;
179: }
180: chars[readLen++] = ch;
181: }
182:
183: NX_HANDLER
184:
185: free ((unichar *)chars);
186: NX_RERAISE ();
187:
188: NX_ENDHANDLER
189:
190: }
191:
192: return [self initFromCharactersNoCopy:(unichar *)chars length:readLen];
193: }
194:
195: - initFromFormat:(NXString *)format, ...
196: {
197: va_list argList;
198:
199: va_start (argList, format);
200: self = [self initFromFormat:format withArgList:argList];
201: va_end (argList);
202:
203: return self;
204: }
205:
206: - initFromFormat:(NXString *)format withArgList:(va_list)argList
207: {
208: int maxLen, len;
209: unsigned formatLen = [format cStringLength] + 1; // Including terminating zero
210: char *bytes;
211: NXStream *stream = NXOpenSmallMemory(NX_WRITEONLY);
212: char tmpBuf[MAXTMPBUFFERLEN], *formatChars;
213:
214: formatChars = (formatLen > MAXTMPBUFFERLEN) ? NXZoneMalloc(NXDefaultMallocZone(), formatLen * sizeof(char)) : tmpBuf;
215: [format getCString:formatChars];
216: NXVPrintf(stream, formatChars, argList);
217: NXFlush(stream);
218: NXGetMemoryBuffer(stream, &bytes, &len, &maxLen);
219: self = [self initFromCString:bytes length:len];
220: NXCloseMemory(stream, NX_FREEBUFFER);
221: if (formatChars != tmpBuf) free(formatChars);
222:
223: return self;
224: }
225:
226:
227: /* Byte oriented methods */
228:
229: - initFromCString:(const char *)bytes
230: {
231: return [self initFromCString:bytes length:strlen(bytes)];
232: }
233:
234: - initFromCString:(const char *)bytes length:(unsigned)length
235: {
236: unichar *buffer = NULL;
237: unsigned cnt;
238:
239: if (length && bytes && *bytes) {
240: buffer = [self allocateCharacterBuffer:length];
241: for (cnt = 0; cnt < length; cnt++) {
242: buffer[cnt] = bytes[cnt];
243: }
244: }
245: return [self initFromCharactersNoCopy:buffer length:length];
246: }
247:
248: - (void)getCString:(char *)buffer
249: {
250: [self getCString:buffer maxLength:NX_MAX_STRING_LENGTH range:(NXRange){0, [self length]} remainingRange:NULL];
251: }
252:
253: - (void)getCString:(char *)buffer maxLength:(unsigned)bytes
254: {
255: [self getCString:buffer maxLength:bytes range:(NXRange){0, [self length]} remainingRange:NULL];
256: }
257:
258: - (void)getCString:(char *)buffer maxLength:(unsigned)bytes range:(NXRange)range remainingRange:(NXRange *)leftover
259: {
260: #if CHARS_ARE_EIGHT_BIT
261: NXRange desiredRange = range;
262:
263: if (RNGLEN(range) > bytes) RNGLEN(range) = bytes;
264: [self getCharacters:(unichar *)buffer range:range];
265: buffer[RNGLEN(range)] = 0;
266:
267: if (leftover) {
268: RNGLOC(*leftover) = RNGLOC(desiredRange) + RNGLEN(range);
269: RNGLEN(*leftover) = RNGLEN(desiredRange) - RNGLEN(range);
270: }
271: #else
272: #warning getCString:maxLength:range:remainingRange: not implemented for Unicode
273: _NXStringErrorRaise (NXStringInternalError, "getCString:maxLength:range:remainingRange: not implemented for Unicode");
274: #if 0
275: NXRange processedRange;
276: unichar tmpBuf[MAXTMPBUFFERLEN];
277: unsigned uCnt = 0, cCnt = 0;
278:
279: if (RNGLOC(range) + RNGLEN(range) > [self length]) BOUNDSERROR;
280:
281: processedRange = range;
282: if (RNGLEN(processedRange) > MAXTMPBUFFERLEN) RNGLEN(processedRange) = MAXTMPBUFFERLEN;
283:
284: [self getCharacters:tmpBuf range:range];
285:
286: while (uCnt < RNGLEN(range)) {
287: unichar ch = uStr[RNGLOC(range) + uCnt];
288: if (cCnt < bytes) { // ??? This check will have to be smarter
289: buffer[cCnt] = (ch > 0x0ff) ? NX_BADBYTE : ch;
290: } else {
291: break;
292: }
293: uCnt++;
294: cCnt++;
295: }
296: buffer[cCnt] = 0;
297: #endif
298: #endif
299: }
300:
301: - (unsigned)length
302: {
303: [self subclassResponsibility:_cmd];
304: return 0;
305: }
306:
307: - (unsigned)cStringLength
308: {
309: #if CHARS_ARE_EIGHT_BIT
310: return [self length];
311: #else
312: #warning cStringLength not implemented for Unicode
313: _NXStringErrorRaise (NXStringInternalError, "cStringLength not implemented for Unicode");
314: return 0;
315: #endif
316: }
317:
318: - (char *)cStringCopy
319: {
320: char *str = NX_BYTEALLOC (NXDefaultMallocZone(), [self cStringLength] + 1);
321: [self getCString:str];
322: return str;
323: }
324:
325: - (NXAtom)uniqueCStringCopy
326: {
327: char tmpBuf[MAXTMPBUFFERLEN], *str;
328: NXAtom unique;
329: unsigned int len = [self cStringLength] + 1;
330:
331: str = (len > MAXTMPBUFFERLEN) ? NX_BYTEALLOC(NXDefaultMallocZone(), len) : tmpBuf;
332: [self getCString:str];
333: unique = NXUniqueString (str);
334: if (str != tmpBuf) free (str);
335:
336: return unique;
337: }
338:
339: - (unichar)characterAt:(unsigned)pos
340: {
341: [self subclassResponsibility:_cmd];
342: return 0;
343: }
344:
345: - (void)getCharacters:(unichar *)buffer range:(NXRange)range
346: {
347: unsigned int cnt;
348:
349: for (cnt = 0; cnt < RNGLEN(range); cnt++) {
350: buffer[cnt] = [self characterAt:RNGLOC(range) + cnt];
351: }
352: }
353:
354: - (void)getCharacters:(unichar *)buffer
355: {
356: NXRange range = {0, [self length]};
357: [self getCharacters:buffer range:range];
358: }
359:
360: /* Comparision and find stuff */
361:
362: // Compare the two character strings (whose lengths are given in firstLen & secondLen)
363: // according to the flags in flagMask.
364:
365: // ??? Unicode string compares work differently: As characters are compared and found to be unequal,
366: // they are normalized. Thus if "o" and "O" are compared and found unequal, they are normalized
367: // (depending on flagMask), and compared again. This normalization might include case conversion,
368: // floating diacritics, etc.
369:
370: NXComparisonResult NXCompareCharacters (const unichar *first, const unichar *second, unsigned firstLen, unsigned secondLen, unsigned flagMask, void *table)
371: {
372: unsigned cnt = 0, compareLen = MIN(firstLen, secondLen);
373: BOOL caseInsensitive = (flagMask & NX_CASE_INSENSITIVE) ? YES : NO;
374:
375: while (cnt < compareLen) {
376: unichar ch1 = first[cnt], ch2 = second[cnt];
377: if (caseInsensitive) { // Don't worry about unrolling this into two loops...
378: if (ch1 >= 'a' && ch1 <= 'z') ch1 += 'A' - 'a';
379: if (ch2 >= 'a' && ch2 <= 'z') ch2 += 'A' - 'a';
380: }
381: if (ch1 < ch2) return NX_OrderedAscending;
382: else if (ch1 > ch2) return NX_OrderedDescending;
383: else cnt++;
384: }
385: return (firstLen < secondLen) ? NX_OrderedAscending : ((firstLen > secondLen) ? NX_OrderedDescending : NX_OrderedSame);
386: }
387:
388: // Find findStr of len findStrLen in inStr of inStrLen. If flagMask contains NX_BACKWARDS_SEARCH,
389: // then look at inStr starting from the last valid character.
390: // See Unicode related warning under NXCompareCharacters().
391:
392: NXRange NXFindCharacters (const unichar *findStr, const unichar *inStr, unsigned findStrLen, unsigned inStrLen, unsigned flagMask, void *table)
393: {
394: int step;
395: unsigned fromLoc, toLoc, cnt; // fromLoc and toLoc are inclusive
396: BOOL found = NO, done = NO;
397: BOOL caseInsensitive = (flagMask & NX_CASE_INSENSITIVE) ? YES : NO;
398: NXRange range = {NX_STRING_NOT_FOUND, 0};
399:
400: if (findStrLen > inStrLen) { // ??? This can't be here for correct Unicode compares
401: return range;
402: }
403:
404: if (flagMask & NX_BACKWARDS_SEARCH) {
405: fromLoc = inStrLen - findStrLen; // Inclusive
406: toLoc = 0;
407: } else {
408: fromLoc = 0;
409: toLoc = inStrLen - findStrLen; // Inclusive
410: }
411:
412: step = (fromLoc <= toLoc) ? 1 : -1;
413: cnt = fromLoc;
414: do {
415: unsigned int chCnt;
416: for (chCnt = 0; chCnt < findStrLen; chCnt++) {
417: unichar ch1 = findStr[chCnt], ch2 = inStr[chCnt + cnt];
418: if (caseInsensitive) {
419: if (ch1 >= 'a' && ch1 <= 'z') ch1 += 'A' - 'a';
420: if (ch2 >= 'a' && ch2 <= 'z') ch2 += 'A' - 'a';
421: }
422: if (ch1 != ch2) {
423: break;
424: }
425: }
426: if (chCnt == findStrLen) {
427: found = done = YES;
428: RNGLOC(range) = cnt;
429: RNGLEN(range) = findStrLen;
430: } else if (cnt == toLoc) {
431: done = YES;
432: } else {
433: cnt += step;
434: }
435: } while (!done);
436:
437: return range;
438: }
439:
440: unsigned NXHashCharacters(const unichar *characters, unsigned length)
441: {
442: unsigned int h = length, cnt;
443:
444: if (length > MAXSTRINGLENFORHASHING) {
445: length = MAXSTRINGLENFORHASHING;
446: }
447:
448: for (cnt = 0; cnt < length; cnt++) {
449: h <<= 4;
450: h += (unsigned int)characters[cnt];
451: h ^= (h >> 24);
452: }
453:
454: return h;
455: }
456:
457: - (BOOL)isEqual:string
458: {
459: static Class stringClass = Nil;
460:
461: if (stringClass == Nil) stringClass = [NXString class];
462:
463: return (self == string) ||
464: ([string isKindOf: stringClass] &&
465: #if CHARS_ARE_EIGHT_BIT
466: ([self cStringLength] == [string cStringLength]) &&
467: #endif
468: ([self compare:string] == NX_OrderedSame));
469: }
470:
471: - (NXComparisonResult)compare:string
472: {
473: return [self compare:string mask:0 table:NULL];
474: }
475:
476: - (NXComparisonResult)compare:string mask:(unsigned int)options table:(void *)table
477: {
478: if (![string isKindOf:[NXString class]]) {
479: return NX_OrderedAscending; // ???
480: } else {
481: unsigned ownLength = [self length], otherLength = [string length];
482: unichar ownBuffer[COMPARELENGTH], otherBuffer[COMPARELENGTH];
483: NXRange ownRange = {0, 0}, otherRange = {0, 0};
484: NXComparisonResult res = NX_OrderedSame;
485:
486: while (1) {
487: RNGLEN(ownRange) = MIN(ownLength - RNGLOC(ownRange), COMPARELENGTH);
488: RNGLEN(otherRange) = MIN(otherLength - RNGLOC(otherRange), COMPARELENGTH);
489: if (RNGLEN(ownRange) == 0 && RNGLEN(otherRange) == 0) return NX_OrderedSame;
490: [self getCharacters:ownBuffer range:ownRange];
491: [string getCharacters:otherBuffer range:otherRange];
492: if ((res = NXCompareCharacters(ownBuffer, otherBuffer, RNGLEN(ownRange), RNGLEN(otherRange), options, table)) != NX_OrderedSame) return res;
493: RNGLOC(ownRange) += RNGLEN(ownRange);
494: RNGLOC(otherRange) += RNGLEN(otherRange);
495: }
496: }
497: }
498:
499: - (NXRange)findString:(NXString *)string
500: {
501: NXRange range = {0, [self length]};
502: return [self findString:string range:range mask:0 table:NULL];
503: }
504:
505: - (NXRange)findString:(NXString *)findStr range:(NXRange)fRange mask:(unsigned int)options table:(void *)table
506: {
507: int step;
508: unsigned fromLoc, toLoc, cnt, findStrLen, len; // fromLoc and toLoc are inclusive
509: BOOL found = NO, done = NO;
510: BOOL caseInsensitive = (options & NX_CASE_INSENSITIVE) ? YES : NO;
511: NXRange range = {NX_STRING_NOT_FOUND, 0};
512: unichar tmpBuf[MAXTMPBUFFERLEN], *findBuf;
513:
514: findStrLen = [findStr length];
515: len = [self length];
516:
517: if (findStrLen > RNGLEN(fRange)) { // ??? This can't be here for correct Unicode compares
518: return range;
519: }
520:
521: findBuf = (findStrLen > MAXTMPBUFFERLEN) ? NX_CHARALLOC(NXDefaultMallocZone(), findStrLen) : tmpBuf;
522: [findStr getCharacters:findBuf];
523:
524: if (options & NX_BACKWARDS_SEARCH) {
525: fromLoc = RNGLOC(fRange) + RNGLEN(fRange) - findStrLen;
526: toLoc = RNGLOC(fRange);
527: } else {
528: fromLoc = RNGLOC(fRange);
529: toLoc = RNGLOC(fRange) + RNGLEN(fRange) - findStrLen;
530: }
531:
532: step = (fromLoc <= toLoc) ? 1 : -1;
533: cnt = fromLoc;
534: do {
535: unsigned int chCnt;
536: for (chCnt = 0; chCnt < findStrLen; chCnt++) {
537: unichar ch1 = findBuf[chCnt], ch2 = [self characterAt:chCnt + cnt];
538: if (caseInsensitive) {
539: if (ch1 >= 'a' && ch1 <= 'z') ch1 += 'A' - 'a';
540: if (ch2 >= 'a' && ch2 <= 'z') ch2 += 'A' - 'a';
541: }
542: if (ch1 != ch2) {
543: break;
544: }
545: }
546: if (chCnt == findStrLen) {
547: found = done = YES;
548: RNGLOC(range) = cnt;
549: RNGLEN(range) = findStrLen;
550: } else if (cnt == toLoc) {
551: done = YES;
552: } else {
553: cnt += step;
554: }
555: } while (!done);
556:
557: if (findBuf != tmpBuf) free(findBuf);
558:
559: return range;
560: }
561:
562: - (unsigned)findCharacter:(unichar)ch
563: {
564: NXRange range = {0, [self length]};
565: return [self findCharacter:ch range:range mask:0 table:NULL];
566: }
567:
568: - (unsigned)findCharacter:(unichar)ch range:(NXRange)fRange mask:(unsigned int)options table:(void *)table
569: {
570: NXString *string = [[NXReadOnlyString alloc] initFromCharactersNoCopy:&ch length:1 freeWhenDone:NO];
571: NXRange result = [self findString:string range:fRange mask:options table:NULL];
572: [string free];
573: return RNGLOC(result);
574: }
575:
576: - (unsigned)findOneOf:(NXCharacterSet *)set
577: {
578: NXRange range = {0, [self length]};
579: return [self findOneOf:set range:range mask:0 table:NULL];
580: }
581:
582: // ??? How should we deal with the CASEINSENSITIVE flag? Probably we shouldn't care...
583:
584: - (unsigned)findOneOf:(NXCharacterSet *)set range:(NXRange)fRange mask:(unsigned int)options table:(void *)table
585: {
586: int step;
587: unsigned fromLoc, toLoc, cnt, len; // fromLoc and toLoc are inclusive
588: BOOL found = NO, done = NO;
589:
590: len = [self length];
591:
592: if (options & NX_BACKWARDS_SEARCH) {
593: fromLoc = RNGLOC(fRange) + RNGLEN(fRange) - 1;
594: toLoc = RNGLOC(fRange);
595: } else {
596: fromLoc = RNGLOC(fRange);
597: toLoc = RNGLOC(fRange) + RNGLEN(fRange) - 1;
598: }
599:
600: step = (fromLoc <= toLoc) ? 1 : -1;
601: cnt = fromLoc;
602:
603: do {
604: unichar ch = [self characterAt:cnt];
605: if ([set characterIsMember:ch]) {
606: done = found = YES;
607: } else if (cnt == toLoc) {
608: done = YES;
609: } else {
610: cnt += step;
611: }
612: } while (!done);
613:
614: return found ? cnt : NX_STRING_NOT_FOUND;
615: }
616:
617: - (unsigned)hash
618: {
619: unichar buffer[MAXSTRINGLENFORHASHING];
620: unsigned len = MIN([self length], MAXSTRINGLENFORHASHING);
621: NXRange range = {0, len};
622:
623: [self getCharacters:buffer range:range];
624: return NXHashCharacters(buffer, [self length]);
625: }
626:
627: - (NXString *)copySubstring:(NXRange)range
628: {
629: return [self copySubstring:range fromZone:[self zone]];
630: }
631:
632: - (NXString *)copySubstring:(NXRange)range fromZone:(NXZone *)zone
633: {
634: if (RNGLOC(range) + RNGLEN(range) > [self length]) BOUNDSERROR;
635: if (RNGLOC(range) == 0 && RNGLEN(range) == [self length]) {
636: return [self copyFromZone:zone];
637: } else {
638: id newObject = [[self class] allocFromZone:zone];
639: unichar *chars = [newObject allocateCharacterBuffer:RNGLEN(range)];
640: [self getCharacters:chars range:range];
641: return [newObject initFromCharactersNoCopy:chars length:RNGLEN(range)];
642: }
643: }
644:
645: - immutableCopy
646: {
647: return [self immutableCopyFromZone:[self zone]];
648: }
649:
650: - immutableCopyFromZone:(NXZone *)zone
651: {
652: return [self copyFromZone:zone];
653: }
654:
655: - mutableCopy
656: {
657: return [self mutableCopyFromZone:[self zone]];
658: }
659:
660: - mutableCopyFromZone:(NXZone *)zone
661: {
662: return [[NXReadWriteString allocFromZone:zone] initFromString:self];
663: }
664:
665: - (void)writeToStream:(NXStream *)stream
666: {
667: #if CHARS_ARE_EIGHT_BIT
668: [self writeCStringToStream:stream];
669: #else
670: #warning writeToStream: not implemented for Unicode
671: _NXStringErrorRaise (NXStringInternalError, "writeToStream: not implemented for Unicode");
672: #endif
673: }
674:
675: #define WRITEBUFFERLEN 1024
676:
677: - (void)writeCStringToStream:(NXStream *)stream
678: {
679: char buf[WRITEBUFFERLEN+1];
680: NXRange range = {0, [self length]};
681:
682: while (RNGLEN(range) > 0) {
683: NXRange remainingRange;
684: [self getCString:buf maxLength:WRITEBUFFERLEN range:range remainingRange:&remainingRange];
685: NXWrite (stream, buf, NX_LENGTH(range) - NX_LENGTH(remainingRange));
686: range = remainingRange;
687: }
688: }
689:
690: - (void)printForDebugger:(NXStream *)stream
691: {
692: unsigned int cnt, length = [self length];
693:
694: NXPrintf (stream, "%s, length %d: ", [[self class] name], length);
695:
696: for (cnt = 0; cnt < length; cnt++) {
697: unichar ch = [self characterAt:cnt];
698: NXPrintf (stream, (ch >= ' ' && ch < 127) ? "%c" : "<0x%x>", ch);
699: }
700: NXFlush (stream);
701: }
702:
703: #define TOOLONGLIMIT 200
704: #define EACHSECTION 80
705:
706: - (void)_print
707: {
708: unsigned int cnt = 0, length = [self length], breakAt = (length > TOOLONGLIMIT) ? EACHSECTION : UINT_MAX;
709:
710: fprintf (stderr, "%s, length %d: ", [[self class] name], length);
711:
712: while (cnt < length) {
713: unichar ch = [self characterAt:cnt];
714: fprintf (stderr, (ch >= ' ' && ch < 127) ? "%c" : "<0x%x>", ch);
715: if (++cnt == breakAt) {
716: cnt = length - EACHSECTION;
717: fprintf (stderr, "...");
718: }
719: }
720: fprintf (stderr, "\n");
721: }
722:
723:
724: #ifndef DONT_USE_OLD_NXSTRING_NAMES
725:
726: /* Compatibility stuff. These are 2.x/3.0 methods which should be preserved for 3.x but removed in 4.0.
727: */
728:
729: - initFromStream:(NXStream *)stream uptoLength:(unsigned)length orUntilOneOf:(NXCharacterSet *)set
730: {
731: return [self initFromStream:stream untilOneOf:set maxLength:length];
732: }
733:
734: - initFromByteStream:(NXStream *)stream uptoLength:(unsigned)length orUntilOneOf:(NXCharacterSet *)set
735: {
736: return [self initFromCStringStream:stream untilOneOf:set maxLength:length];
737: }
738:
739: - (void)getCString:(char *)buffer range:(NXStringRange)range
740: {
741: [self getCString:buffer maxLength:NX_MAX_STRING_LENGTH range:range remainingRange:NULL];
742: }
743:
744: - (void)getCString:(char *)buffer length:(unsigned)bytes
745: {
746: [self getCString:buffer maxLength:bytes];
747: }
748:
749: - (void)getCString:(char *)buffer length:(unsigned)bytes range:(NXStringRange)range
750: {
751: [self getCString:buffer maxLength:bytes range:range remainingRange:NULL];
752: }
753:
754: - (NXComparisionResult)compare:(NXString *)string mask:(unsigned int)options
755: {
756: return [self compare:string mask:options table:NULL];
757: }
758:
759: - (NXComparisionResult)compare:(NXString *)string mask:(unsigned int)options usingTable:(void *)table
760: {
761: return [self compare:string mask:options table:table];
762: }
763:
764: - (NXStringRange)find:(NXString *)string
765: {
766: return [self findString:string];
767: }
768:
769: - (NXStringRange)find:(NXString *)string range:(NXStringRange)range
770: {
771: return [self findString:string range:range mask:0 table:NULL];
772: }
773:
774: - (NXStringRange)find:(NXString *)string mask:(unsigned int)options
775: {
776: return [self findString:string range:(NXRange){0, [self length]} mask:options table:NULL];
777: }
778:
779: - (NXStringRange)find:(NXString *)string range:(NXStringRange)range mask:(unsigned int)options usingTable:(void *)table
780: {
781: return [self findString:string range:range mask:options table:table];
782: }
783:
784: - (unsigned)findOneOf:(NXCharacterSet *)set range:(NXStringRange)range
785: {
786: return [self findOneOf:set range:range mask:0 table:NULL];
787: }
788:
789: - (unsigned)findOneOf:(NXCharacterSet *)set mask:(unsigned int)options
790: {
791: return [self findOneOf:set range:(NXRange){0, [self length]} mask:options table:NULL];
792: }
793:
794: - (unsigned)findOneOf:(NXCharacterSet *)set range:(NXStringRange)range mask:(unsigned int)options usingTable:(void *)table
795: {
796: return [self findOneOf:set range:range mask:options table:table];
797: }
798:
799: - (unsigned)findCharacter:(unichar)ch range:(NXStringRange)range
800: {
801: return [self findCharacter:ch range:range mask:0 table:NULL];
802: }
803:
804: - (unsigned)findCharacter:(unichar)ch mask:(unsigned int)options
805: {
806: return [self findCharacter:ch range:(NXRange){0, [self length]} mask:options table:NULL];
807: }
808:
809: - (unsigned)findCharacter:(unichar)ch range:(NXStringRange)range mask:(unsigned int)options usingTable:(void *)table
810: {
811: return [self findCharacter:ch range:range mask:options table:table];
812: }
813:
814: - (unichar *)createCharacterBuffer:(unsigned)nChars
815: {
816: return [self allocateCharacterBuffer:nChars];
817: }
818:
819: - (void)writeBytesToStream:(NXStream *)stream
820: {
821: return [self writeCStringToStream:stream];
822: }
823:
824: #endif DONT_USE_OLD_NXSTRING_NAMES
825:
826: @end
827: #endif /* KERNEL */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.