|
|
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.