2 * Copyright (c) 2009 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 Copyright (c) 1998-2009, Apple Inc. All rights reserved.
25 Responsibility: Ali Ozer
27 !!! For performance reasons, it's important that all functions marked CF_INLINE in this file are inlined.
30 #include <CoreFoundation/CFBase.h>
31 #include <CoreFoundation/CFString.h>
32 #include <CoreFoundation/CFDictionary.h>
33 #include <CoreFoundation/CFStringEncodingConverterExt.h>
34 #include <CoreFoundation/CFUniChar.h>
35 #include <CoreFoundation/CFUnicodeDecomposition.h>
36 #include <CoreFoundation/CFUnicodePrecomposition.h>
37 #include <CoreFoundation/CFPriv.h>
38 #include "CFInternal.h"
39 #include "CFLocaleInternal.h"
43 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
46 #if DEPLOYMENT_TARGET_WINDOWS
47 #define strncasecmp_l(a, b, c, d) _strnicmp(a, b, c)
51 #define LONG_DOUBLE_SUPPORT 1
53 #define LONG_DOUBLE_SUPPORT 0
58 #define USE_STRING_ROM 0
61 #ifndef INSTRUMENT_SHARED_STRINGS
62 #define INSTRUMENT_SHARED_STRINGS 0
65 __private_extern__
const CFStringRef __kCFLocaleCollatorID
;
67 #if INSTRUMENT_SHARED_STRINGS
68 #include <sys/stat.h> /* for umask() */
70 static void __CFRecordStringAllocationEvent(const char *encoding
, const char *bytes
, CFIndex byteCount
) {
71 static CFSpinLock_t lock
= CFSpinLockInit
;
73 if (memchr(bytes
, '\n', byteCount
)) return; //never record string allocation events for strings with newlines, because those confuse our parser and because they'll never go into the ROM
78 extern char **_NSGetProgname(void);
79 const char *name
= *_NSGetProgname();
80 if (! name
) name
= "UNKNOWN";
83 snprintf(path
, sizeof(path
), "/tmp/CFSharedStringInstrumentation_%s_%d.txt", name
, getpid());
84 fd
= open(path
, O_WRONLY
| O_APPEND
| O_CREAT
, 0666);
87 const char *errString
= strerror(error
);
88 fprintf(stderr
, "open() failed with error %d (%s)\n", error
, errString
);
93 char formatString
[256];
94 snprintf(formatString
, sizeof(formatString
), "%%-8d\t%%-16s\t%%.%lds\n", byteCount
);
95 int resultCount
= asprintf(&buffer
, formatString
, getpid(), encoding
, bytes
);
96 if (buffer
&& resultCount
> 0) write(fd
, buffer
, resultCount
);
97 else puts("Couldn't record allocation event");
100 __CFSpinUnlock(&lock
);
102 #endif //INSTRUMENT_SHARED_STRINGS
106 typedef Boolean (*UNI_CHAR_FUNC
)(UInt32 flags
, UInt8 ch
, UniChar
*unicodeChar
);
108 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
109 extern size_t malloc_good_size(size_t size
);
111 extern void __CFStrConvertBytesToUnicode(const uint8_t *bytes
, UniChar
*buffer
, CFIndex numChars
);
115 // We put this into C & Pascal strings if we can't convert
116 #define CONVERSIONFAILURESTR "CFString conversion failed"
118 // We set this to true when purging the constant string table, so CFStringDeallocate doesn't assert
119 static Boolean __CFConstantStringTableBeingFreed
= false;
125 // This section is for CFString compatibility and other behaviors...
127 static CFOptionFlags _CFStringCompatibilityMask
= 0;
131 void _CFStringSetCompatibility(CFOptionFlags mask
) {
132 _CFStringCompatibilityMask
|= mask
;
135 CF_INLINE Boolean
__CFStringGetCompatibility(CFOptionFlags mask
) {
136 return (_CFStringCompatibilityMask
& mask
) == mask
;
141 // Two constant strings used by CFString; these are initialized in CFStringInitialize
142 CONST_STRING_DECL(kCFEmptyString
, "")
144 // This is separate for C++
145 struct __notInlineMutable
{
148 CFIndex capacity
; // Capacity in bytes
149 unsigned int hasGap
:1; // Currently unused
150 unsigned int isFixedCapacity
:1;
151 unsigned int isExternalMutable
:1;
152 unsigned int capacityProvidedExternally
:1;
154 unsigned long desiredCapacity
:60;
156 unsigned long desiredCapacity
:28;
158 CFAllocatorRef contentsAllocator
; // Optional
159 }; // The only mutable variant for CFString
162 /* !!! Never do sizeof(CFString); the union is here just to make it easier to access some fields.
166 union { // In many cases the allocated structs are smaller than these
169 } inline1
; // Bytes follow the length
170 struct __notInlineImmutable1
{
171 void *buffer
; // Note that the buffer is in the same place for all non-inline variants of CFString
173 CFAllocatorRef contentsDeallocator
; // Optional; just the dealloc func is used
174 } notInlineImmutable1
; // This is the usual not-inline immutable CFString
175 struct __notInlineImmutable2
{
177 CFAllocatorRef contentsDeallocator
; // Optional; just the dealloc func is used
178 } notInlineImmutable2
; // This is the not-inline immutable CFString when length is stored with the contents (first byte)
179 struct __notInlineMutable notInlineMutable
;
185 E = not inline contents
189 D = explicit deallocator for contents (for mutable objects, allocator)
190 C = length field is CFIndex (rather than UInt32); only meaningful for 64-bit, really
191 if needed this bit (valuable real-estate) can be given up for another bit elsewhere, since this info is needed just for 64-bit
193 Also need (only for mutable)
196 Cap, DesCap = capacity
198 B7 B6 B5 B4 B3 B2 B1 B0
203 0 1 E (freed with default allocator)
207 !!! Note: Constant CFStrings use the bit patterns:
208 C8 (11001000 = default allocator, not inline, not freed contents; 8-bit; has NULL byte; doesn't have length; is immutable)
209 D0 (11010000 = default allocator, not inline, not freed contents; Unicode; is immutable)
210 The bit usages should not be modified in a way that would effect these bit patterns.
214 __kCFFreeContentsWhenDoneMask
= 0x020,
215 __kCFFreeContentsWhenDone
= 0x020,
216 __kCFContentsMask
= 0x060,
217 __kCFHasInlineContents
= 0x000,
218 __kCFNotInlineContentsNoFree
= 0x040, // Don't free
219 __kCFNotInlineContentsDefaultFree
= 0x020, // Use allocator's free function
220 __kCFNotInlineContentsCustomFree
= 0x060, // Use a specially provided free function
221 __kCFHasContentsAllocatorMask
= 0x060,
222 __kCFHasContentsAllocator
= 0x060, // (For mutable strings) use a specially provided allocator
223 __kCFHasContentsDeallocatorMask
= 0x060,
224 __kCFHasContentsDeallocator
= 0x060,
225 __kCFIsMutableMask
= 0x01,
226 __kCFIsMutable
= 0x01,
227 __kCFIsUnicodeMask
= 0x10,
228 __kCFIsUnicode
= 0x10,
229 __kCFHasNullByteMask
= 0x08,
230 __kCFHasNullByte
= 0x08,
231 __kCFHasLengthByteMask
= 0x04,
232 __kCFHasLengthByte
= 0x04,
233 // !!! Bit 0x02 has been freed up
238 // Mutable strings are not inline
239 // Compile-time constant strings are not inline
240 // Mutable strings always have explicit length (but they might also have length byte and null byte)
241 // If there is an explicit length, always use that instead of the length byte (length byte is useful for quickly returning pascal strings)
242 // Never look at the length byte for the length; use __CFStrLength or __CFStrLength2
244 /* The following set of functions and macros need to be updated on change to the bit configuration
246 CF_INLINE Boolean
__CFStrIsMutable(CFStringRef str
) {return (str
->base
._cfinfo
[CF_INFO_BITS
] & __kCFIsMutableMask
) == __kCFIsMutable
;}
247 CF_INLINE Boolean
__CFStrIsInline(CFStringRef str
) {return (str
->base
._cfinfo
[CF_INFO_BITS
] & __kCFContentsMask
) == __kCFHasInlineContents
;}
248 CF_INLINE Boolean
__CFStrFreeContentsWhenDone(CFStringRef str
) {return (str
->base
._cfinfo
[CF_INFO_BITS
] & __kCFFreeContentsWhenDoneMask
) == __kCFFreeContentsWhenDone
;}
249 CF_INLINE Boolean
__CFStrHasContentsDeallocator(CFStringRef str
) {return (str
->base
._cfinfo
[CF_INFO_BITS
] & __kCFHasContentsDeallocatorMask
) == __kCFHasContentsDeallocator
;}
250 CF_INLINE Boolean
__CFStrIsUnicode(CFStringRef str
) {return (str
->base
._cfinfo
[CF_INFO_BITS
] & __kCFIsUnicodeMask
) == __kCFIsUnicode
;}
251 CF_INLINE Boolean
__CFStrIsEightBit(CFStringRef str
) {return (str
->base
._cfinfo
[CF_INFO_BITS
] & __kCFIsUnicodeMask
) != __kCFIsUnicode
;}
252 CF_INLINE Boolean
__CFStrHasNullByte(CFStringRef str
) {return (str
->base
._cfinfo
[CF_INFO_BITS
] & __kCFHasNullByteMask
) == __kCFHasNullByte
;}
253 CF_INLINE Boolean
__CFStrHasLengthByte(CFStringRef str
) {return (str
->base
._cfinfo
[CF_INFO_BITS
] & __kCFHasLengthByteMask
) == __kCFHasLengthByte
;}
254 CF_INLINE Boolean
__CFStrHasExplicitLength(CFStringRef str
) {return (str
->base
._cfinfo
[CF_INFO_BITS
] & (__kCFIsMutableMask
| __kCFHasLengthByteMask
)) != __kCFHasLengthByte
;} // Has explicit length if (1) mutable or (2) not mutable and no length byte
255 CF_INLINE Boolean
__CFStrIsConstant(CFStringRef str
) {
257 return str
->base
._rc
== 0;
259 return (str
->base
._cfinfo
[CF_RC_BITS
]) == 0;
263 CF_INLINE SInt32
__CFStrSkipAnyLengthByte(CFStringRef str
) {return ((str
->base
._cfinfo
[CF_INFO_BITS
] & __kCFHasLengthByteMask
) == __kCFHasLengthByte
) ? 1 : 0;} // Number of bytes to skip over the length byte in the contents
265 /* Returns ptr to the buffer (which might include the length byte)
267 CF_INLINE
const void *__CFStrContents(CFStringRef str
) {
268 if (__CFStrIsInline(str
)) {
269 return (const void *)(((uintptr_t)&(str
->variants
)) + (__CFStrHasExplicitLength(str
) ? sizeof(CFIndex
) : 0));
270 } else { // Not inline; pointer is always word 2
271 return str
->variants
.notInlineImmutable1
.buffer
;
275 static CFAllocatorRef
*__CFStrContentsDeallocatorPtr(CFStringRef str
) {
276 return __CFStrHasExplicitLength(str
) ? &(((CFMutableStringRef
)str
)->variants
.notInlineImmutable1
.contentsDeallocator
) : &(((CFMutableStringRef
)str
)->variants
.notInlineImmutable2
.contentsDeallocator
); }
278 // Assumption: Called with immutable strings only, and on strings that are known to have a contentsDeallocator
279 CF_INLINE CFAllocatorRef
__CFStrContentsDeallocator(CFStringRef str
) {
280 return *__CFStrContentsDeallocatorPtr(str
);
283 // Assumption: Called with immutable strings only, and on strings that are known to have a contentsDeallocator
284 CF_INLINE
void __CFStrSetContentsDeallocator(CFStringRef str
, CFAllocatorRef contentsAllocator
) {
285 *__CFStrContentsDeallocatorPtr(str
) = contentsAllocator
;
288 static CFAllocatorRef
*__CFStrContentsAllocatorPtr(CFStringRef str
) {
289 CFAssert(!__CFStrIsInline(str
), __kCFLogAssertion
, "Asking for contents allocator of inline string");
290 CFAssert(__CFStrIsMutable(str
), __kCFLogAssertion
, "Asking for contents allocator of an immutable string");
291 return (CFAllocatorRef
*)&(str
->variants
.notInlineMutable
.contentsAllocator
);
294 // Assumption: Called with strings that have a contents allocator; also, contents allocator follows custom
295 CF_INLINE CFAllocatorRef
__CFStrContentsAllocator(CFMutableStringRef str
) {
296 return *(__CFStrContentsAllocatorPtr(str
));
299 // Assumption: Called with strings that have a contents allocator; also, contents allocator follows custom
300 CF_INLINE
void __CFStrSetContentsAllocator(CFMutableStringRef str
, CFAllocatorRef alloc
) {
301 *(__CFStrContentsAllocatorPtr(str
)) = alloc
;
304 /* Returns length; use __CFStrLength2 if contents buffer pointer has already been computed.
306 CF_INLINE CFIndex
__CFStrLength(CFStringRef str
) {
307 if (__CFStrHasExplicitLength(str
)) {
308 if (__CFStrIsInline(str
)) {
309 return str
->variants
.inline1
.length
;
311 return str
->variants
.notInlineImmutable1
.length
;
314 return (CFIndex
)(*((uint8_t *)__CFStrContents(str
)));
318 CF_INLINE CFIndex
__CFStrLength2(CFStringRef str
, const void *buffer
) {
319 if (__CFStrHasExplicitLength(str
)) {
320 if (__CFStrIsInline(str
)) {
321 return str
->variants
.inline1
.length
;
323 return str
->variants
.notInlineImmutable1
.length
;
326 return (CFIndex
)(*((uint8_t *)buffer
));
331 Boolean
__CFStringIsEightBit(CFStringRef str
) {
332 return __CFStrIsEightBit(str
);
335 /* Sets the content pointer for immutable or mutable strings.
337 CF_INLINE
void __CFStrSetContentPtr(CFStringRef str
, const void *p
) {
338 // XXX_PCB catch all writes for mutable string case.
339 __CFAssignWithWriteBarrier((void **)&((CFMutableStringRef
)str
)->variants
.notInlineImmutable1
.buffer
, (void *)p
);
341 CF_INLINE
void __CFStrSetInfoBits(CFStringRef str
, UInt32 v
) {__CFBitfieldSetValue(((CFMutableStringRef
)str
)->base
._cfinfo
[CF_INFO_BITS
], 6, 0, v
);}
343 CF_INLINE
void __CFStrSetExplicitLength(CFStringRef str
, CFIndex v
) {
344 if (__CFStrIsInline(str
)) {
345 ((CFMutableStringRef
)str
)->variants
.inline1
.length
= v
;
347 ((CFMutableStringRef
)str
)->variants
.notInlineImmutable1
.length
= v
;
351 CF_INLINE
void __CFStrSetUnicode(CFMutableStringRef str
) {str
->base
._cfinfo
[CF_INFO_BITS
] |= __kCFIsUnicode
;}
352 CF_INLINE
void __CFStrClearUnicode(CFMutableStringRef str
) {str
->base
._cfinfo
[CF_INFO_BITS
] &= ~__kCFIsUnicode
;}
353 CF_INLINE
void __CFStrSetHasLengthAndNullBytes(CFMutableStringRef str
) {str
->base
._cfinfo
[CF_INFO_BITS
] |= (__kCFHasLengthByte
| __kCFHasNullByte
);}
354 CF_INLINE
void __CFStrClearHasLengthAndNullBytes(CFMutableStringRef str
) {str
->base
._cfinfo
[CF_INFO_BITS
] &= ~(__kCFHasLengthByte
| __kCFHasNullByte
);}
357 // Assumption: The following set of inlines (using str->variants.notInlineMutable) are called with mutable strings only
358 CF_INLINE Boolean
__CFStrIsFixed(CFStringRef str
) {return str
->variants
.notInlineMutable
.isFixedCapacity
;}
359 CF_INLINE Boolean
__CFStrIsExternalMutable(CFStringRef str
) {return str
->variants
.notInlineMutable
.isExternalMutable
;}
360 CF_INLINE Boolean
__CFStrHasContentsAllocator(CFStringRef str
) {return (str
->base
._cfinfo
[CF_INFO_BITS
] & __kCFHasContentsAllocatorMask
) == __kCFHasContentsAllocator
;}
361 CF_INLINE
void __CFStrSetIsFixed(CFMutableStringRef str
) {str
->variants
.notInlineMutable
.isFixedCapacity
= 1;}
362 CF_INLINE
void __CFStrSetIsExternalMutable(CFMutableStringRef str
) {str
->variants
.notInlineMutable
.isExternalMutable
= 1;}
363 CF_INLINE
void __CFStrSetHasGap(CFMutableStringRef str
) {str
->variants
.notInlineMutable
.hasGap
= 1;}
365 // If capacity is provided externally, we only change it when we need to grow beyond it
366 CF_INLINE Boolean
__CFStrCapacityProvidedExternally(CFStringRef str
) {return str
->variants
.notInlineMutable
.capacityProvidedExternally
;}
367 CF_INLINE
void __CFStrSetCapacityProvidedExternally(CFMutableStringRef str
) {str
->variants
.notInlineMutable
.capacityProvidedExternally
= 1;}
368 CF_INLINE
void __CFStrClearCapacityProvidedExternally(CFMutableStringRef str
) {str
->variants
.notInlineMutable
.capacityProvidedExternally
= 0;}
370 // "Capacity" is stored in number of bytes, not characters. It indicates the total number of bytes in the contents buffer.
371 CF_INLINE CFIndex
__CFStrCapacity(CFStringRef str
) {return str
->variants
.notInlineMutable
.capacity
;}
372 CF_INLINE
void __CFStrSetCapacity(CFMutableStringRef str
, CFIndex cap
) {str
->variants
.notInlineMutable
.capacity
= cap
;}
374 // "Desired capacity" is in number of characters; it is the client requested capacity; if fixed, it is the upper bound on the mutable string backing store.
375 CF_INLINE CFIndex
__CFStrDesiredCapacity(CFStringRef str
) {return str
->variants
.notInlineMutable
.desiredCapacity
;}
376 CF_INLINE
void __CFStrSetDesiredCapacity(CFMutableStringRef str
, CFIndex size
) {str
->variants
.notInlineMutable
.desiredCapacity
= size
;}
379 static void *__CFStrAllocateMutableContents(CFMutableStringRef str
, CFIndex size
) {
381 CFAllocatorRef alloc
= (__CFStrHasContentsAllocator(str
)) ? __CFStrContentsAllocator(str
) : __CFGetAllocator(str
);
382 ptr
= CFAllocatorAllocate(alloc
, size
, 0);
383 if (__CFOASafe
) __CFSetLastAllocationEventName(ptr
, "CFString (store)");
387 static void __CFStrDeallocateMutableContents(CFMutableStringRef str
, void *buffer
) {
388 CFAllocatorRef alloc
= (__CFStrHasContentsAllocator(str
)) ? __CFStrContentsAllocator(str
) : __CFGetAllocator(str
);
389 if (CF_IS_COLLECTABLE_ALLOCATOR(alloc
)) {
390 // GC: for finalization safety, let collector reclaim the buffer in the next GC cycle.
391 auto_zone_release(auto_zone(), buffer
);
393 CFAllocatorDeallocate(alloc
, buffer
);
400 /* CFString specific init flags
401 Note that you cannot count on the external buffer not being copied.
402 Also, if you specify an external buffer, you should not change it behind the CFString's back.
405 __kCFThinUnicodeIfPossible
= 0x1000000, /* See if the Unicode contents can be thinned down to 8-bit */
406 kCFStringPascal
= 0x10000, /* Indicating that the string data has a Pascal string structure (length byte at start) */
407 kCFStringNoCopyProvidedContents
= 0x20000, /* Don't copy the provided string contents if possible; free it when no longer needed */
408 kCFStringNoCopyNoFreeProvidedContents
= 0x30000 /* Don't copy the provided string contents if possible; don't free it when no longer needed */
413 static CFStringEncoding __CFDefaultSystemEncoding
= kCFStringEncodingInvalidId
;
414 static CFStringEncoding __CFDefaultFileSystemEncoding
= kCFStringEncodingInvalidId
;
415 CFStringEncoding __CFDefaultEightBitStringEncoding
= kCFStringEncodingInvalidId
;
418 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
419 #define __defaultEncoding kCFStringEncodingMacRoman
420 #elif DEPLOYMENT_TARGET_WINDOWS
421 #define __defaultEncoding kCFStringEncodingWindowsLatin1
423 #warning This value must match __CFGetConverter condition in CFStringEncodingConverter.c
424 #define __defaultEncoding kCFStringEncodingISOLatin1
427 CFStringEncoding
CFStringGetSystemEncoding(void) {
428 if (__CFDefaultSystemEncoding
== kCFStringEncodingInvalidId
) {
429 __CFDefaultSystemEncoding
= __defaultEncoding
;
430 const CFStringEncodingConverter
*converter
= CFStringEncodingGetConverter(__CFDefaultSystemEncoding
);
431 __CFSetCharToUniCharFunc(converter
->encodingClass
== kCFStringEncodingConverterCheapEightBit
? (UNI_CHAR_FUNC
)converter
->toUnicode
: NULL
);
433 return __CFDefaultSystemEncoding
;
436 // Fast version for internal use
438 CF_INLINE CFStringEncoding
__CFStringGetSystemEncoding(void) {
439 if (__CFDefaultSystemEncoding
== kCFStringEncodingInvalidId
) (void)CFStringGetSystemEncoding();
440 return __CFDefaultSystemEncoding
;
443 CFStringEncoding
CFStringFileSystemEncoding(void) {
444 if (__CFDefaultFileSystemEncoding
== kCFStringEncodingInvalidId
) {
445 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
446 __CFDefaultFileSystemEncoding
= kCFStringEncodingUTF8
;
448 __CFDefaultFileSystemEncoding
= CFStringGetSystemEncoding();
452 return __CFDefaultFileSystemEncoding
;
455 /* ??? Is returning length when no other answer is available the right thing?
456 !!! All of the (length > (LONG_MAX / N)) type checks are to avoid wrap-around and eventual malloc overflow in the client
458 CFIndex
CFStringGetMaximumSizeForEncoding(CFIndex length
, CFStringEncoding encoding
) {
459 if (encoding
== kCFStringEncodingUTF8
) {
460 if (_CFExecutableLinkedOnOrAfter(CFSystemVersionPanther
)) { // 1 Unichar can expand to 3 bytes; we return 6 for older apps for compatibility
461 return (length
> (LONG_MAX
/ 3)) ? kCFNotFound
: (length
* 3);
463 return (length
> (LONG_MAX
/ 6)) ? kCFNotFound
: (length
* 6);
465 } else if ((encoding
== kCFStringEncodingUTF32
) || (encoding
== kCFStringEncodingUTF32BE
) || (encoding
== kCFStringEncodingUTF32LE
)) { // UTF-32
466 return (length
> (LONG_MAX
/ sizeof(UTF32Char
))) ? kCFNotFound
: (length
* sizeof(UTF32Char
));
468 encoding
&= 0xFFF; // Mask off non-base part
471 case kCFStringEncodingUnicode
:
472 return (length
> (LONG_MAX
/ sizeof(UniChar
))) ? kCFNotFound
: (length
* sizeof(UniChar
));
474 case kCFStringEncodingNonLossyASCII
:
475 return (length
> (LONG_MAX
/ 6)) ? kCFNotFound
: (length
* 6); // 1 Unichar can expand to 6 bytes
477 case kCFStringEncodingMacRoman
:
478 case kCFStringEncodingWindowsLatin1
:
479 case kCFStringEncodingISOLatin1
:
480 case kCFStringEncodingNextStepLatin
:
481 case kCFStringEncodingASCII
:
482 return length
/ sizeof(uint8_t);
485 return length
/ sizeof(uint8_t);
490 /* Returns whether the indicated encoding can be stored in 8-bit chars
492 CF_INLINE Boolean
__CFStrEncodingCanBeStoredInEightBit(CFStringEncoding encoding
) {
493 switch (encoding
& 0xFFF) { // just use encoding base
494 case kCFStringEncodingInvalidId
:
495 case kCFStringEncodingUnicode
:
496 case kCFStringEncodingNonLossyASCII
:
499 case kCFStringEncodingMacRoman
:
500 case kCFStringEncodingWindowsLatin1
:
501 case kCFStringEncodingISOLatin1
:
502 case kCFStringEncodingNextStepLatin
:
503 case kCFStringEncodingASCII
:
506 default: return false;
510 /* Returns the encoding used in eight bit CFStrings (can't be any encoding which isn't 1-to-1 with Unicode)
511 ??? Perhaps only ASCII fits the bill due to Unicode decomposition.
513 CFStringEncoding
__CFStringComputeEightBitStringEncoding(void) {
514 if (__CFDefaultEightBitStringEncoding
== kCFStringEncodingInvalidId
) {
515 CFStringEncoding systemEncoding
= CFStringGetSystemEncoding();
516 if (systemEncoding
== kCFStringEncodingInvalidId
) { // We're right in the middle of querying system encoding from default database. Delaying to set until system encoding is determined.
517 return kCFStringEncodingASCII
;
518 } else if (__CFStrEncodingCanBeStoredInEightBit(systemEncoding
)) {
519 __CFDefaultEightBitStringEncoding
= systemEncoding
;
521 __CFDefaultEightBitStringEncoding
= kCFStringEncodingASCII
;
525 return __CFDefaultEightBitStringEncoding
;
528 /* Returns whether the provided bytes can be stored in ASCII
530 CF_INLINE Boolean
__CFBytesInASCII(const uint8_t *bytes
, CFIndex len
) {
531 while (len
--) if ((uint8_t)(*bytes
++) >= 128) return false;
535 /* Returns whether the provided 8-bit string in the specified encoding can be stored in an 8-bit CFString.
537 CF_INLINE Boolean
__CFCanUseEightBitCFStringForBytes(const uint8_t *bytes
, CFIndex len
, CFStringEncoding encoding
) {
538 // If the encoding is the same as the 8-bit CFString encoding, we can just use the bytes as-is.
539 // One exception is ASCII, which unfortunately needs to mean ISOLatin1 for compatibility reasons <rdar://problem/5458321>.
540 if (encoding
== __CFStringGetEightBitStringEncoding() && encoding
!= kCFStringEncodingASCII
) return true;
541 if (__CFStringEncodingIsSupersetOfASCII(encoding
) && __CFBytesInASCII(bytes
, len
)) return true;
546 /* Returns whether a length byte can be tacked on to a string of the indicated length.
548 CF_INLINE Boolean
__CFCanUseLengthByte(CFIndex len
) {
549 #define __kCFMaxPascalStrLen 255
550 return (len
<= __kCFMaxPascalStrLen
) ? true : false;
553 /* Various string assertions
555 #define __CFAssertIsString(cf) __CFGenericValidateType(cf, __kCFStringTypeID)
556 #define __CFAssertIndexIsInStringBounds(cf, idx) CFAssert3((idx) >= 0 && (idx) < __CFStrLength(cf), __kCFLogAssertion, "%s(): string index %d out of bounds (length %d)", __PRETTY_FUNCTION__, idx, __CFStrLength(cf))
557 #define __CFAssertRangeIsInStringBounds(cf, idx, count) CFAssert4((idx) >= 0 && (idx + count) <= __CFStrLength(cf), __kCFLogAssertion, "%s(): string range %d,%d out of bounds (length %d)", __PRETTY_FUNCTION__, idx, count, __CFStrLength(cf))
558 #define __CFAssertIsStringAndMutable(cf) {__CFGenericValidateType(cf, __kCFStringTypeID); CFAssert1(__CFStrIsMutable(cf), __kCFLogAssertion, "%s(): string not mutable", __PRETTY_FUNCTION__);}
559 #define __CFAssertIsStringAndExternalMutable(cf) {__CFGenericValidateType(cf, __kCFStringTypeID); CFAssert1(__CFStrIsMutable(cf) && __CFStrIsExternalMutable(cf), __kCFLogAssertion, "%s(): string not external mutable", __PRETTY_FUNCTION__);}
560 #define __CFAssertIsNotNegative(idx) CFAssert2(idx >= 0, __kCFLogAssertion, "%s(): index %d is negative", __PRETTY_FUNCTION__, idx)
561 #define __CFAssertIfFixedLengthIsOK(cf, reqLen) CFAssert2(!__CFStrIsFixed(cf) || (reqLen <= __CFStrDesiredCapacity(cf)), __kCFLogAssertion, "%s(): length %d too large", __PRETTY_FUNCTION__, reqLen)
564 /* Basic algorithm is to shrink memory when capacity is SHRINKFACTOR times the required capacity or to allocate memory when the capacity is less than GROWFACTOR times the required capacity. This function will return -1 if the new capacity is just too big (> LONG_MAX).
565 Additional complications are applied in the following order:
566 - desiredCapacity, which is the minimum (except initially things can be at zero)
567 - rounding up to factor of 8
568 - compressing (to fit the number if 16 bits), which effectively rounds up to factor of 256
569 - we need to make sure GROWFACTOR computation doesn't suffer from overflow issues on 32-bit, hence the casting to unsigned. Normally for required capacity of C bytes, the allocated space is (3C+1)/2. If C > ULONG_MAX/3, we instead simply return LONG_MAX
571 #define SHRINKFACTOR(c) (c / 2)
574 #define GROWFACTOR(c) ((c * 3 + 1) / 2)
576 #define GROWFACTOR(c) (((c) >= (ULONG_MAX / 3UL)) ? __CFMax(LONG_MAX - 4095, (c)) : (((unsigned long)c * 3 + 1) / 2))
579 CF_INLINE CFIndex
__CFStrNewCapacity(CFMutableStringRef str
, unsigned long reqCapacity
, CFIndex capacity
, Boolean leaveExtraRoom
, CFIndex charSize
) {
580 if (capacity
!= 0 || reqCapacity
!= 0) { /* If initially zero, and space not needed, leave it at that... */
581 if ((capacity
< reqCapacity
) || /* We definitely need the room... */
582 (!__CFStrCapacityProvidedExternally(str
) && /* Assuming we control the capacity... */
583 ((reqCapacity
< SHRINKFACTOR(capacity
)) || /* ...we have too much room! */
584 (!leaveExtraRoom
&& (reqCapacity
< capacity
))))) { /* ...we need to eliminate the extra space... */
585 if (reqCapacity
> LONG_MAX
) return -1; /* Too big any way you cut it */
586 unsigned long newCapacity
= leaveExtraRoom
? GROWFACTOR(reqCapacity
) : reqCapacity
; /* Grow by 3/2 if extra room is desired */
587 CFIndex desiredCapacity
= __CFStrDesiredCapacity(str
) * charSize
;
588 if (newCapacity
< desiredCapacity
) { /* If less than desired, bump up to desired */
589 newCapacity
= desiredCapacity
;
590 } else if (__CFStrIsFixed(str
)) { /* Otherwise, if fixed, no need to go above the desired (fixed) capacity */
591 newCapacity
= __CFMax(desiredCapacity
, reqCapacity
); /* !!! So, fixed is not really fixed, but "tight" */
593 if (__CFStrHasContentsAllocator(str
)) { /* Also apply any preferred size from the allocator */
594 newCapacity
= CFAllocatorGetPreferredSizeForSize(__CFStrContentsAllocator(str
), newCapacity
, 0);
595 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
597 newCapacity
= malloc_good_size(newCapacity
);
600 return (newCapacity
> LONG_MAX
) ? -1 : (CFIndex
)newCapacity
; // If packing: __CFStrUnpackNumber(__CFStrPackNumber(newCapacity));
607 /* rearrangeBlocks() rearranges the blocks of data within the buffer so that they are "evenly spaced". buffer is assumed to have enough room for the result.
608 numBlocks is current total number of blocks within buffer.
609 blockSize is the size of each block in bytes
610 ranges and numRanges hold the ranges that are no longer needed; ranges are stored sorted in increasing order, and don't overlap
611 insertLength is the final spacing between the remaining blocks
613 Example: buffer = A B C D E F G H, blockSize = 1, ranges = { (2,1) , (4,2) } (so we want to "delete" C and E F), fromEnd = NO
614 if insertLength = 4, result = A B ? ? ? ? D ? ? ? ? G H
615 if insertLength = 0, result = A B D G H
617 Example: buffer = A B C D E F G H I J K L M N O P Q R S T U, blockSize = 1, ranges { (1,1), (3,1), (5,11), (17,1), (19,1) }, fromEnd = NO
618 if insertLength = 3, result = A ? ? ? C ? ? ? E ? ? ? Q ? ? ? S ? ? ? U
621 typedef struct _CFStringDeferredRange
{
625 } CFStringDeferredRange
;
627 typedef struct _CFStringStackInfo
{
628 CFIndex capacity
; // Capacity (if capacity == count, need to realloc to add another)
629 CFIndex count
; // Number of elements actually stored
630 CFStringDeferredRange
*stack
;
631 Boolean hasMalloced
; // Indicates "stack" is allocated and needs to be deallocated when done
635 CF_INLINE
void pop (CFStringStackInfo
*si
, CFStringDeferredRange
*topRange
) {
636 si
->count
= si
->count
- 1;
637 *topRange
= si
->stack
[si
->count
];
640 CF_INLINE
void push (CFStringStackInfo
*si
, const CFStringDeferredRange
*newRange
) {
641 if (si
->count
== si
->capacity
) {
642 // increase size of the stack
643 si
->capacity
= (si
->capacity
+ 4) * 2;
644 if (si
->hasMalloced
) {
645 si
->stack
= (CFStringDeferredRange
*)CFAllocatorReallocate(kCFAllocatorSystemDefault
, si
->stack
, si
->capacity
* sizeof(CFStringDeferredRange
), 0);
647 CFStringDeferredRange
*newStack
= (CFStringDeferredRange
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, si
->capacity
* sizeof(CFStringDeferredRange
), 0);
648 memmove(newStack
, si
->stack
, si
->count
* sizeof(CFStringDeferredRange
));
649 si
->stack
= newStack
;
650 si
->hasMalloced
= true;
653 si
->stack
[si
->count
] = *newRange
;
654 si
->count
= si
->count
+ 1;
657 static void rearrangeBlocks(
661 const CFRange
*ranges
,
663 CFIndex insertLength
) {
665 #define origStackSize 10
666 CFStringDeferredRange origStack
[origStackSize
];
667 CFStringStackInfo si
= {origStackSize
, 0, origStack
, false, {0, 0, 0}};
668 CFStringDeferredRange currentNonRange
= {0, 0, 0};
669 CFIndex currentRange
= 0;
670 CFIndex amountShifted
= 0;
672 // must have at least 1 range left.
674 while (currentRange
< numRanges
) {
675 currentNonRange
.beginning
= (ranges
[currentRange
].location
+ ranges
[currentRange
].length
) * blockSize
;
676 if ((numRanges
- currentRange
) == 1) {
678 currentNonRange
.length
= numBlocks
* blockSize
- currentNonRange
.beginning
;
679 if (currentNonRange
.length
== 0) break;
681 currentNonRange
.length
= (ranges
[currentRange
+ 1].location
* blockSize
) - currentNonRange
.beginning
;
683 currentNonRange
.shift
= amountShifted
+ (insertLength
* blockSize
) - (ranges
[currentRange
].length
* blockSize
);
684 amountShifted
= currentNonRange
.shift
;
685 if (amountShifted
<= 0) {
686 // process current item and rest of stack
687 if (currentNonRange
.shift
&& currentNonRange
.length
) memmove (&buffer
[currentNonRange
.beginning
+ currentNonRange
.shift
], &buffer
[currentNonRange
.beginning
], currentNonRange
.length
);
688 while (si
.count
> 0) {
689 pop (&si
, ¤tNonRange
); // currentNonRange now equals the top element of the stack.
690 if (currentNonRange
.shift
&& currentNonRange
.length
) memmove (&buffer
[currentNonRange
.beginning
+ currentNonRange
.shift
], &buffer
[currentNonRange
.beginning
], currentNonRange
.length
);
693 // add currentNonRange to stack.
694 push (&si
, ¤tNonRange
);
699 // no more ranges. if anything is on the stack, process.
701 while (si
.count
> 0) {
702 pop (&si
, ¤tNonRange
); // currentNonRange now equals the top element of the stack.
703 if (currentNonRange
.shift
&& currentNonRange
.length
) memmove (&buffer
[currentNonRange
.beginning
+ currentNonRange
.shift
], &buffer
[currentNonRange
.beginning
], currentNonRange
.length
);
705 if (si
.hasMalloced
) CFAllocatorDeallocate (kCFAllocatorSystemDefault
, si
.stack
);
708 /* See comments for rearrangeBlocks(); this is the same, but the string is assembled in another buffer (dstBuffer), so the algorithm is much easier. We also take care of the case where the source is not-Unicode but destination is. (The reverse case is not supported.)
710 static void copyBlocks(
711 const uint8_t *srcBuffer
,
714 Boolean srcIsUnicode
,
715 Boolean dstIsUnicode
,
716 const CFRange
*ranges
,
718 CFIndex insertLength
) {
720 CFIndex srcLocationInBytes
= 0; // in order to avoid multiplying all the time, this is in terms of bytes, not blocks
721 CFIndex dstLocationInBytes
= 0; // ditto
722 CFIndex srcBlockSize
= srcIsUnicode
? sizeof(UniChar
) : sizeof(uint8_t);
723 CFIndex insertLengthInBytes
= insertLength
* (dstIsUnicode
? sizeof(UniChar
) : sizeof(uint8_t));
724 CFIndex rangeIndex
= 0;
725 CFIndex srcToDstMultiplier
= (srcIsUnicode
== dstIsUnicode
) ? 1 : (sizeof(UniChar
) / sizeof(uint8_t));
727 // Loop over the ranges, copying the range to be preserved (right before each range)
728 while (rangeIndex
< numRanges
) {
729 CFIndex srcLengthInBytes
= ranges
[rangeIndex
].location
* srcBlockSize
- srcLocationInBytes
; // srcLengthInBytes is in terms of bytes, not blocks; represents length of region to be preserved
730 if (srcLengthInBytes
> 0) {
731 if (srcIsUnicode
== dstIsUnicode
) {
732 memmove(dstBuffer
+ dstLocationInBytes
, srcBuffer
+ srcLocationInBytes
, srcLengthInBytes
);
734 __CFStrConvertBytesToUnicode(srcBuffer
+ srcLocationInBytes
, (UniChar
*)(dstBuffer
+ dstLocationInBytes
), srcLengthInBytes
);
737 srcLocationInBytes
+= srcLengthInBytes
+ ranges
[rangeIndex
].length
* srcBlockSize
; // Skip over the just-copied and to-be-deleted stuff
738 dstLocationInBytes
+= srcLengthInBytes
* srcToDstMultiplier
+ insertLengthInBytes
;
742 // Do last range (the one beyond last range)
743 if (srcLocationInBytes
< srcLength
* srcBlockSize
) {
744 if (srcIsUnicode
== dstIsUnicode
) {
745 memmove(dstBuffer
+ dstLocationInBytes
, srcBuffer
+ srcLocationInBytes
, srcLength
* srcBlockSize
- srcLocationInBytes
);
747 __CFStrConvertBytesToUnicode(srcBuffer
+ srcLocationInBytes
, (UniChar
*)(dstBuffer
+ dstLocationInBytes
), srcLength
* srcBlockSize
- srcLocationInBytes
);
752 /* Call the callback; if it doesn't exist or returns false, then log
754 static void __CFStringHandleOutOfMemory(CFTypeRef obj
) {
755 CFStringRef msg
= CFSTR("Out of memory. We suggest restarting the application. If you have an unsaved document, create a backup copy in Finder, then try to save.");
757 CFLog(kCFLogLevelCritical
, CFSTR("%@"), msg
);
761 /* Reallocates the backing store of the string to accomodate the new length. Space is reserved or characters are deleted as indicated by insertLength and the ranges in deleteRanges. The length is updated to reflect the new state. Will also maintain a length byte and a null byte in 8-bit strings. If length cannot fit in length byte, the space will still be reserved, but will be 0. (Hence the reason the length byte should never be looked at as length unless there is no explicit length.)
763 static void __CFStringChangeSizeMultiple(CFMutableStringRef str
, const CFRange
*deleteRanges
, CFIndex numDeleteRanges
, CFIndex insertLength
, Boolean makeUnicode
) {
764 const uint8_t *curContents
= (uint8_t *)__CFStrContents(str
);
765 CFIndex curLength
= curContents
? __CFStrLength2(str
, curContents
) : 0;
766 unsigned long newLength
; // We use unsigned to better keep track of overflow
768 // Compute new length of the string
769 if (numDeleteRanges
== 1) {
770 newLength
= curLength
+ insertLength
- deleteRanges
[0].length
;
773 newLength
= curLength
+ insertLength
* numDeleteRanges
;
774 for (cnt
= 0; cnt
< numDeleteRanges
; cnt
++) newLength
-= deleteRanges
[cnt
].length
;
777 __CFAssertIfFixedLengthIsOK(str
, newLength
);
779 if (newLength
== 0) {
780 // An somewhat optimized code-path for this special case, with the following implicit values:
781 // newIsUnicode = false
782 // useLengthAndNullBytes = false
783 // newCharSize = sizeof(uint8_t)
784 // If the newCapacity happens to be the same as the old, we don't free the buffer; otherwise we just free it totally
785 // instead of doing a potentially useless reallocation (as the needed capacity later might turn out to be different anyway)
786 CFIndex curCapacity
= __CFStrCapacity(str
);
787 CFIndex newCapacity
= __CFStrNewCapacity(str
, 0, curCapacity
, true, sizeof(uint8_t));
788 if (newCapacity
!= curCapacity
) { // If we're reallocing anyway (larger or smaller --- larger could happen if desired capacity was changed in the meantime), let's just free it all
789 if (curContents
) __CFStrDeallocateMutableContents(str
, (uint8_t *)curContents
);
790 __CFStrSetContentPtr(str
, NULL
);
791 __CFStrSetCapacity(str
, 0);
792 __CFStrClearCapacityProvidedExternally(str
);
793 __CFStrClearHasLengthAndNullBytes(str
);
794 if (!__CFStrIsExternalMutable(str
)) __CFStrClearUnicode(str
); // External mutable implies Unicode
796 if (!__CFStrIsExternalMutable(str
)) {
797 __CFStrClearUnicode(str
);
798 if (curCapacity
>= (int)(sizeof(uint8_t) * 2)) { // If there's room
799 __CFStrSetHasLengthAndNullBytes(str
);
800 ((uint8_t *)curContents
)[0] = ((uint8_t *)curContents
)[1] = 0;
802 __CFStrClearHasLengthAndNullBytes(str
);
806 __CFStrSetExplicitLength(str
, 0);
807 } else { /* This else-clause assumes newLength > 0 */
808 Boolean oldIsUnicode
= __CFStrIsUnicode(str
);
809 Boolean newIsUnicode
= makeUnicode
|| (oldIsUnicode
/* && (newLength > 0) - implicit */ ) || __CFStrIsExternalMutable(str
);
810 CFIndex newCharSize
= newIsUnicode
? sizeof(UniChar
) : sizeof(uint8_t);
811 Boolean useLengthAndNullBytes
= !newIsUnicode
/* && (newLength > 0) - implicit */;
812 CFIndex numExtraBytes
= useLengthAndNullBytes
? 2 : 0; /* 2 extra bytes to keep the length byte & null... */
813 CFIndex curCapacity
= __CFStrCapacity(str
);
814 if (newLength
> (LONG_MAX
- numExtraBytes
) / newCharSize
) __CFStringHandleOutOfMemory(str
); // Does not return
815 CFIndex newCapacity
= __CFStrNewCapacity(str
, newLength
* newCharSize
+ numExtraBytes
, curCapacity
, true, newCharSize
);
816 if (newCapacity
== -1) __CFStringHandleOutOfMemory(str
); // Does not return
817 Boolean allocNewBuffer
= (newCapacity
!= curCapacity
) || (curLength
> 0 && !oldIsUnicode
&& newIsUnicode
); /* We alloc new buffer if oldIsUnicode != newIsUnicode because the contents have to be copied */
818 uint8_t *newContents
;
819 if (allocNewBuffer
) {
820 newContents
= (uint8_t *)__CFStrAllocateMutableContents(str
, newCapacity
);
821 if (!newContents
) { // Try allocating without extra room
822 newCapacity
= __CFStrNewCapacity(str
, newLength
* newCharSize
+ numExtraBytes
, curCapacity
, false, newCharSize
);
823 // Since we checked for this above, it shouldn't be the case here, but just in case
824 if (newCapacity
== -1) __CFStringHandleOutOfMemory(str
); // Does not return
825 newContents
= (uint8_t *)__CFStrAllocateMutableContents(str
, newCapacity
);
826 if (!newContents
) __CFStringHandleOutOfMemory(str
); // Does not return
829 newContents
= (uint8_t *)curContents
;
832 Boolean hasLengthAndNullBytes
= __CFStrHasLengthByte(str
);
834 CFAssert1(hasLengthAndNullBytes
== __CFStrHasNullByte(str
), __kCFLogAssertion
, "%s(): Invalid state in 8-bit string", __PRETTY_FUNCTION__
);
836 if (hasLengthAndNullBytes
) curContents
++;
837 if (useLengthAndNullBytes
) newContents
++;
840 if (oldIsUnicode
== newIsUnicode
) {
841 if (newContents
== curContents
) {
842 rearrangeBlocks(newContents
, curLength
, newCharSize
, deleteRanges
, numDeleteRanges
, insertLength
);
844 copyBlocks(curContents
, newContents
, curLength
, oldIsUnicode
, newIsUnicode
, deleteRanges
, numDeleteRanges
, insertLength
);
846 } else if (newIsUnicode
) { /* this implies we have a new buffer */
847 copyBlocks(curContents
, newContents
, curLength
, oldIsUnicode
, newIsUnicode
, deleteRanges
, numDeleteRanges
, insertLength
);
849 if (hasLengthAndNullBytes
) curContents
--; /* Undo the damage from above */
850 if (allocNewBuffer
&& __CFStrFreeContentsWhenDone(str
)) __CFStrDeallocateMutableContents(str
, (void *)curContents
);
854 if (useLengthAndNullBytes
) {
855 newContents
[newLength
] = 0; /* Always have null byte, if not unicode */
856 newContents
--; /* Undo the damage from above */
857 newContents
[0] = __CFCanUseLengthByte(newLength
) ? (uint8_t)newLength
: 0;
858 if (!hasLengthAndNullBytes
) __CFStrSetHasLengthAndNullBytes(str
);
860 if (hasLengthAndNullBytes
) __CFStrClearHasLengthAndNullBytes(str
);
862 if (oldIsUnicode
) __CFStrClearUnicode(str
);
863 } else { // New is unicode...
864 if (!oldIsUnicode
) __CFStrSetUnicode(str
);
865 if (hasLengthAndNullBytes
) __CFStrClearHasLengthAndNullBytes(str
);
867 __CFStrSetExplicitLength(str
, newLength
);
869 if (allocNewBuffer
) {
870 __CFStrSetCapacity(str
, newCapacity
);
871 __CFStrClearCapacityProvidedExternally(str
);
872 __CFStrSetContentPtr(str
, newContents
);
877 /* Same as above, but takes one range (very common case)
879 CF_INLINE
void __CFStringChangeSize(CFMutableStringRef str
, CFRange range
, CFIndex insertLength
, Boolean makeUnicode
) {
880 __CFStringChangeSizeMultiple(str
, &range
, 1, insertLength
, makeUnicode
);
885 static Boolean
__CFStrIsConstantString(CFStringRef str
);
888 static void __CFStringDeallocate(CFTypeRef cf
) {
889 CFStringRef str
= (CFStringRef
)cf
;
891 // If in DEBUG mode, check to see if the string a CFSTR, and complain.
892 CFAssert1(__CFConstantStringTableBeingFreed
|| !__CFStrIsConstantString((CFStringRef
)cf
), __kCFLogAssertion
, "Tried to deallocate CFSTR(\"%@\")", str
);
894 if (!__CFStrIsInline(str
)) {
896 Boolean isMutable
= __CFStrIsMutable(str
);
897 if (__CFStrFreeContentsWhenDone(str
) && (contents
= (uint8_t *)__CFStrContents(str
))) {
899 __CFStrDeallocateMutableContents((CFMutableStringRef
)str
, contents
);
901 if (__CFStrHasContentsDeallocator(str
)) {
902 CFAllocatorRef contentsDeallocator
= __CFStrContentsDeallocator(str
);
903 CFAllocatorDeallocate(contentsDeallocator
, contents
);
904 CFRelease(contentsDeallocator
);
906 CFAllocatorRef alloc
= __CFGetAllocator(str
);
907 CFAllocatorDeallocate(alloc
, contents
);
911 if (isMutable
&& __CFStrHasContentsAllocator(str
)) CFRelease(__CFStrContentsAllocator((CFMutableStringRef
)str
));
915 static Boolean
__CFStringEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
916 CFStringRef str1
= (CFStringRef
)cf1
;
917 CFStringRef str2
= (CFStringRef
)cf2
;
918 const uint8_t *contents1
;
919 const uint8_t *contents2
;
922 /* !!! We do not need IsString assertions, as the CFBase runtime assures this */
923 /* !!! We do not need == test, as the CFBase runtime assures this */
925 contents1
= (uint8_t *)__CFStrContents(str1
);
926 contents2
= (uint8_t *)__CFStrContents(str2
);
927 len1
= __CFStrLength2(str1
, contents1
);
929 if (len1
!= __CFStrLength2(str2
, contents2
)) return false;
931 contents1
+= __CFStrSkipAnyLengthByte(str1
);
932 contents2
+= __CFStrSkipAnyLengthByte(str2
);
934 if (__CFStrIsEightBit(str1
) && __CFStrIsEightBit(str2
)) {
935 return memcmp((const char *)contents1
, (const char *)contents2
, len1
) ? false : true;
936 } else if (__CFStrIsEightBit(str1
)) { /* One string has Unicode contents */
937 CFStringInlineBuffer buf
;
940 CFStringInitInlineBuffer(str1
, &buf
, CFRangeMake(0, len1
));
941 for (buf_idx
= 0; buf_idx
< len1
; buf_idx
++) {
942 if (__CFStringGetCharacterFromInlineBufferQuick(&buf
, buf_idx
) != ((UniChar
*)contents2
)[buf_idx
]) return false;
944 } else if (__CFStrIsEightBit(str2
)) { /* One string has Unicode contents */
945 CFStringInlineBuffer buf
;
948 CFStringInitInlineBuffer(str2
, &buf
, CFRangeMake(0, len1
));
949 for (buf_idx
= 0; buf_idx
< len1
; buf_idx
++) {
950 if (__CFStringGetCharacterFromInlineBufferQuick(&buf
, buf_idx
) != ((UniChar
*)contents1
)[buf_idx
]) return false;
952 } else { /* Both strings have Unicode contents */
954 for (idx
= 0; idx
< len1
; idx
++) {
955 if (((UniChar
*)contents1
)[idx
] != ((UniChar
*)contents2
)[idx
]) return false;
962 /* String hashing: Should give the same results whatever the encoding; so we hash UniChars.
963 If the length is less than or equal to 96, then the hash function is simply the
964 following (n is the nth UniChar character, starting from 0):
967 hash(n) = hash(n-1) * 257 + unichar(n);
968 Hash = hash(length-1) * ((length & 31) + 1)
970 If the length is greater than 96, then the above algorithm applies to
971 characters 0..31, (length/2)-16..(length/2)+15, and length-32..length-1, inclusive;
972 thus the first, middle, and last 32 characters.
974 Note that the loops below are unrolled; and: 257^2 = 66049; 257^3 = 16974593; 257^4 = 4362470401; 67503105 is 257^4 - 256^4
975 If hashcode is changed from UInt32 to something else, this last piece needs to be readjusted.
976 !!! We haven't updated for LP64 yet
978 NOTE: The hash algorithm used to be duplicated in CF and Foundation; but now it should only be in the four functions below.
980 Hash function was changed between Panther and Tiger, and Tiger and Leopard.
982 #define HashEverythingLimit 96
984 #define HashNextFourUniChars(accessStart, accessEnd, pointer) \
985 {result = result * 67503105 + (accessStart 0 accessEnd) * 16974593 + (accessStart 1 accessEnd) * 66049 + (accessStart 2 accessEnd) * 257 + (accessStart 3 accessEnd); pointer += 4;}
987 #define HashNextUniChar(accessStart, accessEnd, pointer) \
988 {result = result * 257 + (accessStart 0 accessEnd); pointer++;}
991 /* In this function, actualLen is the length of the original string; but len is the number of characters in buffer. The buffer is expected to contain the parts of the string relevant to hashing.
993 CF_INLINE CFHashCode
__CFStrHashCharacters(const UniChar
*uContents
, CFIndex len
, CFIndex actualLen
) {
994 CFHashCode result
= actualLen
;
995 if (len
<= HashEverythingLimit
) {
996 const UniChar
*end4
= uContents
+ (len
& ~3);
997 const UniChar
*end
= uContents
+ len
;
998 while (uContents
< end4
) HashNextFourUniChars(uContents
[, ], uContents
); // First count in fours
999 while (uContents
< end
) HashNextUniChar(uContents
[, ], uContents
); // Then for the last <4 chars, count in ones...
1001 const UniChar
*contents
, *end
;
1002 contents
= uContents
;
1003 end
= contents
+ 32;
1004 while (contents
< end
) HashNextFourUniChars(contents
[, ], contents
);
1005 contents
= uContents
+ (len
>> 1) - 16;
1006 end
= contents
+ 32;
1007 while (contents
< end
) HashNextFourUniChars(contents
[, ], contents
);
1008 end
= uContents
+ len
;
1009 contents
= end
- 32;
1010 while (contents
< end
) HashNextFourUniChars(contents
[, ], contents
);
1012 return result
+ (result
<< (actualLen
& 31));
1015 /* This hashes cString in the eight bit string encoding. It also includes the little debug-time sanity check.
1017 CF_INLINE CFHashCode
__CFStrHashEightBit(const uint8_t *cContents
, CFIndex len
) {
1019 if (!__CFCharToUniCharFunc
) { // A little sanity verification: If this is not set, trying to hash high byte chars would be a bad idea
1021 Boolean err
= false;
1022 if (len
<= HashEverythingLimit
) {
1023 for (cnt
= 0; cnt
< len
; cnt
++) if (cContents
[cnt
] >= 128) err
= true;
1025 for (cnt
= 0; cnt
< 32; cnt
++) if (cContents
[cnt
] >= 128) err
= true;
1026 for (cnt
= (len
>> 1) - 16; cnt
< (len
>> 1) + 16; cnt
++) if (cContents
[cnt
] >= 128) err
= true;
1027 for (cnt
= (len
- 32); cnt
< len
; cnt
++) if (cContents
[cnt
] >= 128) err
= true;
1030 // Can't do log here, as it might be too early
1031 fprintf(stderr
, "Warning: CFHash() attempting to hash CFString containing high bytes before properly initialized to do so\n");
1035 CFHashCode result
= len
;
1036 if (len
<= HashEverythingLimit
) {
1037 const uint8_t *end4
= cContents
+ (len
& ~3);
1038 const uint8_t *end
= cContents
+ len
;
1039 while (cContents
< end4
) HashNextFourUniChars(__CFCharToUniCharTable
[cContents
[, ]], cContents
); // First count in fours
1040 while (cContents
< end
) HashNextUniChar(__CFCharToUniCharTable
[cContents
[, ]], cContents
); // Then for the last <4 chars, count in ones...
1042 const uint8_t *contents
, *end
;
1043 contents
= cContents
;
1044 end
= contents
+ 32;
1045 while (contents
< end
) HashNextFourUniChars(__CFCharToUniCharTable
[contents
[, ]], contents
);
1046 contents
= cContents
+ (len
>> 1) - 16;
1047 end
= contents
+ 32;
1048 while (contents
< end
) HashNextFourUniChars(__CFCharToUniCharTable
[contents
[, ]], contents
);
1049 end
= cContents
+ len
;
1050 contents
= end
- 32;
1051 while (contents
< end
) HashNextFourUniChars(__CFCharToUniCharTable
[contents
[, ]], contents
);
1053 return result
+ (result
<< (len
& 31));
1056 CFHashCode
CFStringHashISOLatin1CString(const uint8_t *bytes
, CFIndex len
) {
1057 CFHashCode result
= len
;
1058 if (len
<= HashEverythingLimit
) {
1059 const uint8_t *end4
= bytes
+ (len
& ~3);
1060 const uint8_t *end
= bytes
+ len
;
1061 while (bytes
< end4
) HashNextFourUniChars(bytes
[, ], bytes
); // First count in fours
1062 while (bytes
< end
) HashNextUniChar(bytes
[, ], bytes
); // Then for the last <4 chars, count in ones...
1064 const uint8_t *contents
, *end
;
1066 end
= contents
+ 32;
1067 while (contents
< end
) HashNextFourUniChars(contents
[, ], contents
);
1068 contents
= bytes
+ (len
>> 1) - 16;
1069 end
= contents
+ 32;
1070 while (contents
< end
) HashNextFourUniChars(contents
[, ], contents
);
1072 contents
= end
- 32;
1073 while (contents
< end
) HashNextFourUniChars(contents
[, ], contents
);
1075 return result
+ (result
<< (len
& 31));
1078 CFHashCode
CFStringHashCString(const uint8_t *bytes
, CFIndex len
) {
1079 return __CFStrHashEightBit(bytes
, len
);
1082 CFHashCode
CFStringHashCharacters(const UniChar
*characters
, CFIndex len
) {
1083 return __CFStrHashCharacters(characters
, len
, len
);
1086 /* This is meant to be called from NSString or subclassers only. It is an error for this to be called without the ObjC runtime or an argument which is not an NSString or subclass. It can be called with NSCFString, although that would be inefficient (causing indirection) and won't normally happen anyway, as NSCFString overrides hash.
1088 CFHashCode
CFStringHashNSString(CFStringRef str
) {
1089 UniChar buffer
[HashEverythingLimit
];
1090 CFIndex bufLen
; // Number of characters in the buffer for hashing
1091 CFIndex len
= 0; // Actual length of the string
1093 CF_OBJC_CALL0(CFIndex
, len
, str
, "length");
1094 if (len
<= HashEverythingLimit
) {
1095 CF_OBJC_VOIDCALL2(str
, "getCharacters:range:", buffer
, CFRangeMake(0, len
));
1098 CF_OBJC_VOIDCALL2(str
, "getCharacters:range:", buffer
, CFRangeMake(0, 32));
1099 CF_OBJC_VOIDCALL2(str
, "getCharacters:range:", buffer
+32, CFRangeMake((len
>> 1) - 16, 32));
1100 CF_OBJC_VOIDCALL2(str
, "getCharacters:range:", buffer
+64, CFRangeMake(len
- 32, 32));
1101 bufLen
= HashEverythingLimit
;
1103 return __CFStrHashCharacters(buffer
, bufLen
, len
);
1106 CFHashCode
__CFStringHash(CFTypeRef cf
) {
1107 /* !!! We do not need an IsString assertion here, as this is called by the CFBase runtime only */
1108 CFStringRef str
= (CFStringRef
)cf
;
1109 const uint8_t *contents
= (uint8_t *)__CFStrContents(str
);
1110 CFIndex len
= __CFStrLength2(str
, contents
);
1112 if (__CFStrIsEightBit(str
)) {
1113 contents
+= __CFStrSkipAnyLengthByte(str
);
1114 return __CFStrHashEightBit(contents
, len
);
1116 return __CFStrHashCharacters((const UniChar
*)contents
, len
, len
);
1121 static CFStringRef
__CFStringCopyDescription(CFTypeRef cf
) {
1122 return CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("<CFString %p [%p]>{contents = \"%@\"}"), cf
, __CFGetAllocator(cf
), cf
);
1125 static CFStringRef
__CFStringCopyFormattingDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
1126 return (CFStringRef
)CFStringCreateCopy(__CFGetAllocator(cf
), (CFStringRef
)cf
);
1129 static CFTypeID __kCFStringTypeID
= _kCFRuntimeNotATypeID
;
1131 typedef CFTypeRef (*CF_STRING_CREATE_COPY
)(CFAllocatorRef alloc
, CFTypeRef theString
);
1133 static const CFRuntimeClass __CFStringClass
= {
1137 (CF_STRING_CREATE_COPY
)CFStringCreateCopy
,
1138 __CFStringDeallocate
,
1141 __CFStringCopyFormattingDescription
,
1142 __CFStringCopyDescription
1145 __private_extern__
void __CFStringInitialize(void) {
1146 __kCFStringTypeID
= _CFRuntimeRegisterClass(&__CFStringClass
);
1149 CFTypeID
CFStringGetTypeID(void) {
1150 return __kCFStringTypeID
;
1154 static Boolean
CFStrIsUnicode(CFStringRef str
) {
1155 CF_OBJC_FUNCDISPATCH0(__kCFStringTypeID
, Boolean
, str
, "_encodingCantBeStoredInEightBitCFString");
1156 return __CFStrIsUnicode(str
);
1161 #define ALLOCATORSFREEFUNC ((CFAllocatorRef)-1)
1163 /* contentsDeallocator indicates how to free the data if it's noCopy == true:
1164 kCFAllocatorNull: don't free
1165 ALLOCATORSFREEFUNC: free with main allocator's free func (don't pass in the real func ptr here)
1166 NULL: default allocator
1167 otherwise it's the allocator that should be used (it will be explicitly stored)
1168 if noCopy == false, then freeFunc should be ALLOCATORSFREEFUNC
1169 hasLengthByte, hasNullByte: refers to bytes; used only if encoding != Unicode
1170 possiblyExternalFormat indicates that the bytes might have BOM and be swapped
1171 tryToReduceUnicode means that the Unicode should be checked to see if it contains just ASCII (and reduce it if so)
1172 numBytes contains the actual number of bytes in "bytes", including Length byte,
1173 BUT not the NULL byte at the end
1174 bytes should not contain BOM characters
1175 !!! Various flags should be combined to reduce number of arguments, if possible
1177 __private_extern__ CFStringRef
__CFStringCreateImmutableFunnel3(
1178 CFAllocatorRef alloc
, const void *bytes
, CFIndex numBytes
, CFStringEncoding encoding
,
1179 Boolean possiblyExternalFormat
, Boolean tryToReduceUnicode
, Boolean hasLengthByte
, Boolean hasNullByte
, Boolean noCopy
,
1180 CFAllocatorRef contentsDeallocator
, UInt32 converterFlags
) {
1182 CFMutableStringRef str
;
1183 CFVarWidthCharBuffer vBuf
;
1185 Boolean useLengthByte
= false;
1186 Boolean useNullByte
= false;
1187 Boolean useInlineData
= false;
1189 #if INSTRUMENT_SHARED_STRINGS
1190 const char *recordedEncoding
;
1191 char encodingBuffer
[128];
1192 if (encoding
== kCFStringEncodingUnicode
) recordedEncoding
= "Unicode";
1193 else if (encoding
== kCFStringEncodingASCII
) recordedEncoding
= "ASCII";
1194 else if (encoding
== kCFStringEncodingUTF8
) recordedEncoding
= "UTF8";
1195 else if (encoding
== kCFStringEncodingMacRoman
) recordedEncoding
= "MacRoman";
1197 snprintf(encodingBuffer
, sizeof(encodingBuffer
), "0x%lX", (unsigned long)encoding
);
1198 recordedEncoding
= encodingBuffer
;
1202 if (alloc
== NULL
) alloc
= __CFGetDefaultAllocator();
1204 if (contentsDeallocator
== ALLOCATORSFREEFUNC
) {
1205 contentsDeallocator
= alloc
;
1206 } else if (contentsDeallocator
== NULL
) {
1207 contentsDeallocator
= __CFGetDefaultAllocator();
1210 if ((NULL
!= kCFEmptyString
) && (numBytes
== 0) && (alloc
== kCFAllocatorSystemDefault
)) { // If we are using the system default allocator, and the string is empty, then use the empty string!
1211 if (noCopy
&& (contentsDeallocator
!= kCFAllocatorNull
)) { // See 2365208... This change was done after Sonata; before we didn't free the bytes at all (leak).
1212 CFAllocatorDeallocate(contentsDeallocator
, (void *)bytes
);
1214 return (CFStringRef
)CFRetain(kCFEmptyString
); // Quick exit; won't catch all empty strings, but most
1217 // At this point, contentsDeallocator is either same as alloc, or kCFAllocatorNull, or something else, but not NULL
1219 vBuf
.shouldFreeChars
= false; // We use this to remember to free the buffer possibly allocated by decode
1221 // Record whether we're starting out with an ASCII-superset string, because we need to know this later for the string ROM; this may get changed later if we successfully convert down from Unicode. We only record this once because __CFCanUseEightBitCFStringForBytes() can be expensive.
1222 Boolean stringSupportsEightBitCFRepresentation
= encoding
!= kCFStringEncodingUnicode
&& __CFCanUseEightBitCFStringForBytes((const uint8_t *)bytes
, numBytes
, encoding
);
1224 // We may also change noCopy within this function if we have to decode the string into an external buffer. We do not want to avoid the use of the string ROM merely because we tried to be efficient and reuse the decoded buffer for the CFString's external storage. Therefore, we use this variable to track whether we actually can ignore the noCopy flag (which may or may not be set anyways).
1225 Boolean stringROMShouldIgnoreNoCopy
= false;
1227 // First check to see if the data needs to be converted...
1228 // ??? We could be more efficient here and in some cases (Unicode data) eliminate a copy
1230 if ((encoding
== kCFStringEncodingUnicode
&& possiblyExternalFormat
) || encoding
!= kCFStringEncodingUnicode
&& ! stringSupportsEightBitCFRepresentation
) {
1231 const void *realBytes
= (uint8_t *) bytes
+ (hasLengthByte
? 1 : 0);
1232 CFIndex realNumBytes
= numBytes
- (hasLengthByte
? 1 : 0);
1233 Boolean usingPassedInMemory
= false;
1235 vBuf
.allocator
= __CFGetDefaultAllocator(); // We don't want to use client's allocator for temp stuff
1236 vBuf
.chars
.unicode
= NULL
; // This will cause the decode function to allocate memory if necessary
1238 if (!__CFStringDecodeByteStream3((const uint8_t *)realBytes
, realNumBytes
, encoding
, false, &vBuf
, &usingPassedInMemory
, converterFlags
)) {
1239 // Note that if the string can't be created, we don't free the buffer, even if there is a contents deallocator. This is on purpose.
1243 encoding
= vBuf
.isASCII
? kCFStringEncodingASCII
: kCFStringEncodingUnicode
;
1245 // Update our flag according to whether the decoded buffer is ASCII
1246 stringSupportsEightBitCFRepresentation
= vBuf
.isASCII
;
1248 if (!usingPassedInMemory
) {
1250 // Because __CFStringDecodeByteStream3() allocated our buffer, it's OK for us to free it if we can get the string from the ROM.
1251 stringROMShouldIgnoreNoCopy
= true;
1253 // Make the parameters fit the new situation
1254 numBytes
= vBuf
.isASCII
? vBuf
.numChars
: (vBuf
.numChars
* sizeof(UniChar
));
1255 hasLengthByte
= hasNullByte
= false;
1257 // Get rid of the original buffer if its not being used
1258 if (noCopy
&& (contentsDeallocator
!= kCFAllocatorNull
)) {
1259 CFAllocatorDeallocate(contentsDeallocator
, (void *)bytes
);
1261 contentsDeallocator
= alloc
; // At this point we are using the string's allocator, as the original buffer is gone...
1263 // See if we can reuse any storage the decode func might have allocated
1264 // We do this only for Unicode, as otherwise we would not have NULL and Length bytes
1266 if (vBuf
.shouldFreeChars
&& (alloc
== vBuf
.allocator
) && encoding
== kCFStringEncodingUnicode
) {
1267 vBuf
.shouldFreeChars
= false; // Transferring ownership to the CFString
1268 bytes
= CFAllocatorReallocate(vBuf
.allocator
, (void *)vBuf
.chars
.unicode
, numBytes
, 0); // Tighten up the storage
1270 #if INSTRUMENT_SHARED_STRINGS
1271 if (encoding
== kCFStringEncodingASCII
) recordedEncoding
= "ForeignASCII-NoCopy";
1272 else recordedEncoding
= "ForeignUnicode-NoCopy";
1275 #if INSTRUMENT_SHARED_STRINGS
1276 if (encoding
== kCFStringEncodingASCII
) recordedEncoding
= "ForeignASCII-Copy";
1277 else recordedEncoding
= "ForeignUnicode-Copy";
1279 bytes
= vBuf
.chars
.unicode
;
1280 noCopy
= false; // Can't do noCopy anymore
1281 // If vBuf.shouldFreeChars is true, the buffer will be freed as intended near the end of this func
1286 // At this point, all necessary input arguments have been changed to reflect the new state
1288 } else if (encoding
== kCFStringEncodingUnicode
&& tryToReduceUnicode
) { // Check to see if we can reduce Unicode to ASCII
1290 CFIndex len
= numBytes
/ sizeof(UniChar
);
1291 Boolean allASCII
= true;
1293 for (cnt
= 0; cnt
< len
; cnt
++) if (((const UniChar
*)bytes
)[cnt
] > 127) {
1298 if (allASCII
) { // Yes we can!
1300 Boolean newHasLengthByte
= __CFCanUseLengthByte(len
);
1301 numBytes
= (len
+ 1 + (newHasLengthByte
? 1 : 0)) * sizeof(uint8_t); // NULL and possible length byte
1302 // See if we can use that temporary local buffer in vBuf...
1303 if (numBytes
>= __kCFVarWidthLocalBufferSize
) {
1304 mem
= ptr
= (uint8_t *)CFAllocatorAllocate(alloc
, numBytes
, 0);
1305 if (__CFOASafe
) __CFSetLastAllocationEventName(mem
, "CFString (store)");
1307 mem
= ptr
= (uint8_t *)(vBuf
.localBuffer
);
1309 if (mem
) { // If we can't allocate memory for some reason, use what we had (that is, as if we didn't have all ASCII)
1310 // Copy the Unicode bytes into the new ASCII buffer
1311 hasLengthByte
= newHasLengthByte
;
1313 if (hasLengthByte
) *ptr
++ = (uint8_t)len
;
1314 for (cnt
= 0; cnt
< len
; cnt
++) ptr
[cnt
] = (uint8_t)(((const UniChar
*)bytes
)[cnt
]);
1316 if (noCopy
&& (contentsDeallocator
!= kCFAllocatorNull
)) {
1317 CFAllocatorDeallocate(contentsDeallocator
, (void *)bytes
);
1319 // Now make everything look like we had an ASCII buffer to start with
1321 encoding
= kCFStringEncodingASCII
;
1322 contentsDeallocator
= alloc
; // At this point we are using the string's allocator, as the original buffer is gone...
1323 noCopy
= (numBytes
>= __kCFVarWidthLocalBufferSize
); // If we had to allocate it, make sure it's kept around
1324 numBytes
--; // Should not contain the NULL byte at end...
1325 stringSupportsEightBitCFRepresentation
= true; // We're ASCII now!
1326 stringROMShouldIgnoreNoCopy
= true; // We allocated this buffer, so we should feel free to get rid of it if we can use the string ROM
1327 #if INSTRUMENT_SHARED_STRINGS
1328 recordedEncoding
= "U->A";
1333 // At this point, all necessary input arguments have been changed to reflect the new state
1336 // Now determine the necessary size
1337 #if INSTRUMENT_SHARED_STRINGS || USE_STRING_ROM
1338 Boolean stringSupportsROM
= stringSupportsEightBitCFRepresentation
;
1341 #if INSTRUMENT_SHARED_STRINGS
1342 if (stringSupportsROM
) {
1343 const void *realBytes
= (uint8_t *) bytes
+ (hasLengthByte
? 1 : 0);
1344 CFIndex realNumBytes
= numBytes
- !! hasLengthByte
;
1345 __CFRecordStringAllocationEvent(recordedEncoding
, realBytes
, realNumBytes
);
1349 CFStringRef romResult
= NULL
;
1353 if (stringSupportsROM
) {
1354 // Disable the string ROM if necessary
1355 static char sDisableStringROM
= -1;
1356 if (sDisableStringROM
== -1) sDisableStringROM
= !! __CFgetenv("CFStringDisableROM");
1358 if (sDisableStringROM
== 0) romResult
= _CFSearchStringROM(bytes
+ !! hasLengthByte
, numBytes
- !! hasLengthByte
);
1360 /* if we get a result from our ROM, and noCopy is set, then deallocate the buffer immediately */
1362 if (noCopy
&& (contentsDeallocator
!= kCFAllocatorNull
)) {
1363 CFAllocatorDeallocate(contentsDeallocator
, (void *)bytes
);
1366 /* these don't get used again, but clear them for consistency */
1370 /* set our result to the ROM result which is not really mutable, of course, but that's OK because we don't try to modify it. */
1371 str
= (CFMutableStringRef
)romResult
;
1376 // Now determine the necessary size
1380 size
= sizeof(void *); // Pointer to the buffer
1381 if (contentsDeallocator
!= alloc
&& contentsDeallocator
!= kCFAllocatorNull
) {
1382 size
+= sizeof(void *); // The contentsDeallocator
1384 if (!hasLengthByte
) size
+= sizeof(CFIndex
); // Explicit length
1385 useLengthByte
= hasLengthByte
;
1386 useNullByte
= hasNullByte
;
1388 } else { // Inline data; reserve space for it
1390 useInlineData
= true;
1393 if (hasLengthByte
|| (encoding
!= kCFStringEncodingUnicode
&& __CFCanUseLengthByte(numBytes
))) {
1394 useLengthByte
= true;
1395 if (!hasLengthByte
) size
+= 1;
1397 size
+= sizeof(CFIndex
); // Explicit length
1399 if (hasNullByte
|| encoding
!= kCFStringEncodingUnicode
) {
1405 #ifdef STRING_SIZE_STATS
1406 // Dump alloced CFString size info every so often
1408 static unsigned sizes
[256] = {0};
1409 int allocedSize
= size
+ sizeof(CFRuntimeBase
);
1410 if (allocedSize
< 255) sizes
[allocedSize
]++; else sizes
[255]++;
1411 if ((++cnt
% 1000) == 0) {
1412 printf ("\nTotal: %d\n", cnt
);
1413 int i
; for (i
= 0; i
< 256; i
++) printf("%03d: %5d%s", i
, sizes
[i
], ((i
% 8) == 7) ? "\n" : " ");
1417 // Finally, allocate!
1419 str
= (CFMutableStringRef
)_CFRuntimeCreateInstance(alloc
, __kCFStringTypeID
, size
, NULL
);
1421 if (__CFOASafe
) __CFSetLastAllocationEventName(str
, "CFString (immutable)");
1423 __CFStrSetInfoBits(str
,
1424 (useInlineData
? __kCFHasInlineContents
: (contentsDeallocator
== alloc
? __kCFNotInlineContentsDefaultFree
: (contentsDeallocator
== kCFAllocatorNull
? __kCFNotInlineContentsNoFree
: __kCFNotInlineContentsCustomFree
))) |
1425 ((encoding
== kCFStringEncodingUnicode
) ? __kCFIsUnicode
: 0) |
1426 (useNullByte
? __kCFHasNullByte
: 0) |
1427 (useLengthByte
? __kCFHasLengthByte
: 0));
1429 if (!useLengthByte
) {
1430 CFIndex length
= numBytes
- (hasLengthByte
? 1 : 0);
1431 if (encoding
== kCFStringEncodingUnicode
) length
/= sizeof(UniChar
);
1432 __CFStrSetExplicitLength(str
, length
);
1435 if (useInlineData
) {
1436 uint8_t *contents
= (uint8_t *)__CFStrContents(str
);
1437 if (useLengthByte
&& !hasLengthByte
) *contents
++ = (uint8_t)numBytes
;
1438 memmove(contents
, bytes
, numBytes
);
1439 if (useNullByte
) contents
[numBytes
] = 0;
1441 __CFStrSetContentPtr(str
, bytes
);
1442 if (contentsDeallocator
!= alloc
&& contentsDeallocator
!= kCFAllocatorNull
) __CFStrSetContentsDeallocator(str
, (CFAllocatorRef
)CFRetain(contentsDeallocator
));
1445 if (noCopy
&& (contentsDeallocator
!= kCFAllocatorNull
)) {
1446 CFAllocatorDeallocate(contentsDeallocator
, (void *)bytes
);
1450 if (vBuf
.shouldFreeChars
) CFAllocatorDeallocate(vBuf
.allocator
, (void *)bytes
);
1455 /* !!! __CFStringCreateImmutableFunnel2() is kept around for compatibility; it should be deprecated
1457 CFStringRef
__CFStringCreateImmutableFunnel2(
1458 CFAllocatorRef alloc
, const void *bytes
, CFIndex numBytes
, CFStringEncoding encoding
,
1459 Boolean possiblyExternalFormat
, Boolean tryToReduceUnicode
, Boolean hasLengthByte
, Boolean hasNullByte
, Boolean noCopy
,
1460 CFAllocatorRef contentsDeallocator
) {
1461 return __CFStringCreateImmutableFunnel3(alloc
, bytes
, numBytes
, encoding
, possiblyExternalFormat
, tryToReduceUnicode
, hasLengthByte
, hasNullByte
, noCopy
, contentsDeallocator
, 0);
1466 CFStringRef
CFStringCreateWithPascalString(CFAllocatorRef alloc
, ConstStringPtr pStr
, CFStringEncoding encoding
) {
1467 CFIndex len
= (CFIndex
)(*(uint8_t *)pStr
);
1468 return __CFStringCreateImmutableFunnel3(alloc
, pStr
, len
+1, encoding
, false, false, true, false, false, ALLOCATORSFREEFUNC
, 0);
1472 CFStringRef
CFStringCreateWithCString(CFAllocatorRef alloc
, const char *cStr
, CFStringEncoding encoding
) {
1473 CFIndex len
= strlen(cStr
);
1474 return __CFStringCreateImmutableFunnel3(alloc
, cStr
, len
, encoding
, false, false, false, true, false, ALLOCATORSFREEFUNC
, 0);
1477 CFStringRef
CFStringCreateWithPascalStringNoCopy(CFAllocatorRef alloc
, ConstStringPtr pStr
, CFStringEncoding encoding
, CFAllocatorRef contentsDeallocator
) {
1478 CFIndex len
= (CFIndex
)(*(uint8_t *)pStr
);
1479 return __CFStringCreateImmutableFunnel3(alloc
, pStr
, len
+1, encoding
, false, false, true, false, true, contentsDeallocator
, 0);
1483 CFStringRef
CFStringCreateWithCStringNoCopy(CFAllocatorRef alloc
, const char *cStr
, CFStringEncoding encoding
, CFAllocatorRef contentsDeallocator
) {
1484 CFIndex len
= strlen(cStr
);
1485 return __CFStringCreateImmutableFunnel3(alloc
, cStr
, len
, encoding
, false, false, false, true, true, contentsDeallocator
, 0);
1489 CFStringRef
CFStringCreateWithCharacters(CFAllocatorRef alloc
, const UniChar
*chars
, CFIndex numChars
) {
1490 return __CFStringCreateImmutableFunnel3(alloc
, chars
, numChars
* sizeof(UniChar
), kCFStringEncodingUnicode
, false, true, false, false, false, ALLOCATORSFREEFUNC
, 0);
1494 CFStringRef
CFStringCreateWithCharactersNoCopy(CFAllocatorRef alloc
, const UniChar
*chars
, CFIndex numChars
, CFAllocatorRef contentsDeallocator
) {
1495 return __CFStringCreateImmutableFunnel3(alloc
, chars
, numChars
* sizeof(UniChar
), kCFStringEncodingUnicode
, false, false, false, false, true, contentsDeallocator
, 0);
1499 CFStringRef
CFStringCreateWithBytes(CFAllocatorRef alloc
, const uint8_t *bytes
, CFIndex numBytes
, CFStringEncoding encoding
, Boolean externalFormat
) {
1500 return __CFStringCreateImmutableFunnel3(alloc
, bytes
, numBytes
, encoding
, externalFormat
, true, false, false, false, ALLOCATORSFREEFUNC
, 0);
1503 CFStringRef
_CFStringCreateWithBytesNoCopy(CFAllocatorRef alloc
, const uint8_t *bytes
, CFIndex numBytes
, CFStringEncoding encoding
, Boolean externalFormat
, CFAllocatorRef contentsDeallocator
) {
1504 return __CFStringCreateImmutableFunnel3(alloc
, bytes
, numBytes
, encoding
, externalFormat
, true, false, false, true, contentsDeallocator
, 0);
1507 CFStringRef
CFStringCreateWithBytesNoCopy(CFAllocatorRef alloc
, const uint8_t *bytes
, CFIndex numBytes
, CFStringEncoding encoding
, Boolean externalFormat
, CFAllocatorRef contentsDeallocator
) {
1508 return __CFStringCreateImmutableFunnel3(alloc
, bytes
, numBytes
, encoding
, externalFormat
, true, false, false, true, contentsDeallocator
, 0);
1511 CFStringRef
CFStringCreateWithFormatAndArguments(CFAllocatorRef alloc
, CFDictionaryRef formatOptions
, CFStringRef format
, va_list arguments
) {
1512 return _CFStringCreateWithFormatAndArgumentsAux(alloc
, NULL
, formatOptions
, format
, arguments
);
1515 CFStringRef
_CFStringCreateWithFormatAndArgumentsAux(CFAllocatorRef alloc
, CFStringRef (*copyDescFunc
)(void *, const void *), CFDictionaryRef formatOptions
, CFStringRef format
, va_list arguments
) {
1517 CFMutableStringRef outputString
= CFStringCreateMutable(__CFGetDefaultAllocator(), 0); //should use alloc if no copy/release
1518 __CFStrSetDesiredCapacity(outputString
, 120); // Given this will be tightened later, choosing a larger working string is fine
1519 _CFStringAppendFormatAndArgumentsAux(outputString
, copyDescFunc
, formatOptions
, format
, arguments
);
1520 // ??? copy/release should not be necessary here -- just make immutable, compress if possible
1521 // (However, this does make the string inline, and cause the supplied allocator to be used...)
1522 str
= (CFStringRef
)CFStringCreateCopy(alloc
, outputString
);
1523 CFRelease(outputString
);
1527 CFStringRef
CFStringCreateWithFormat(CFAllocatorRef alloc
, CFDictionaryRef formatOptions
, CFStringRef format
, ...) {
1531 va_start(argList
, format
);
1532 result
= CFStringCreateWithFormatAndArguments(alloc
, formatOptions
, format
, argList
);
1538 CFStringRef
CFStringCreateWithSubstring(CFAllocatorRef alloc
, CFStringRef str
, CFRange range
) {
1539 // CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID, CFStringRef , str, "_createSubstringWithRange:", CFRangeMake(range.location, range.length));
1541 __CFAssertIsString(str
);
1542 __CFAssertRangeIsInStringBounds(str
, range
.location
, range
.length
);
1544 if ((range
.location
== 0) && (range
.length
== __CFStrLength(str
))) { /* The substring is the whole string... */
1545 return (CFStringRef
)CFStringCreateCopy(alloc
, str
);
1546 } else if (__CFStrIsEightBit(str
)) {
1547 const uint8_t *contents
= (const uint8_t *)__CFStrContents(str
);
1548 return __CFStringCreateImmutableFunnel3(alloc
, contents
+ range
.location
+ __CFStrSkipAnyLengthByte(str
), range
.length
, __CFStringGetEightBitStringEncoding(), false, false, false, false, false, ALLOCATORSFREEFUNC
, 0);
1550 const UniChar
*contents
= (UniChar
*)__CFStrContents(str
);
1551 return __CFStringCreateImmutableFunnel3(alloc
, contents
+ range
.location
, range
.length
* sizeof(UniChar
), kCFStringEncodingUnicode
, false, true, false, false, false, ALLOCATORSFREEFUNC
, 0);
1555 CFStringRef
CFStringCreateCopy(CFAllocatorRef alloc
, CFStringRef str
) {
1556 // CF_OBJC_FUNCDISPATCH0(__kCFStringTypeID, CFStringRef, str, "copy");
1558 __CFAssertIsString(str
);
1559 if (!__CFStrIsMutable((CFStringRef
)str
) && // If the string is not mutable
1560 ((alloc
? alloc
: __CFGetDefaultAllocator()) == __CFGetAllocator(str
)) && // and it has the same allocator as the one we're using
1561 (__CFStrIsInline((CFStringRef
)str
) || __CFStrFreeContentsWhenDone((CFStringRef
)str
) || __CFStrIsConstant((CFStringRef
)str
))) { // and the characters are inline, or are owned by the string, or the string is constant
1562 CFRetain(str
); // Then just retain instead of making a true copy
1565 if (__CFStrIsEightBit((CFStringRef
)str
)) {
1566 const uint8_t *contents
= (const uint8_t *)__CFStrContents((CFStringRef
)str
);
1567 return __CFStringCreateImmutableFunnel3(alloc
, contents
+ __CFStrSkipAnyLengthByte((CFStringRef
)str
), __CFStrLength2((CFStringRef
)str
, contents
), __CFStringGetEightBitStringEncoding(), false, false, false, false, false, ALLOCATORSFREEFUNC
, 0);
1569 const UniChar
*contents
= (const UniChar
*)__CFStrContents((CFStringRef
)str
);
1570 return __CFStringCreateImmutableFunnel3(alloc
, contents
, __CFStrLength2((CFStringRef
)str
, contents
) * sizeof(UniChar
), kCFStringEncodingUnicode
, false, true, false, false, false, ALLOCATORSFREEFUNC
, 0);
1576 /*** Constant string stuff... ***/
1578 /* Table which holds constant strings created with CFSTR, when -fconstant-cfstrings option is not used. These dynamically created constant strings are stored in constantStringTable. The keys are the 8-bit constant C-strings from the compiler; the values are the CFStrings created for them. _CFSTRLock protects this table.
1580 static CFMutableDictionaryRef constantStringTable
= NULL
;
1581 static CFSpinLock_t _CFSTRLock
= CFSpinLockInit
;
1583 static CFStringRef
__cStrCopyDescription(const void *ptr
) {
1584 return CFStringCreateWithCStringNoCopy(kCFAllocatorSystemDefault
, (const char *)ptr
, __CFStringGetEightBitStringEncoding(), kCFAllocatorNull
);
1587 static Boolean
__cStrEqual(const void *ptr1
, const void *ptr2
) {
1588 return (strcmp((const char *)ptr1
, (const char *)ptr2
) == 0);
1591 static CFHashCode
__cStrHash(const void *ptr
) {
1592 // It doesn't quite matter if we convert to Unicode correctly, as long as we do it consistently
1593 const char *cStr
= (const char *)ptr
;
1594 CFIndex len
= strlen(cStr
);
1595 CFHashCode result
= 0;
1596 if (len
<= 4) { // All chars
1598 while (cnt
--) result
+= (result
<< 8) + *cStr
++;
1599 } else { // First and last 2 chars
1600 result
+= (result
<< 8) + cStr
[0];
1601 result
+= (result
<< 8) + cStr
[1];
1602 result
+= (result
<< 8) + cStr
[len
-2];
1603 result
+= (result
<< 8) + cStr
[len
-1];
1605 result
+= (result
<< (len
& 31));
1610 CFStringRef
__CFStringMakeConstantString(const char *cStr
) {
1613 // StringTest checks that we share kCFEmptyString, which is defeated by constantStringAllocatorForDebugging
1614 if ('\0' == *cStr
) return kCFEmptyString
;
1616 if (constantStringTable
== NULL
) {
1617 CFDictionaryKeyCallBacks constantStringCallBacks
= {0, NULL
, NULL
, __cStrCopyDescription
, __cStrEqual
, __cStrHash
};
1618 CFDictionaryValueCallBacks constantStringValueCallBacks
= kCFTypeDictionaryValueCallBacks
;
1619 constantStringValueCallBacks
.equal
= NULL
; // So that we only find strings that are ==
1620 CFMutableDictionaryRef table
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &constantStringCallBacks
, &constantStringValueCallBacks
);
1621 _CFDictionarySetCapacity(table
, 2500); // avoid lots of rehashing
1622 __CFSpinLock(&_CFSTRLock
);
1623 if (constantStringTable
== NULL
) constantStringTable
= table
;
1624 __CFSpinUnlock(&_CFSTRLock
);
1625 if (constantStringTable
!= table
) CFRelease(table
);
1628 __CFSpinLock(&_CFSTRLock
);
1629 if ((result
= (CFStringRef
)CFDictionaryGetValue(constantStringTable
, cStr
))) {
1630 __CFSpinUnlock(&_CFSTRLock
);
1632 __CFSpinUnlock(&_CFSTRLock
);
1636 Boolean isASCII
= true;
1637 // Given this code path is rarer these days, OK to do this extra work to verify the strings
1638 const char *tmp
= cStr
;
1640 if (*(tmp
++) & 0x80) {
1646 CFMutableStringRef ms
= CFStringCreateMutable(kCFAllocatorSystemDefault
, 0);
1649 CFStringAppendFormat(ms
, NULL
, (*tmp
& 0x80) ? CFSTR("\\%3o") : CFSTR("%1c"), *tmp
);
1652 CFLog(kCFLogLevelWarning
, CFSTR("WARNING: CFSTR(\"%@\") has non-7 bit chars, interpreting using MacOS Roman encoding for now, but this will change. Please eliminate usages of non-7 bit chars (including escaped characters above \\177 octal) in CFSTR()."), ms
);
1655 // Treat non-7 bit chars in CFSTR() as MacOSRoman, for compatibility
1656 result
= CFStringCreateWithCString(kCFAllocatorSystemDefault
, cStr
, kCFStringEncodingMacRoman
);
1657 if (result
== NULL
) {
1658 CFLog(__kCFLogAssertion
, CFSTR("Can't interpret CFSTR() as MacOS Roman, crashing"));
1661 if (__CFOASafe
) __CFSetLastAllocationEventName((void *)result
, "CFString (CFSTR)");
1662 if (__CFStrIsEightBit(result
)) {
1663 key
= (char *)__CFStrContents(result
) + __CFStrSkipAnyLengthByte(result
);
1664 } else { // For some reason the string is not 8-bit!
1665 key
= (char *)CFAllocatorAllocate(kCFAllocatorSystemDefault
, strlen(cStr
) + 1, 0);
1666 if (__CFOASafe
) __CFSetLastAllocationEventName((void *)key
, "CFString (CFSTR key)");
1667 strlcpy(key
, cStr
, strlen(cStr
) + 1); // !!! We will leak this, if the string is removed from the table (or table is freed)
1671 CFStringRef resultToBeReleased
= result
;
1673 __CFSpinLock(&_CFSTRLock
);
1674 count
= CFDictionaryGetCount(constantStringTable
);
1675 CFDictionaryAddValue(constantStringTable
, key
, result
);
1676 if (CFDictionaryGetCount(constantStringTable
) == count
) { // add did nothing, someone already put it there
1677 result
= (CFStringRef
)CFDictionaryGetValue(constantStringTable
, key
);
1680 ((struct __CFString
*)result
)->base
._rc
= 0;
1682 ((struct __CFString
*)result
)->base
._cfinfo
[CF_RC_BITS
] = 0;
1685 __CFSpinUnlock(&_CFSTRLock
);
1686 // This either eliminates the extra retain on the freshly created string, or frees it, if it was actually not inserted into the table
1687 CFRelease(resultToBeReleased
);
1695 static Boolean
__CFStrIsConstantString(CFStringRef str
) {
1696 Boolean found
= false;
1697 if (constantStringTable
) {
1698 __CFSpinLock(&_CFSTRLock
);
1699 found
= CFDictionaryContainsValue(constantStringTable
, str
);
1700 __CFSpinUnlock(&_CFSTRLock
);
1707 #if DEPLOYMENT_TARGET_WINDOWS
1708 void __CFStringCleanup (void) {
1709 /* in case library is unloaded, release store for the constant string table */
1710 if (constantStringTable
!= NULL
) {
1712 __CFConstantStringTableBeingFreed
= true;
1713 CFRelease(constantStringTable
);
1714 __CFConstantStringTableBeingFreed
= false;
1716 CFRelease(constantStringTable
);
1718 constantStringTable
= NULL
;
1724 // Can pass in NSString as replacement string
1725 // Call with numRanges > 0, and incrementing ranges
1727 static void __CFStringReplaceMultiple(CFMutableStringRef str
, CFRange
*ranges
, CFIndex numRanges
, CFStringRef replacement
) {
1729 CFStringRef copy
= NULL
;
1730 if (replacement
== str
) copy
= replacement
= CFStringCreateCopy(kCFAllocatorSystemDefault
, replacement
); // Very special and hopefully rare case
1731 CFIndex replacementLength
= CFStringGetLength(replacement
);
1733 __CFStringChangeSizeMultiple(str
, ranges
, numRanges
, replacementLength
, (replacementLength
> 0) && CFStrIsUnicode(replacement
));
1735 if (__CFStrIsUnicode(str
)) {
1736 UniChar
*contents
= (UniChar
*)__CFStrContents(str
);
1737 UniChar
*firstReplacement
= contents
+ ranges
[0].location
;
1738 // Extract the replacementString into the first location, then copy from there
1739 CFStringGetCharacters(replacement
, CFRangeMake(0, replacementLength
), firstReplacement
);
1740 for (cnt
= 1; cnt
< numRanges
; cnt
++) {
1741 // The ranges are in terms of the original string; so offset by the change in length due to insertion
1742 contents
+= replacementLength
- ranges
[cnt
- 1].length
;
1743 memmove(contents
+ ranges
[cnt
].location
, firstReplacement
, replacementLength
* sizeof(UniChar
));
1746 uint8_t *contents
= (uint8_t *)__CFStrContents(str
);
1747 uint8_t *firstReplacement
= contents
+ ranges
[0].location
+ __CFStrSkipAnyLengthByte(str
);
1748 // Extract the replacementString into the first location, then copy from there
1749 CFStringGetBytes(replacement
, CFRangeMake(0, replacementLength
), __CFStringGetEightBitStringEncoding(), 0, false, firstReplacement
, replacementLength
, NULL
);
1750 contents
+= __CFStrSkipAnyLengthByte(str
); // Now contents will simply track the location to insert next string into
1751 for (cnt
= 1; cnt
< numRanges
; cnt
++) {
1752 // The ranges are in terms of the original string; so offset by the change in length due to insertion
1753 contents
+= replacementLength
- ranges
[cnt
- 1].length
;
1754 memmove(contents
+ ranges
[cnt
].location
, firstReplacement
, replacementLength
);
1757 if (copy
) CFRelease(copy
);
1760 // Can pass in NSString as replacement string
1762 CF_INLINE
void __CFStringReplace(CFMutableStringRef str
, CFRange range
, CFStringRef replacement
) {
1763 CFStringRef copy
= NULL
;
1764 if (replacement
== str
) copy
= replacement
= (CFStringRef
)CFStringCreateCopy(kCFAllocatorSystemDefault
, replacement
); // Very special and hopefully rare case
1765 CFIndex replacementLength
= CFStringGetLength(replacement
);
1767 __CFStringChangeSize(str
, range
, replacementLength
, (replacementLength
> 0) && CFStrIsUnicode(replacement
));
1769 if (__CFStrIsUnicode(str
)) {
1770 UniChar
*contents
= (UniChar
*)__CFStrContents(str
);
1771 CFStringGetCharacters(replacement
, CFRangeMake(0, replacementLength
), contents
+ range
.location
);
1773 uint8_t *contents
= (uint8_t *)__CFStrContents(str
);
1774 CFStringGetBytes(replacement
, CFRangeMake(0, replacementLength
), __CFStringGetEightBitStringEncoding(), 0, false, contents
+ range
.location
+ __CFStrSkipAnyLengthByte(str
), replacementLength
, NULL
);
1777 if (copy
) CFRelease(copy
);
1780 /* If client does not provide a minimum capacity
1782 #define DEFAULTMINCAPACITY 32
1784 CF_INLINE CFMutableStringRef
__CFStringCreateMutableFunnel(CFAllocatorRef alloc
, CFIndex maxLength
, UInt32 additionalInfoBits
) {
1785 CFMutableStringRef str
;
1786 Boolean hasExternalContentsAllocator
= (additionalInfoBits
& __kCFHasContentsAllocator
) ? true : false;
1788 if (alloc
== NULL
) alloc
= __CFGetDefaultAllocator();
1790 // Note that if there is an externalContentsAllocator, then we also have the storage for the string allocator...
1791 str
= (CFMutableStringRef
)_CFRuntimeCreateInstance(alloc
, __kCFStringTypeID
, sizeof(struct __notInlineMutable
) - (hasExternalContentsAllocator
? 0 : sizeof(CFAllocatorRef
)), NULL
);
1793 if (__CFOASafe
) __CFSetLastAllocationEventName(str
, "CFString (mutable)");
1795 __CFStrSetInfoBits(str
, __kCFIsMutable
| additionalInfoBits
);
1796 str
->variants
.notInlineMutable
.buffer
= NULL
;
1797 __CFStrSetExplicitLength(str
, 0);
1798 str
->variants
.notInlineMutable
.hasGap
= str
->variants
.notInlineMutable
.isFixedCapacity
= str
->variants
.notInlineMutable
.isExternalMutable
= str
->variants
.notInlineMutable
.capacityProvidedExternally
= 0;
1799 if (maxLength
!= 0) __CFStrSetIsFixed(str
);
1800 __CFStrSetDesiredCapacity(str
, (maxLength
== 0) ? DEFAULTMINCAPACITY
: maxLength
);
1801 __CFStrSetCapacity(str
, 0);
1806 CFMutableStringRef
CFStringCreateMutableWithExternalCharactersNoCopy(CFAllocatorRef alloc
, UniChar
*chars
, CFIndex numChars
, CFIndex capacity
, CFAllocatorRef externalCharactersAllocator
) {
1807 CFOptionFlags contentsAllocationBits
= externalCharactersAllocator
? ((externalCharactersAllocator
== kCFAllocatorNull
) ? __kCFNotInlineContentsNoFree
: __kCFHasContentsAllocator
) : __kCFNotInlineContentsDefaultFree
;
1808 CFMutableStringRef string
= __CFStringCreateMutableFunnel(alloc
, 0, contentsAllocationBits
| __kCFIsUnicode
);
1810 __CFStrSetIsExternalMutable(string
);
1811 if (contentsAllocationBits
== __kCFHasContentsAllocator
) __CFStrSetContentsAllocator(string
, (CFAllocatorRef
)CFRetain(externalCharactersAllocator
));
1812 CFStringSetExternalCharactersNoCopy(string
, chars
, numChars
, capacity
);
1817 CFMutableStringRef
CFStringCreateMutable(CFAllocatorRef alloc
, CFIndex maxLength
) {
1818 return __CFStringCreateMutableFunnel(alloc
, maxLength
, __kCFNotInlineContentsDefaultFree
);
1821 CFMutableStringRef
CFStringCreateMutableCopy(CFAllocatorRef alloc
, CFIndex maxLength
, CFStringRef string
) {
1822 CFMutableStringRef newString
;
1824 // CF_OBJC_FUNCDISPATCH0(__kCFStringTypeID, CFMutableStringRef, string, "mutableCopy");
1826 __CFAssertIsString(string
);
1828 newString
= CFStringCreateMutable(alloc
, maxLength
);
1829 __CFStringReplace(newString
, CFRangeMake(0, 0), string
);
1835 __private_extern__
void _CFStrSetDesiredCapacity(CFMutableStringRef str
, CFIndex len
) {
1836 __CFAssertIsStringAndMutable(str
);
1837 __CFStrSetDesiredCapacity(str
, len
);
1841 /* This one is for CF
1843 CFIndex
CFStringGetLength(CFStringRef str
) {
1844 CF_OBJC_FUNCDISPATCH0(__kCFStringTypeID
, CFIndex
, str
, "length");
1846 __CFAssertIsString(str
);
1847 return __CFStrLength(str
);
1850 /* This one is for NSCFString; it does not ObjC dispatch or assertion check
1852 CFIndex
_CFStringGetLength2(CFStringRef str
) {
1853 return __CFStrLength(str
);
1857 /* Guts of CFStringGetCharacterAtIndex(); called from the two functions below. Don't call it from elsewhere.
1859 CF_INLINE UniChar
__CFStringGetCharacterAtIndexGuts(CFStringRef str
, CFIndex idx
, const uint8_t *contents
) {
1860 if (__CFStrIsEightBit(str
)) {
1861 contents
+= __CFStrSkipAnyLengthByte(str
);
1863 if (!__CFCharToUniCharFunc
&& (contents
[idx
] >= 128)) {
1864 // Can't do log here, as it might be too early
1865 fprintf(stderr
, "Warning: CFStringGetCharacterAtIndex() attempted on CFString containing high bytes before properly initialized to do so\n");
1868 return __CFCharToUniCharTable
[contents
[idx
]];
1871 return ((UniChar
*)contents
)[idx
];
1874 /* This one is for the CF API
1876 UniChar
CFStringGetCharacterAtIndex(CFStringRef str
, CFIndex idx
) {
1877 CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID
, UniChar
, str
, "characterAtIndex:", idx
);
1879 __CFAssertIsString(str
);
1880 __CFAssertIndexIsInStringBounds(str
, idx
);
1881 return __CFStringGetCharacterAtIndexGuts(str
, idx
, (const uint8_t *)__CFStrContents(str
));
1884 /* This one is for NSCFString usage; it doesn't do ObjC dispatch; but it does do range check
1886 int _CFStringCheckAndGetCharacterAtIndex(CFStringRef str
, CFIndex idx
, UniChar
*ch
) {
1887 const uint8_t *contents
= (const uint8_t *)__CFStrContents(str
);
1888 if (idx
>= __CFStrLength2(str
, contents
) && __CFStringNoteErrors()) return _CFStringErrBounds
;
1889 *ch
= __CFStringGetCharacterAtIndexGuts(str
, idx
, contents
);
1890 return _CFStringErrNone
;
1894 /* Guts of CFStringGetCharacters(); called from the two functions below. Don't call it from elsewhere.
1896 CF_INLINE
void __CFStringGetCharactersGuts(CFStringRef str
, CFRange range
, UniChar
*buffer
, const uint8_t *contents
) {
1897 if (__CFStrIsEightBit(str
)) {
1898 __CFStrConvertBytesToUnicode(((uint8_t *)contents
) + (range
.location
+ __CFStrSkipAnyLengthByte(str
)), buffer
, range
.length
);
1900 const UniChar
*uContents
= ((UniChar
*)contents
) + range
.location
;
1901 memmove(buffer
, uContents
, range
.length
* sizeof(UniChar
));
1905 /* This one is for the CF API
1907 void CFStringGetCharacters(CFStringRef str
, CFRange range
, UniChar
*buffer
) {
1908 CF_OBJC_FUNCDISPATCH2(__kCFStringTypeID
, void, str
, "getCharacters:range:", buffer
, CFRangeMake(range
.location
, range
.length
));
1910 __CFAssertIsString(str
);
1911 __CFAssertRangeIsInStringBounds(str
, range
.location
, range
.length
);
1912 __CFStringGetCharactersGuts(str
, range
, buffer
, (const uint8_t *)__CFStrContents(str
));
1915 /* This one is for NSCFString usage; it doesn't do ObjC dispatch; but it does do range check
1917 int _CFStringCheckAndGetCharacters(CFStringRef str
, CFRange range
, UniChar
*buffer
) {
1918 const uint8_t *contents
= (const uint8_t *)__CFStrContents(str
);
1919 if (range
.location
+ range
.length
> __CFStrLength2(str
, contents
) && __CFStringNoteErrors()) return _CFStringErrBounds
;
1920 __CFStringGetCharactersGuts(str
, range
, buffer
, contents
);
1921 return _CFStringErrNone
;
1925 CFIndex
CFStringGetBytes(CFStringRef str
, CFRange range
, CFStringEncoding encoding
, uint8_t lossByte
, Boolean isExternalRepresentation
, uint8_t *buffer
, CFIndex maxBufLen
, CFIndex
*usedBufLen
) {
1927 /* No objc dispatch needed here since __CFStringEncodeByteStream works with both CFString and NSString */
1928 __CFAssertIsNotNegative(maxBufLen
);
1930 if (!CF_IS_OBJC(__kCFStringTypeID
, str
)) { // If we can grope the ivars, let's do it...
1931 __CFAssertIsString(str
);
1932 __CFAssertRangeIsInStringBounds(str
, range
.location
, range
.length
);
1934 if (__CFStrIsEightBit(str
) && ((__CFStringGetEightBitStringEncoding() == encoding
) || (__CFStringGetEightBitStringEncoding() == kCFStringEncodingASCII
&& __CFStringEncodingIsSupersetOfASCII(encoding
)))) { // Requested encoding is equal to the encoding in string
1935 const unsigned char *contents
= (const unsigned char *)__CFStrContents(str
);
1936 CFIndex cLength
= range
.length
;
1939 if (cLength
> maxBufLen
) cLength
= maxBufLen
;
1940 memmove(buffer
, contents
+ __CFStrSkipAnyLengthByte(str
) + range
.location
, cLength
);
1942 if (usedBufLen
) *usedBufLen
= cLength
;
1948 return __CFStringEncodeByteStream(str
, range
.location
, range
.length
, isExternalRepresentation
, encoding
, lossByte
, buffer
, maxBufLen
, usedBufLen
);
1952 ConstStringPtr
CFStringGetPascalStringPtr (CFStringRef str
, CFStringEncoding encoding
) {
1954 if (!CF_IS_OBJC(__kCFStringTypeID
, str
)) { /* ??? Hope the compiler optimizes this away if OBJC_MAPPINGS is not on */
1955 __CFAssertIsString(str
);
1956 if (__CFStrHasLengthByte(str
) && __CFStrIsEightBit(str
) && ((__CFStringGetEightBitStringEncoding() == encoding
) || (__CFStringGetEightBitStringEncoding() == kCFStringEncodingASCII
&& __CFStringEncodingIsSupersetOfASCII(encoding
)))) { // Requested encoding is equal to the encoding in string || the contents is in ASCII
1957 const uint8_t *contents
= (const uint8_t *)__CFStrContents(str
);
1958 if (__CFStrHasExplicitLength(str
) && (__CFStrLength2(str
, contents
) != (SInt32
)(*contents
))) return NULL
; // Invalid length byte
1959 return (ConstStringPtr
)contents
;
1961 // ??? Also check for encoding = SystemEncoding and perhaps bytes are all ASCII?
1967 const char * CFStringGetCStringPtr(CFStringRef str
, CFStringEncoding encoding
) {
1969 if (encoding
!= __CFStringGetEightBitStringEncoding() && (kCFStringEncodingASCII
!= __CFStringGetEightBitStringEncoding() || !__CFStringEncodingIsSupersetOfASCII(encoding
))) return NULL
;
1970 // ??? Also check for encoding = SystemEncoding and perhaps bytes are all ASCII?
1972 CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID
, const char *, str
, "_fastCStringContents:", true);
1974 __CFAssertIsString(str
);
1976 if (__CFStrHasNullByte(str
)) {
1977 // Note: this is called a lot, 27000 times to open a small xcode project with one file open.
1978 // Of these uses about 1500 are for cStrings/utf8strings.
1980 // Only sometimes when the stars are aligned will this call return a gc pointer
1981 // under GC we can only really return a pointer to the start of a GC buffer for cString use
1982 // (Is there a simpler way to ask if contents isGC?)
1983 CFAllocatorRef alloc
= (__CFStrHasContentsAllocator(str
)) ? __CFStrContentsAllocator(str
) : __CFGetAllocator(str
);
1984 if (CF_IS_COLLECTABLE_ALLOCATOR(alloc
)) {
1985 if (__CFStrSkipAnyLengthByte(str
) != 0 || !__CFStrIsMutable(str
)) {
1986 static int counter
= 0;
1987 printf("CFString %dth unsafe safe string %s\n", ++counter
, __CFStrContents(str
) + __CFStrSkipAnyLengthByte(str
));
1992 return (const char *)__CFStrContents(str
) + __CFStrSkipAnyLengthByte(str
);
1999 const UniChar
*CFStringGetCharactersPtr(CFStringRef str
) {
2001 CF_OBJC_FUNCDISPATCH0(__kCFStringTypeID
, const UniChar
*, str
, "_fastCharacterContents");
2003 __CFAssertIsString(str
);
2004 if (__CFStrIsUnicode(str
)) return (const UniChar
*)__CFStrContents(str
);
2009 Boolean
CFStringGetPascalString(CFStringRef str
, Str255 buffer
, CFIndex bufferSize
, CFStringEncoding encoding
) {
2013 __CFAssertIsNotNegative(bufferSize
);
2014 if (bufferSize
< 1) return false;
2016 if (CF_IS_OBJC(__kCFStringTypeID
, str
)) { /* ??? Hope the compiler optimizes this away if OBJC_MAPPINGS is not on */
2017 length
= CFStringGetLength(str
);
2018 if (!__CFCanUseLengthByte(length
)) return false; // Can't fit into pstring
2020 const uint8_t *contents
;
2022 __CFAssertIsString(str
);
2024 contents
= (const uint8_t *)__CFStrContents(str
);
2025 length
= __CFStrLength2(str
, contents
);
2027 if (!__CFCanUseLengthByte(length
)) return false; // Can't fit into pstring
2029 if (__CFStrIsEightBit(str
) && ((__CFStringGetEightBitStringEncoding() == encoding
) || (__CFStringGetEightBitStringEncoding() == kCFStringEncodingASCII
&& __CFStringEncodingIsSupersetOfASCII(encoding
)))) { // Requested encoding is equal to the encoding in string
2030 if (length
>= bufferSize
) return false;
2031 memmove((void*)(1 + (const char*)buffer
), (__CFStrSkipAnyLengthByte(str
) + contents
), length
);
2032 *buffer
= (unsigned char)length
;
2037 if (__CFStringEncodeByteStream(str
, 0, length
, false, encoding
, false, (UInt8
*)(1 + (uint8_t *)buffer
), bufferSize
- 1, &usedLen
) != length
) {
2040 if (bufferSize
> 0) {
2041 strlcpy((char *)buffer
+ 1, CONVERSIONFAILURESTR
, bufferSize
- 1);
2042 buffer
[0] = (unsigned char)((CFIndex
)sizeof(CONVERSIONFAILURESTR
) < (bufferSize
- 1) ? (CFIndex
)sizeof(CONVERSIONFAILURESTR
) : (bufferSize
- 1));
2045 if (bufferSize
> 0) buffer
[0] = 0;
2049 *buffer
= (unsigned char)usedLen
;
2053 Boolean
CFStringGetCString(CFStringRef str
, char *buffer
, CFIndex bufferSize
, CFStringEncoding encoding
) {
2054 const uint8_t *contents
;
2057 __CFAssertIsNotNegative(bufferSize
);
2058 if (bufferSize
< 1) return false;
2060 CF_OBJC_FUNCDISPATCH3(__kCFStringTypeID
, Boolean
, str
, "_getCString:maxLength:encoding:", buffer
, bufferSize
- 1, encoding
);
2062 __CFAssertIsString(str
);
2064 contents
= (const uint8_t *)__CFStrContents(str
);
2065 len
= __CFStrLength2(str
, contents
);
2067 if (__CFStrIsEightBit(str
) && ((__CFStringGetEightBitStringEncoding() == encoding
) || (__CFStringGetEightBitStringEncoding() == kCFStringEncodingASCII
&& __CFStringEncodingIsSupersetOfASCII(encoding
)))) { // Requested encoding is equal to the encoding in string
2068 if (len
>= bufferSize
) return false;
2069 memmove(buffer
, contents
+ __CFStrSkipAnyLengthByte(str
), len
);
2075 if (__CFStringEncodeByteStream(str
, 0, len
, false, encoding
, false, (unsigned char*) buffer
, bufferSize
- 1, &usedLen
) == len
) {
2076 buffer
[usedLen
] = '\0';
2080 strlcpy(buffer
, CONVERSIONFAILURESTR
, bufferSize
);
2082 if (bufferSize
> 0) buffer
[0] = 0;
2089 extern Boolean
__CFLocaleGetNullLocale(struct __CFLocale
*locale
);
2090 extern void __CFLocaleSetNullLocale(struct __CFLocale
*locale
);
2092 static const char *_CFStrGetLanguageIdentifierForLocale(CFLocaleRef locale
) {
2093 CFStringRef collatorID
;
2094 const char *langID
= NULL
;
2095 static const void *lastLocale
= NULL
;
2096 static const char *lastLangID
= NULL
;
2097 static CFSpinLock_t lock
= CFSpinLockInit
;
2099 if (__CFLocaleGetNullLocale((struct __CFLocale
*)locale
)) return NULL
;
2101 __CFSpinLock(&lock
);
2102 if ((NULL
!= lastLocale
) && (lastLocale
== locale
)) {
2103 __CFSpinUnlock(&lock
);
2106 __CFSpinUnlock(&lock
);
2108 collatorID
= (CFStringRef
)CFLocaleGetValue(locale
, __kCFLocaleCollatorID
);
2110 // This is somewhat depending on CFLocale implementation always creating CFString for locale identifer ???
2111 if (__CFStrLength(collatorID
) > 1) {
2112 const void *contents
= __CFStrContents(collatorID
);
2116 if (__CFStrIsEightBit(collatorID
)) {
2117 string
= ((const char *)contents
) + __CFStrSkipAnyLengthByte(collatorID
);
2119 const UTF16Char
*characters
= (const UTF16Char
*)contents
;
2121 buffer
[0] = (char)*(characters
++);
2122 buffer
[1] = (char)*characters
;
2126 if (!strncmp(string
, "az", 2)) { // Azerbaijani
2128 } else if (!strncmp(string
, "lt", 2)) { // Lithuanian
2130 } else if (!strncmp(string
, "tr", 2)) { // Turkish
2136 if (langID
== NULL
) __CFLocaleSetNullLocale((struct __CFLocale
*)locale
);
2138 __CFSpinLock(&lock
);
2139 lastLocale
= locale
;
2140 lastLangID
= langID
;
2141 __CFSpinUnlock(&lock
);
2146 static int8_t __CFCheckLocaleCFType
= -1;
2148 CF_INLINE
bool _CFCanUseLocale(CFLocaleRef locale
) {
2150 if (__CFCheckLocaleCFType
< 0) __CFCheckLocaleCFType
= !_CFExecutableLinkedOnOrAfter(CFSystemVersionPanther
);
2151 if (!__CFCheckLocaleCFType
|| (CFGetTypeID(locale
) == CFLocaleGetTypeID())) return true;
2156 #define MAX_CASE_MAPPING_BUF (8)
2157 #define ZERO_WIDTH_JOINER (0x200D)
2158 #define COMBINING_GRAPHEME_JOINER (0x034F)
2160 #define HANGUL_CHOSEONG_START (0x1100)
2161 #define HANGUL_CHOSEONG_END (0x115F)
2162 #define HANGUL_JUNGSEONG_START (0x1160)
2163 #define HANGUL_JUNGSEONG_END (0x11A2)
2164 #define HANGUL_JONGSEONG_START (0x11A8)
2165 #define HANGUL_JONGSEONG_END (0x11F9)
2167 #define HANGUL_SYLLABLE_START (0xAC00)
2168 #define HANGUL_SYLLABLE_END (0xD7AF)
2171 // Returns the length of characters filled into outCharacters. If no change, returns 0. maxBufLen shoule be at least 8
2172 static CFIndex
__CFStringFoldCharacterClusterAtIndex(UTF32Char character
, CFStringInlineBuffer
*buffer
, CFIndex index
, CFOptionFlags flags
, const uint8_t *langCode
, UTF32Char
*outCharacters
, CFIndex maxBufferLength
, CFIndex
*consumedLength
) {
2173 CFIndex filledLength
= 0, currentIndex
= index
;
2175 if (0 != character
) {
2176 UTF16Char lowSurrogate
;
2177 CFIndex planeNo
= (character
>> 16);
2178 bool isTurkikCapitalI
= false;
2179 static const uint8_t *decompBMP
= NULL
;
2180 static const uint8_t *graphemeBMP
= NULL
;
2182 if (NULL
== decompBMP
) {
2183 decompBMP
= CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet
, 0);
2184 graphemeBMP
= CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet
, 0);
2189 if ((character
< 0x0080) && ((NULL
== langCode
) || (character
!= 'I'))) { // ASCII
2190 if ((flags
& kCFCompareCaseInsensitive
) && (character
>= 'A') && (character
<= 'Z')) {
2191 character
+= ('a' - 'A');
2192 *outCharacters
= character
;
2196 // do width-insensitive mapping
2197 if ((flags
& kCFCompareWidthInsensitive
) && (character
>= 0xFF00) && (character
<= 0xFFEF)) {
2198 (void)CFUniCharCompatibilityDecompose(&character
, 1, 1);
2199 *outCharacters
= character
;
2204 if ((0 == planeNo
) && CFUniCharIsSurrogateHighCharacter(character
) && CFUniCharIsSurrogateLowCharacter((lowSurrogate
= CFStringGetCharacterFromInlineBuffer(buffer
, currentIndex
)))) {
2205 character
= CFUniCharGetLongCharacterForSurrogatePair(character
, lowSurrogate
);
2207 planeNo
= (character
>> 16);
2211 if (flags
& (kCFCompareDiacriticInsensitive
|kCFCompareNonliteral
)) {
2212 if (CFUniCharIsMemberOfBitmap(character
, ((0 == planeNo
) ? decompBMP
: CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet
, planeNo
)))) {
2213 UTF32Char original
= character
;
2215 filledLength
= CFUniCharDecomposeCharacter(character
, outCharacters
, maxBufferLength
);
2216 character
= *outCharacters
;
2218 if ((flags
& kCFCompareDiacriticInsensitive
) && (character
< 0x0510)) {
2219 filledLength
= 1; // reset if Roman, Greek, Cyrillic
2220 } else if (0 == (flags
& kCFCompareNonliteral
)) {
2221 character
= original
;
2228 if (flags
& kCFCompareCaseInsensitive
) {
2229 const uint8_t *nonBaseBitmap
;
2230 bool filterNonBase
= (((flags
& kCFCompareDiacriticInsensitive
) && (character
< 0x0510)) ? true : false);
2231 static const uint8_t *lowerBMP
= NULL
;
2232 static const uint8_t *caseFoldBMP
= NULL
;
2234 if (NULL
== lowerBMP
) {
2235 lowerBMP
= CFUniCharGetBitmapPtrForPlane(kCFUniCharHasNonSelfLowercaseCharacterSet
, 0);
2236 caseFoldBMP
= CFUniCharGetBitmapPtrForPlane(kCFUniCharHasNonSelfCaseFoldingCharacterSet
, 0);
2239 if ((NULL
!= langCode
) && ('I' == character
) && ((0 == strcmp((const char *)langCode
, "tr")) || (0 == strcmp((const char *)langCode
, "az")))) { // do Turkik special-casing
2240 if (filledLength
> 1) {
2241 if (0x0307 == outCharacters
[1]) {
2242 if (--filledLength
> 1) memmove((outCharacters
+ 1), (outCharacters
+ 2), sizeof(UTF32Char
) * (filledLength
- 1));
2243 character
= *outCharacters
= 'i';
2244 isTurkikCapitalI
= true;
2246 } else if (0x0307 == CFStringGetCharacterFromInlineBuffer(buffer
, currentIndex
)) {
2247 character
= *outCharacters
= 'i';
2250 isTurkikCapitalI
= true;
2253 if (!isTurkikCapitalI
&& (CFUniCharIsMemberOfBitmap(character
, ((0 == planeNo
) ? lowerBMP
: CFUniCharGetBitmapPtrForPlane(kCFUniCharHasNonSelfLowercaseCharacterSet
, planeNo
))) || CFUniCharIsMemberOfBitmap(character
, ((0 == planeNo
) ? caseFoldBMP
: CFUniCharGetBitmapPtrForPlane(kCFUniCharHasNonSelfCaseFoldingCharacterSet
, planeNo
))))) {
2254 UTF16Char caseFoldBuffer
[MAX_CASE_MAPPING_BUF
];
2255 const UTF16Char
*bufferP
= caseFoldBuffer
, *bufferLimit
;
2256 UTF32Char
*outCharactersP
= outCharacters
;
2257 uint32_t bufferLength
= CFUniCharMapCaseTo(character
, caseFoldBuffer
, MAX_CASE_MAPPING_BUF
, kCFUniCharCaseFold
, 0, langCode
);
2259 bufferLimit
= bufferP
+ bufferLength
;
2261 if (filledLength
> 0) --filledLength
; // decrement filledLength (will add back later)
2263 // make space for casefold characters
2264 if ((filledLength
> 0) && (bufferLength
> 1)) {
2265 CFIndex totalScalerLength
= 0;
2267 while (bufferP
< bufferLimit
) {
2268 if (CFUniCharIsSurrogateHighCharacter(*(bufferP
++)) && (bufferP
< bufferLimit
) && CFUniCharIsSurrogateLowCharacter(*bufferP
)) ++bufferP
;
2269 ++totalScalerLength
;
2271 memmove(outCharacters
+ totalScalerLength
, outCharacters
+ 1, filledLength
* sizeof(UTF32Char
));
2272 bufferP
= caseFoldBuffer
;
2276 while (bufferP
< bufferLimit
) {
2277 character
= *(bufferP
++);
2278 if (CFUniCharIsSurrogateHighCharacter(character
) && (bufferP
< bufferLimit
) && CFUniCharIsSurrogateLowCharacter(*bufferP
)) {
2279 character
= CFUniCharGetLongCharacterForSurrogatePair(character
, *(bufferP
++));
2280 nonBaseBitmap
= CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet
, (character
>> 16));
2282 nonBaseBitmap
= graphemeBMP
;
2285 if (!filterNonBase
|| !CFUniCharIsMemberOfBitmap(character
, nonBaseBitmap
)) {
2286 *(outCharactersP
++) = character
;
2294 // collect following combining marks
2295 if (flags
& (kCFCompareDiacriticInsensitive
|kCFCompareNonliteral
)) {
2296 const uint8_t *nonBaseBitmap
;
2297 const uint8_t *decompBitmap
;
2298 bool doFill
= (((flags
& kCFCompareDiacriticInsensitive
) && (character
< 0x0510)) ? false : true);
2300 if (0 == filledLength
) {
2301 *outCharacters
= character
; // filledLength will be updated below on demand
2303 if (doFill
) { // check if really needs to fill
2304 UTF32Char nonBaseCharacter
= CFStringGetCharacterFromInlineBuffer(buffer
, currentIndex
);
2306 if (CFUniCharIsSurrogateHighCharacter(nonBaseCharacter
) && CFUniCharIsSurrogateLowCharacter((lowSurrogate
= CFStringGetCharacterFromInlineBuffer(buffer
, currentIndex
+ 1)))) {
2307 nonBaseCharacter
= CFUniCharGetLongCharacterForSurrogatePair(nonBaseCharacter
, lowSurrogate
);
2308 nonBaseBitmap
= CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet
, (nonBaseCharacter
>> 16));
2309 decompBitmap
= CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet
, (nonBaseCharacter
>> 16));
2311 nonBaseBitmap
= graphemeBMP
;
2312 decompBitmap
= decompBMP
;
2315 if (CFUniCharIsMemberOfBitmap(nonBaseCharacter
, nonBaseBitmap
)) {
2316 filledLength
= 1; // For the base character
2318 if ((0 == (flags
& kCFCompareDiacriticInsensitive
)) || (nonBaseCharacter
> 0x050F)) {
2319 if (CFUniCharIsMemberOfBitmap(nonBaseCharacter
, decompBitmap
)) {
2320 filledLength
+= CFUniCharDecomposeCharacter(nonBaseCharacter
, &(outCharacters
[filledLength
]), maxBufferLength
- filledLength
);
2322 outCharacters
[filledLength
++] = nonBaseCharacter
;
2325 currentIndex
+= ((nonBaseBitmap
== graphemeBMP
) ? 1 : 2);
2332 while (filledLength
< maxBufferLength
) { // do the rest
2333 character
= CFStringGetCharacterFromInlineBuffer(buffer
, currentIndex
);
2335 if (CFUniCharIsSurrogateHighCharacter(character
) && CFUniCharIsSurrogateLowCharacter((lowSurrogate
= CFStringGetCharacterFromInlineBuffer(buffer
, currentIndex
+ 1)))) {
2336 character
= CFUniCharGetLongCharacterForSurrogatePair(character
, lowSurrogate
);
2337 nonBaseBitmap
= CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet
, (character
>> 16));
2338 decompBitmap
= CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet
, (character
>> 16));
2340 nonBaseBitmap
= graphemeBMP
;
2341 decompBitmap
= decompBMP
;
2343 if (isTurkikCapitalI
) {
2344 isTurkikCapitalI
= false;
2345 } else if (CFUniCharIsMemberOfBitmap(character
, nonBaseBitmap
)) {
2347 if (CFUniCharIsMemberOfBitmap(character
, decompBitmap
)) {
2348 CFIndex currentLength
= CFUniCharDecomposeCharacter(character
, &(outCharacters
[filledLength
]), maxBufferLength
- filledLength
);
2350 if (0 == currentLength
) break; // didn't fit
2352 filledLength
+= currentLength
;
2354 outCharacters
[filledLength
++] = character
;
2356 } else if (0 == filledLength
) {
2357 filledLength
= 1; // For the base character
2359 currentIndex
+= ((nonBaseBitmap
== graphemeBMP
) ? 1 : 2);
2365 if (filledLength
> 1) {
2366 UTF32Char
*sortCharactersLimit
= outCharacters
+ filledLength
;
2367 UTF32Char
*sortCharacters
= sortCharactersLimit
- 1;
2369 while ((outCharacters
< sortCharacters
) && CFUniCharIsMemberOfBitmap(*sortCharacters
, ((*sortCharacters
< 0x10000) ? graphemeBMP
: CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet
, (*sortCharacters
>> 16))))) --sortCharacters
;
2371 if ((sortCharactersLimit
- sortCharacters
) > 1) CFUniCharPrioritySort(sortCharacters
, (sortCharactersLimit
- sortCharacters
)); // priority sort
2376 if ((filledLength
> 0) && (NULL
!= consumedLength
)) *consumedLength
= (currentIndex
- index
);
2378 return filledLength
;
2381 static bool __CFStringFillCharacterSetInlineBuffer(CFCharacterSetInlineBuffer
*buffer
, CFStringCompareFlags compareOptions
) {
2382 if (0 != (compareOptions
& kCFCompareIgnoreNonAlphanumeric
)) {
2383 static CFCharacterSetRef nonAlnumChars
= NULL
;
2385 if (NULL
== nonAlnumChars
) {
2386 CFMutableCharacterSetRef cset
= CFCharacterSetCreateMutableCopy(NULL
, CFCharacterSetGetPredefined(kCFCharacterSetAlphaNumeric
));
2387 CFCharacterSetInvert(cset
);
2388 if (!OSAtomicCompareAndSwapPtrBarrier(NULL
, cset
, (void **)&nonAlnumChars
)) CFRelease(cset
);
2391 CFCharacterSetInitInlineBuffer(nonAlnumChars
, buffer
);
2399 #define kCFStringStackBufferLength (__kCFStringInlineBufferLength)
2401 CFComparisonResult
CFStringCompareWithOptionsAndLocale(CFStringRef string
, CFStringRef string2
, CFRange rangeToCompare
, CFStringCompareFlags compareOptions
, CFLocaleRef locale
) {
2402 /* No objc dispatch needed here since CFStringInlineBuffer works with both CFString and NSString */
2403 UTF32Char strBuf1
[kCFStringStackBufferLength
];
2404 UTF32Char strBuf2
[kCFStringStackBufferLength
];
2405 CFStringInlineBuffer inlineBuf1
, inlineBuf2
;
2406 UTF32Char str1Char
, str2Char
;
2407 CFIndex str1UsedLen
, str2UsedLen
;
2408 CFIndex str1Index
= 0, str2Index
= 0, strBuf1Index
= 0, strBuf2Index
= 0, strBuf1Len
= 0, strBuf2Len
= 0;
2409 CFIndex str1LocalizedIndex
= 0, str2LocalizedIndex
= 0;
2410 CFIndex forcedIndex1
= 0, forcedIndex2
= 0;
2411 CFIndex str2Len
= CFStringGetLength(string2
);
2412 bool caseInsensitive
= ((compareOptions
& kCFCompareCaseInsensitive
) ? true : false);
2413 bool diacriticsInsensitive
= ((compareOptions
& kCFCompareDiacriticInsensitive
) ? true : false);
2414 bool equalityOptions
= ((compareOptions
& (kCFCompareCaseInsensitive
|kCFCompareNonliteral
|kCFCompareDiacriticInsensitive
|kCFCompareWidthInsensitive
)) ? true : false);
2415 bool numerically
= ((compareOptions
& kCFCompareNumerically
) ? true : false);
2416 bool forceOrdering
= ((compareOptions
& kCFCompareForcedOrdering
) ? true : false);
2417 const uint8_t *graphemeBMP
= CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet
, 0);
2418 const uint8_t *langCode
;
2419 CFComparisonResult compareResult
= kCFCompareEqualTo
;
2420 UTF16Char otherChar
;
2421 Boolean freeLocale
= false;
2422 CFCharacterSetInlineBuffer
*ignoredChars
= NULL
;
2423 CFCharacterSetInlineBuffer csetBuffer
;
2425 if ((compareOptions
& kCFCompareLocalized
) && (NULL
== locale
)) {
2426 locale
= CFLocaleCopyCurrent();
2430 langCode
= ((NULL
== locale
) ? NULL
: (const uint8_t *)_CFStrGetLanguageIdentifierForLocale(locale
));
2432 if (__CFStringFillCharacterSetInlineBuffer(&csetBuffer
, compareOptions
)) {
2433 ignoredChars
= &csetBuffer
;
2434 equalityOptions
= true;
2437 if ((NULL
== locale
) && (NULL
== ignoredChars
) && !numerically
) { // could do binary comp (be careful when adding new flags)
2438 CFStringEncoding eightBitEncoding
= __CFStringGetEightBitStringEncoding();
2439 const uint8_t *str1Bytes
= (const uint8_t *)CFStringGetCStringPtr(string
, eightBitEncoding
);
2440 const uint8_t *str2Bytes
= (const uint8_t *)CFStringGetCStringPtr(string2
, eightBitEncoding
);
2441 CFIndex factor
= sizeof(uint8_t);
2443 if ((NULL
!= str1Bytes
) && (NULL
!= str2Bytes
)) {
2444 compareOptions
&= ~kCFCompareNonliteral
; // remove non-literal
2446 if ((kCFStringEncodingASCII
== eightBitEncoding
) && (false == forceOrdering
)) {
2447 if (caseInsensitive
) {
2448 int cmpResult
= strncasecmp_l((const char *)str1Bytes
+ rangeToCompare
.location
, (const char *)str2Bytes
, __CFMin(rangeToCompare
.length
, str2Len
), NULL
);
2450 if (0 == cmpResult
) cmpResult
= rangeToCompare
.length
- str2Len
;
2452 return ((0 == cmpResult
) ? kCFCompareEqualTo
: ((cmpResult
< 0) ? kCFCompareLessThan
: kCFCompareGreaterThan
));
2454 } else if (caseInsensitive
|| diacriticsInsensitive
) {
2455 CFIndex limitLength
= __CFMin(rangeToCompare
.length
, str2Len
);
2457 str1Bytes
+= rangeToCompare
.location
;
2459 while (str1Index
< limitLength
) {
2460 str1Char
= str1Bytes
[str1Index
];
2461 str2Char
= str2Bytes
[str1Index
];
2463 if (str1Char
!= str2Char
) {
2464 if ((str1Char
< 0x80) && (str2Char
< 0x80)) {
2465 if (forceOrdering
&& (kCFCompareEqualTo
== compareResult
) && (str1Char
!= str2Char
)) compareResult
= ((str1Char
< str2Char
) ? kCFCompareLessThan
: kCFCompareGreaterThan
);
2466 if ((str1Char
>= 'A') && (str1Char
<= 'Z')) str1Char
+= ('a' - 'A');
2467 if ((str2Char
>= 'A') && (str2Char
<= 'Z')) str2Char
+= ('a' - 'A');
2469 if (str1Char
!= str2Char
) return ((str1Char
< str2Char
) ? kCFCompareLessThan
: kCFCompareGreaterThan
);
2478 str2Index
= str1Index
;
2480 if (str1Index
== limitLength
) {
2481 int cmpResult
= rangeToCompare
.length
- str2Len
;
2483 return ((0 == cmpResult
) ? compareResult
: ((cmpResult
< 0) ? kCFCompareLessThan
: kCFCompareGreaterThan
));
2486 } else if (!equalityOptions
&& (NULL
== str1Bytes
) && (NULL
== str2Bytes
)) {
2487 str1Bytes
= (const uint8_t *)CFStringGetCharactersPtr(string
);
2488 str2Bytes
= (const uint8_t *)CFStringGetCharactersPtr(string2
);
2489 factor
= sizeof(UTF16Char
);
2490 #if __LITTLE_ENDIAN__
2491 if ((NULL
!= str1Bytes
) && (NULL
!= str2Bytes
)) { // we cannot use memcmp
2492 const UTF16Char
*str1
= ((const UTF16Char
*)str1Bytes
) + rangeToCompare
.location
;
2493 const UTF16Char
*str1Limit
= str1
+ __CFMin(rangeToCompare
.length
, str2Len
);
2494 const UTF16Char
*str2
= (const UTF16Char
*)str2Bytes
;
2495 CFIndex cmpResult
= 0;
2497 while ((0 == cmpResult
) && (str1
< str1Limit
)) cmpResult
= (CFIndex
)*(str1
++) - (CFIndex
)*(str2
++);
2499 if (0 == cmpResult
) cmpResult
= rangeToCompare
.length
- str2Len
;
2501 return ((0 == cmpResult
) ? kCFCompareEqualTo
: ((cmpResult
< 0) ? kCFCompareLessThan
: kCFCompareGreaterThan
));
2503 #endif /* __LITTLE_ENDIAN__ */
2505 if ((NULL
!= str1Bytes
) && (NULL
!= str2Bytes
)) {
2506 int cmpResult
= memcmp(str1Bytes
+ (rangeToCompare
.location
* factor
), str2Bytes
, __CFMin(rangeToCompare
.length
, str2Len
) * factor
);
2508 if (0 == cmpResult
) cmpResult
= rangeToCompare
.length
- str2Len
;
2510 return ((0 == cmpResult
) ? kCFCompareEqualTo
: ((cmpResult
< 0) ? kCFCompareLessThan
: kCFCompareGreaterThan
));
2514 CFStringInitInlineBuffer(string
, &inlineBuf1
, rangeToCompare
);
2515 CFStringInitInlineBuffer(string2
, &inlineBuf2
, CFRangeMake(0, str2Len
));
2517 if (NULL
!= locale
) {
2518 str1LocalizedIndex
= str1Index
;
2519 str2LocalizedIndex
= str2Index
;
2521 // We temporarily disable kCFCompareDiacriticInsensitive for SL <rdar://problem/6767096>. Should be revisited in NMOS <rdar://problem/7003830>
2522 if (forceOrdering
) {
2523 diacriticsInsensitive
= false;
2524 compareOptions
&= ~kCFCompareDiacriticInsensitive
;
2527 while ((str1Index
< rangeToCompare
.length
) && (str2Index
< str2Len
)) {
2528 if (strBuf1Len
== 0) {
2529 str1Char
= CFStringGetCharacterFromInlineBuffer(&inlineBuf1
, str1Index
);
2530 if (caseInsensitive
&& (str1Char
>= 'A') && (str1Char
<= 'Z') && ((NULL
== langCode
) || (str1Char
!= 'I')) && ((false == forceOrdering
) || (kCFCompareEqualTo
!= compareResult
))) str1Char
+= ('a' - 'A');
2533 str1Char
= strBuf1
[strBuf1Index
++];
2535 if (strBuf2Len
== 0) {
2536 str2Char
= CFStringGetCharacterFromInlineBuffer(&inlineBuf2
, str2Index
);
2537 if (caseInsensitive
&& (str2Char
>= 'A') && (str2Char
<= 'Z') && ((NULL
== langCode
) || (str2Char
!= 'I')) && ((false == forceOrdering
) || (kCFCompareEqualTo
!= compareResult
))) str2Char
+= ('a' - 'A');
2540 str2Char
= strBuf2
[strBuf2Index
++];
2543 if (numerically
&& ((0 == strBuf1Len
) && (str1Char
<= '9') && (str1Char
>= '0')) && ((0 == strBuf2Len
) && (str2Char
<= '9') && (str2Char
>= '0'))) { // If both are not ASCII digits, then don't do numerical comparison here
2544 uint64_t intValue1
= 0, intValue2
= 0; // !!! Doesn't work if numbers are > max uint64_t
2546 if (forceOrdering
&& (kCFCompareEqualTo
== compareResult
) && (str1Char
!= str2Char
)) {
2547 compareResult
= ((str1Char
< str2Char
) ? kCFCompareLessThan
: kCFCompareGreaterThan
);
2548 forcedIndex1
= str1Index
;
2549 forcedIndex2
= str2Index
;
2553 intValue1
= (intValue1
* 10) + (str1Char
- '0');
2554 str1Char
= CFStringGetCharacterFromInlineBuffer(&inlineBuf1
, ++str1Index
);
2555 } while ((str1Char
<= '9') && (str1Char
>= '0'));
2558 intValue2
= intValue2
* 10 + (str2Char
- '0');
2559 str2Char
= CFStringGetCharacterFromInlineBuffer(&inlineBuf2
, ++str2Index
);
2560 } while ((str2Char
<= '9') && (str2Char
>= '0'));
2562 if (intValue1
== intValue2
) {
2564 } else if (intValue1
< intValue2
) {
2565 if (freeLocale
&& locale
) {
2568 return kCFCompareLessThan
;
2570 if (freeLocale
&& locale
) {
2573 return kCFCompareGreaterThan
;
2577 if (str1Char
!= str2Char
) {
2578 if (!equalityOptions
) {
2579 compareResult
= ((NULL
== locale
) ? ((str1Char
< str2Char
) ? kCFCompareLessThan
: kCFCompareGreaterThan
) : _CFCompareStringsWithLocale(&inlineBuf1
, CFRangeMake(str1Index
, rangeToCompare
.length
- str1Index
), &inlineBuf2
, CFRangeMake(str2Index
, str2Len
- str2Index
), compareOptions
, locale
));
2580 if (freeLocale
&& locale
) {
2583 return compareResult
;
2586 if (forceOrdering
&& (kCFCompareEqualTo
== compareResult
)) {
2587 compareResult
= ((str1Char
< str2Char
) ? kCFCompareLessThan
: kCFCompareGreaterThan
);
2588 forcedIndex1
= str1LocalizedIndex
;
2589 forcedIndex2
= str2LocalizedIndex
;
2592 if ((str1Char
< 0x80) && (str2Char
< 0x80) && (NULL
== ignoredChars
)) {
2593 if (NULL
!= locale
) {
2594 compareResult
= _CFCompareStringsWithLocale(&inlineBuf1
, CFRangeMake(str1Index
, rangeToCompare
.length
- str1Index
), &inlineBuf2
, CFRangeMake(str2Index
, str2Len
- str2Index
), compareOptions
, locale
);
2595 if (freeLocale
&& locale
) {
2598 return compareResult
;
2599 } else if (!caseInsensitive
) {
2600 if (freeLocale
&& locale
) {
2603 return ((str1Char
< str2Char
) ? kCFCompareLessThan
: kCFCompareGreaterThan
);
2607 if (CFUniCharIsSurrogateHighCharacter(str1Char
) && CFUniCharIsSurrogateLowCharacter((otherChar
= CFStringGetCharacterFromInlineBuffer(&inlineBuf1
, str1Index
+ 1)))) {
2608 str1Char
= CFUniCharGetLongCharacterForSurrogatePair(str1Char
, otherChar
);
2612 if (CFUniCharIsSurrogateHighCharacter(str2Char
) && CFUniCharIsSurrogateLowCharacter((otherChar
= CFStringGetCharacterFromInlineBuffer(&inlineBuf2
, str2Index
+ 1)))) {
2613 str2Char
= CFUniCharGetLongCharacterForSurrogatePair(str2Char
, otherChar
);
2617 if (NULL
!= ignoredChars
) {
2618 if (CFCharacterSetInlineBufferIsLongCharacterMember(ignoredChars
, str1Char
)) {
2619 if ((strBuf1Len
> 0) && (strBuf1Index
== strBuf1Len
)) strBuf1Len
= 0;
2620 if (strBuf1Len
== 0) str1Index
+= str1UsedLen
;
2621 if (strBuf2Len
> 0) --strBuf2Index
;
2624 if (CFCharacterSetInlineBufferIsLongCharacterMember(ignoredChars
, str2Char
)) {
2625 if ((strBuf2Len
> 0) && (strBuf2Index
== strBuf2Len
)) strBuf2Len
= 0;
2626 if (strBuf2Len
== 0) str2Index
+= str2UsedLen
;
2627 if (strBuf1Len
> 0) -- strBuf1Index
;
2632 if (diacriticsInsensitive
&& (str1Index
> 0)) {
2633 bool str1Skip
= false;
2634 bool str2Skip
= false;
2636 if ((0 == strBuf1Len
) && CFUniCharIsMemberOfBitmap(str1Char
, ((str1Char
< 0x10000) ? graphemeBMP
: CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet
, (str1Char
>> 16))))) {
2637 str1Char
= str2Char
;
2640 if ((0 == strBuf2Len
) && CFUniCharIsMemberOfBitmap(str2Char
, ((str2Char
< 0x10000) ? graphemeBMP
: CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet
, (str2Char
>> 16))))) {
2641 str2Char
= str1Char
;
2645 if (str1Skip
!= str2Skip
) {
2646 if (str1Skip
) str2Index
-= str2UsedLen
;
2647 if (str2Skip
) str1Index
-= str1UsedLen
;
2651 if (str1Char
!= str2Char
) {
2652 if (0 == strBuf1Len
) {
2653 strBuf1Len
= __CFStringFoldCharacterClusterAtIndex(str1Char
, &inlineBuf1
, str1Index
, compareOptions
, langCode
, strBuf1
, kCFStringStackBufferLength
, &str1UsedLen
);
2654 if (strBuf1Len
> 0) {
2655 str1Char
= *strBuf1
;
2660 if ((0 == strBuf1Len
) && (0 < strBuf2Len
)) {
2661 compareResult
= ((NULL
== locale
) ? ((str1Char
< str2Char
) ? kCFCompareLessThan
: kCFCompareGreaterThan
) : _CFCompareStringsWithLocale(&inlineBuf1
, CFRangeMake(str1LocalizedIndex
, rangeToCompare
.length
- str1LocalizedIndex
), &inlineBuf2
, CFRangeMake(str2LocalizedIndex
, str2Len
- str2LocalizedIndex
), compareOptions
, locale
));
2662 if (freeLocale
&& locale
) {
2665 return compareResult
;
2668 if ((0 == strBuf2Len
) && ((0 == strBuf1Len
) || (str1Char
!= str2Char
))) {
2669 strBuf2Len
= __CFStringFoldCharacterClusterAtIndex(str2Char
, &inlineBuf2
, str2Index
, compareOptions
, langCode
, strBuf2
, kCFStringStackBufferLength
, &str2UsedLen
);
2670 if (strBuf2Len
> 0) {
2671 str2Char
= *strBuf2
;
2674 if ((0 == strBuf2Len
) || (str1Char
!= str2Char
)) {
2675 compareResult
= ((NULL
== locale
) ? ((str1Char
< str2Char
) ? kCFCompareLessThan
: kCFCompareGreaterThan
) : _CFCompareStringsWithLocale(&inlineBuf1
, CFRangeMake(str1LocalizedIndex
, rangeToCompare
.length
- str1LocalizedIndex
), &inlineBuf2
, CFRangeMake(str2LocalizedIndex
, str2Len
- str2LocalizedIndex
), compareOptions
, locale
));
2676 if (freeLocale
&& locale
) {
2679 return compareResult
;
2684 if ((strBuf1Len
> 0) && (strBuf2Len
> 0)) {
2685 while ((strBuf1Index
< strBuf1Len
) && (strBuf2Index
< strBuf2Len
)) {
2686 if (strBuf1
[strBuf1Index
] != strBuf2
[strBuf2Index
]) break;
2687 ++strBuf1Index
; ++strBuf2Index
;
2689 if ((strBuf1Index
< strBuf1Len
) && (strBuf2Index
< strBuf2Len
)) {
2690 CFComparisonResult res
= ((NULL
== locale
) ? ((strBuf1
[strBuf1Index
] < strBuf2
[strBuf2Index
]) ? kCFCompareLessThan
: kCFCompareGreaterThan
) : _CFCompareStringsWithLocale(&inlineBuf1
, CFRangeMake(str1LocalizedIndex
, rangeToCompare
.length
- str1LocalizedIndex
), &inlineBuf2
, CFRangeMake(str2LocalizedIndex
, str2Len
- str2LocalizedIndex
), compareOptions
, locale
));
2691 if (freeLocale
&& locale
) {
2699 if ((strBuf1Len
> 0) && (strBuf1Index
== strBuf1Len
)) strBuf1Len
= 0;
2700 if ((strBuf2Len
> 0) && (strBuf2Index
== strBuf2Len
)) strBuf2Len
= 0;
2702 if (strBuf1Len
== 0) str1Index
+= str1UsedLen
;
2703 if (strBuf2Len
== 0) str2Index
+= str2UsedLen
;
2704 if ((strBuf1Len
== 0) && (strBuf2Len
== 0)) {
2705 str1LocalizedIndex
= str1Index
;
2706 str2LocalizedIndex
= str2Index
;
2710 if (diacriticsInsensitive
|| (NULL
!= ignoredChars
)) {
2711 while (str1Index
< rangeToCompare
.length
) {
2712 str1Char
= CFStringGetCharacterFromInlineBuffer(&inlineBuf1
, str1Index
);
2713 if ((str1Char
< 0x80) && (NULL
== ignoredChars
)) break; // found ASCII
2715 if (CFUniCharIsSurrogateHighCharacter(str1Char
) && CFUniCharIsSurrogateLowCharacter((otherChar
= CFStringGetCharacterFromInlineBuffer(&inlineBuf1
, str1Index
+ 1)))) str1Char
= CFUniCharGetLongCharacterForSurrogatePair(str1Char
, otherChar
);
2717 if ((!diacriticsInsensitive
|| !CFUniCharIsMemberOfBitmap(str1Char
, ((str1Char
< 0x10000) ? graphemeBMP
: CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet
, (str1Char
>> 16))))) && ((NULL
== ignoredChars
) || !CFCharacterSetInlineBufferIsLongCharacterMember(ignoredChars
, str1Char
))) break;
2719 str1Index
+= ((str1Char
< 0x10000) ? 1 : 2);
2722 while (str2Index
< str2Len
) {
2723 str2Char
= CFStringGetCharacterFromInlineBuffer(&inlineBuf2
, str2Index
);
2724 if ((str2Char
< 0x80) && (NULL
== ignoredChars
)) break; // found ASCII
2726 if (CFUniCharIsSurrogateHighCharacter(str2Char
) && CFUniCharIsSurrogateLowCharacter((otherChar
= CFStringGetCharacterFromInlineBuffer(&inlineBuf2
, str2Index
+ 1)))) str2Char
= CFUniCharGetLongCharacterForSurrogatePair(str2Char
, otherChar
);
2728 if ((!diacriticsInsensitive
|| !CFUniCharIsMemberOfBitmap(str2Char
, ((str2Char
< 0x10000) ? graphemeBMP
: CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet
, (str2Char
>> 16))))) && ((NULL
== ignoredChars
) || !CFCharacterSetInlineBufferIsLongCharacterMember(ignoredChars
, str2Char
))) break;
2730 str2Index
+= ((str2Char
< 0x10000) ? 1 : 2);
2733 // Need to recalc localized result here for forced ordering
2734 if ((NULL
!= locale
) && (kCFCompareEqualTo
!= compareResult
) && (str1Index
== rangeToCompare
.length
) && (str2Index
== str2Len
)) compareResult
= _CFCompareStringsWithLocale(&inlineBuf1
, CFRangeMake(forcedIndex1
, rangeToCompare
.length
- forcedIndex1
), &inlineBuf2
, CFRangeMake(forcedIndex2
, str2Len
- forcedIndex2
), compareOptions
, locale
);
2736 if (freeLocale
&& locale
) {
2740 return ((str1Index
< rangeToCompare
.length
) ? kCFCompareGreaterThan
: ((str2Index
< str2Len
) ? kCFCompareLessThan
: compareResult
));
2744 CFComparisonResult
CFStringCompareWithOptions(CFStringRef string
, CFStringRef string2
, CFRange rangeToCompare
, CFStringCompareFlags compareOptions
) { return CFStringCompareWithOptionsAndLocale(string
, string2
, rangeToCompare
, compareOptions
, NULL
); }
2746 CFComparisonResult
CFStringCompare(CFStringRef string
, CFStringRef str2
, CFOptionFlags options
) {
2747 return CFStringCompareWithOptions(string
, str2
, CFRangeMake(0, CFStringGetLength(string
)), options
);
2750 Boolean
CFStringFindWithOptionsAndLocale(CFStringRef string
, CFStringRef stringToFind
, CFRange rangeToSearch
, CFStringCompareFlags compareOptions
, CFLocaleRef locale
, CFRange
*result
) {
2751 /* No objc dispatch needed here since CFStringInlineBuffer works with both CFString and NSString */
2752 CFIndex findStrLen
= CFStringGetLength(stringToFind
);
2753 Boolean didFind
= false;
2754 bool lengthVariants
= ((compareOptions
& (kCFCompareCaseInsensitive
|kCFCompareNonliteral
|kCFCompareDiacriticInsensitive
)) ? true : false);
2755 CFCharacterSetInlineBuffer
*ignoredChars
= NULL
;
2756 CFCharacterSetInlineBuffer csetBuffer
;
2758 if (__CFStringFillCharacterSetInlineBuffer(&csetBuffer
, compareOptions
)) {
2759 ignoredChars
= &csetBuffer
;
2760 lengthVariants
= true;
2763 if ((findStrLen
> 0) && (rangeToSearch
.length
> 0) && ((findStrLen
<= rangeToSearch
.length
) || lengthVariants
)) {
2764 UTF32Char strBuf1
[kCFStringStackBufferLength
];
2765 UTF32Char strBuf2
[kCFStringStackBufferLength
];
2766 CFStringInlineBuffer inlineBuf1
, inlineBuf2
;
2767 UTF32Char str1Char
= 0, str2Char
= 0;
2768 CFStringEncoding eightBitEncoding
= __CFStringGetEightBitStringEncoding();
2769 const uint8_t *str1Bytes
= (const uint8_t *)CFStringGetCStringPtr(string
, eightBitEncoding
);
2770 const uint8_t *str2Bytes
= (const uint8_t *)CFStringGetCStringPtr(stringToFind
, eightBitEncoding
);
2771 const UTF32Char
*characters
, *charactersLimit
;
2772 const uint8_t *langCode
= NULL
;
2773 CFIndex fromLoc
, toLoc
;
2774 CFIndex str1Index
, str2Index
;
2775 CFIndex strBuf1Len
, strBuf2Len
;
2776 CFIndex maxStr1Index
= (rangeToSearch
.location
+ rangeToSearch
.length
);
2777 bool equalityOptions
= ((lengthVariants
|| (compareOptions
& kCFCompareWidthInsensitive
)) ? true : false);
2778 bool caseInsensitive
= ((compareOptions
& kCFCompareCaseInsensitive
) ? true : false);
2779 bool forwardAnchor
= ((kCFCompareAnchored
== (compareOptions
& (kCFCompareBackwards
|kCFCompareAnchored
))) ? true : false);
2780 bool backwardAnchor
= (((kCFCompareBackwards
|kCFCompareAnchored
) == (compareOptions
& (kCFCompareBackwards
|kCFCompareAnchored
))) ? true : false);
2783 if (NULL
== locale
) {
2784 if (compareOptions
& kCFCompareLocalized
) {
2785 CFLocaleRef currentLocale
= CFLocaleCopyCurrent();
2786 langCode
= (const uint8_t *)_CFStrGetLanguageIdentifierForLocale(currentLocale
);
2787 CFRelease(currentLocale
);
2790 langCode
= (const uint8_t *)_CFStrGetLanguageIdentifierForLocale(locale
);
2793 CFStringInitInlineBuffer(string
, &inlineBuf1
, CFRangeMake(0, rangeToSearch
.location
+ rangeToSearch
.length
));
2794 CFStringInitInlineBuffer(stringToFind
, &inlineBuf2
, CFRangeMake(0, findStrLen
));
2796 if (compareOptions
& kCFCompareBackwards
) {
2797 fromLoc
= rangeToSearch
.location
+ rangeToSearch
.length
- (lengthVariants
? 1 : findStrLen
);
2798 toLoc
= (((compareOptions
& kCFCompareAnchored
) && !lengthVariants
) ? fromLoc
: rangeToSearch
.location
);
2800 fromLoc
= rangeToSearch
.location
;
2801 toLoc
= ((compareOptions
& kCFCompareAnchored
) ? fromLoc
: rangeToSearch
.location
+ rangeToSearch
.length
- (lengthVariants
? 1 : findStrLen
));
2804 delta
= ((fromLoc
<= toLoc
) ? 1 : -1);
2806 if ((NULL
!= str1Bytes
) && (NULL
!= str2Bytes
)) {
2807 uint8_t str1Byte
, str2Byte
;
2810 str1Index
= fromLoc
;
2813 while ((str1Index
< maxStr1Index
) && (str2Index
< findStrLen
)) {
2814 str1Byte
= str1Bytes
[str1Index
];
2815 str2Byte
= str2Bytes
[str2Index
];
2817 if (str1Byte
!= str2Byte
) {
2818 if (equalityOptions
) {
2819 if ((str1Byte
< 0x80) && ((NULL
== langCode
) || ('I' != str1Byte
))) {
2820 if (caseInsensitive
&& (str1Byte
>= 'A') && (str1Byte
<= 'Z')) str1Byte
+= ('a' - 'A');
2821 *strBuf1
= str1Byte
;
2824 str1Char
= CFStringGetCharacterFromInlineBuffer(&inlineBuf1
, str1Index
);
2825 strBuf1Len
= __CFStringFoldCharacterClusterAtIndex(str1Char
, &inlineBuf1
, str1Index
, compareOptions
, langCode
, strBuf1
, kCFStringStackBufferLength
, NULL
);
2826 if (1 > strBuf1Len
) {
2827 *strBuf1
= str1Char
;
2832 if ((NULL
!= ignoredChars
) && (forwardAnchor
|| (str1Index
!= fromLoc
)) && CFCharacterSetInlineBufferIsLongCharacterMember(ignoredChars
, ((str1Byte
< 0x80) ? str1Byte
: str1Char
))) {
2837 if ((str2Byte
< 0x80) && ((NULL
== langCode
) || ('I' != str2Byte
))) {
2838 if (caseInsensitive
&& (str2Byte
>= 'A') && (str2Byte
<= 'Z')) str2Byte
+= ('a' - 'A');
2839 *strBuf2
= str2Byte
;
2842 str2Char
= CFStringGetCharacterFromInlineBuffer(&inlineBuf2
, str2Index
);
2843 strBuf2Len
= __CFStringFoldCharacterClusterAtIndex(str2Char
, &inlineBuf2
, str2Index
, compareOptions
, langCode
, strBuf2
, kCFStringStackBufferLength
, NULL
);
2844 if (1 > strBuf2Len
) {
2845 *strBuf2
= str2Char
;
2850 if ((NULL
!= ignoredChars
) && CFCharacterSetInlineBufferIsLongCharacterMember(ignoredChars
, ((str2Byte
< 0x80) ? str2Byte
: str2Char
))) {
2855 if ((1 == strBuf1Len
) && (1 == strBuf2Len
)) { // normal case
2856 if (*strBuf1
!= *strBuf2
) break;
2860 if (!caseInsensitive
&& (strBuf1Len
!= strBuf2Len
)) break;
2861 if (memcmp(strBuf1
, strBuf2
, sizeof(UTF32Char
) * __CFMin(strBuf1Len
, strBuf2Len
))) break;
2863 if (strBuf1Len
< strBuf2Len
) {
2864 delta
= strBuf2Len
- strBuf1Len
;
2866 if ((str1Index
+ strBuf1Len
+ delta
) > maxStr1Index
) break;
2868 characters
= &(strBuf2
[strBuf1Len
]);
2869 charactersLimit
= characters
+ delta
;
2871 while (characters
< charactersLimit
) {
2872 strBuf1Len
= __CFStringFoldCharacterClusterAtIndex(CFStringGetCharacterFromInlineBuffer(&inlineBuf1
, str1Index
+ 1), &inlineBuf1
, str1Index
+ 1, compareOptions
, langCode
, strBuf1
, kCFStringStackBufferLength
, NULL
);
2873 if ((strBuf1Len
> 0) || (*characters
!= *strBuf1
)) break;
2874 ++characters
; ++str1Index
;
2876 if (characters
< charactersLimit
) break;
2877 } else if (strBuf2Len
< strBuf1Len
) {
2878 delta
= strBuf1Len
- strBuf2Len
;
2880 if ((str2Index
+ strBuf2Len
+ delta
) > findStrLen
) break;
2882 characters
= &(strBuf1
[strBuf2Len
]);
2883 charactersLimit
= characters
+ delta
;
2885 while (characters
< charactersLimit
) {
2886 strBuf2Len
= __CFStringFoldCharacterClusterAtIndex(CFStringGetCharacterFromInlineBuffer(&inlineBuf2
, str1Index
+ 1), &inlineBuf2
, str2Index
+ 1, compareOptions
, langCode
, strBuf2
, kCFStringStackBufferLength
, NULL
);
2887 if ((strBuf2Len
> 0) || (*characters
!= *strBuf2
)) break;
2888 ++characters
; ++str2Index
;
2890 if (characters
< charactersLimit
) break;
2897 ++str1Index
; ++str2Index
;
2900 if ((NULL
!= ignoredChars
) && (str1Index
== maxStr1Index
) && (str2Index
< findStrLen
)) { // Process the stringToFind tail
2901 while (str2Index
< findStrLen
) {
2902 str2Char
= CFStringGetCharacterFromInlineBuffer(&inlineBuf2
, str2Index
);
2904 if (!CFCharacterSetInlineBufferIsLongCharacterMember(ignoredChars
, str2Char
)) break;
2909 if (str2Index
== findStrLen
) {
2910 if ((NULL
!= ignoredChars
) && backwardAnchor
&& (str1Index
< maxStr1Index
)) { // Process the anchor tail
2911 while (str1Index
< maxStr1Index
) {
2912 str1Char
= CFStringGetCharacterFromInlineBuffer(&inlineBuf1
, str1Index
);
2914 if (!CFCharacterSetInlineBufferIsLongCharacterMember(ignoredChars
, str1Char
)) break;
2919 if (!backwardAnchor
|| (str1Index
== maxStr1Index
)) {
2921 if (NULL
!= result
) *result
= CFRangeMake(fromLoc
, str1Index
- fromLoc
);
2926 if (fromLoc
== toLoc
) break;
2929 } else if (equalityOptions
) {
2930 UTF16Char otherChar
;
2931 CFIndex str1UsedLen
, str2UsedLen
, strBuf1Index
= 0, strBuf2Index
= 0;
2932 bool diacriticsInsensitive
= ((compareOptions
& kCFCompareDiacriticInsensitive
) ? true : false);
2933 const uint8_t *graphemeBMP
= CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet
, 0);
2934 const uint8_t *combClassBMP
= (const uint8_t *)CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty
, 0);
2937 str1Index
= fromLoc
;
2940 strBuf1Len
= strBuf2Len
= 0;
2942 while (str2Index
< findStrLen
) {
2943 if (strBuf1Len
== 0) {
2944 str1Char
= CFStringGetCharacterFromInlineBuffer(&inlineBuf1
, str1Index
);
2945 if (caseInsensitive
&& (str1Char
>= 'A') && (str1Char
<= 'Z') && ((NULL
== langCode
) || (str1Char
!= 'I'))) str1Char
+= ('a' - 'A');
2948 str1Char
= strBuf1
[strBuf1Index
++];
2950 if (strBuf2Len
== 0) {
2951 str2Char
= CFStringGetCharacterFromInlineBuffer(&inlineBuf2
, str2Index
);
2952 if (caseInsensitive
&& (str2Char
>= 'A') && (str2Char
<= 'Z') && ((NULL
== langCode
) || (str2Char
!= 'I'))) str2Char
+= ('a' - 'A');
2955 str2Char
= strBuf2
[strBuf2Index
++];
2958 if (str1Char
!= str2Char
) {
2959 if ((str1Char
< 0x80) && (str2Char
< 0x80) && (NULL
== ignoredChars
) && ((NULL
== langCode
) || !caseInsensitive
)) break;
2961 if (CFUniCharIsSurrogateHighCharacter(str1Char
) && CFUniCharIsSurrogateLowCharacter((otherChar
= CFStringGetCharacterFromInlineBuffer(&inlineBuf1
, str1Index
+ 1)))) {
2962 str1Char
= CFUniCharGetLongCharacterForSurrogatePair(str1Char
, otherChar
);
2966 if (CFUniCharIsSurrogateHighCharacter(str2Char
) && CFUniCharIsSurrogateLowCharacter((otherChar
= CFStringGetCharacterFromInlineBuffer(&inlineBuf2
, str2Index
+ 1)))) {
2967 str2Char
= CFUniCharGetLongCharacterForSurrogatePair(str2Char
, otherChar
);
2971 if (NULL
!= ignoredChars
) {
2972 if ((forwardAnchor
|| (str1Index
!= fromLoc
)) && (str1Index
< maxStr1Index
) && CFCharacterSetInlineBufferIsLongCharacterMember(ignoredChars
, str1Char
)) {
2973 if ((strBuf1Len
> 0) && (strBuf1Index
== strBuf1Len
)) strBuf1Len
= 0;
2974 if (strBuf1Len
== 0) str1Index
+= str1UsedLen
;
2975 if (strBuf2Len
> 0) --strBuf2Index
;
2978 if (CFCharacterSetInlineBufferIsLongCharacterMember(ignoredChars
, str2Char
)) {
2979 if ((strBuf2Len
> 0) && (strBuf2Index
== strBuf2Len
)) strBuf2Len
= 0;
2980 if (strBuf2Len
== 0) str2Index
+= str2UsedLen
;
2981 if (strBuf1Len
> 0) -- strBuf1Index
;
2986 if (diacriticsInsensitive
&& (str1Index
> fromLoc
)) {
2987 bool str1Skip
= false;
2988 bool str2Skip
= false;
2990 if ((0 == strBuf1Len
) && CFUniCharIsMemberOfBitmap(str1Char
, ((str1Char
< 0x10000) ? graphemeBMP
: CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet
, (str1Char
>> 16))))) {
2991 str1Char
= str2Char
;
2994 if ((0 == strBuf2Len
) && CFUniCharIsMemberOfBitmap(str2Char
, ((str2Char
< 0x10000) ? graphemeBMP
: CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet
, (str2Char
>> 16))))) {
2995 str2Char
= str1Char
;
2999 if (str1Skip
!= str2Skip
) {
3000 if (str1Skip
) str2Index
-= str2UsedLen
;
3001 if (str2Skip
) str1Index
-= str1UsedLen
;
3005 if (str1Char
!= str2Char
) {
3006 if (0 == strBuf1Len
) {
3007 strBuf1Len
= __CFStringFoldCharacterClusterAtIndex(str1Char
, &inlineBuf1
, str1Index
, compareOptions
, langCode
, strBuf1
, kCFStringStackBufferLength
, &str1UsedLen
);
3008 if (strBuf1Len
> 0) {
3009 str1Char
= *strBuf1
;
3014 if ((0 == strBuf1Len
) && (0 < strBuf2Len
)) break;
3016 if ((0 == strBuf2Len
) && ((0 == strBuf1Len
) || (str1Char
!= str2Char
))) {
3017 strBuf2Len
= __CFStringFoldCharacterClusterAtIndex(str2Char
, &inlineBuf2
, str2Index
, compareOptions
, langCode
, strBuf2
, kCFStringStackBufferLength
, &str2UsedLen
);
3018 if ((0 == strBuf2Len
) || (str1Char
!= *strBuf2
)) break;
3023 if ((strBuf1Len
> 0) && (strBuf2Len
> 0)) {
3024 while ((strBuf1Index
< strBuf1Len
) && (strBuf2Index
< strBuf2Len
)) {
3025 if (strBuf1
[strBuf1Index
] != strBuf2
[strBuf2Index
]) break;
3026 ++strBuf1Index
; ++strBuf2Index
;
3028 if ((strBuf1Index
< strBuf1Len
) && (strBuf2Index
< strBuf2Len
)) break;
3032 if ((strBuf1Len
> 0) && (strBuf1Index
== strBuf1Len
)) strBuf1Len
= 0;
3033 if ((strBuf2Len
> 0) && (strBuf2Index
== strBuf2Len
)) strBuf2Len
= 0;
3035 if (strBuf1Len
== 0) str1Index
+= str1UsedLen
;
3036 if (strBuf2Len
== 0) str2Index
+= str2UsedLen
;
3039 if ((NULL
!= ignoredChars
) && (str1Index
== maxStr1Index
) && (str2Index
< findStrLen
)) { // Process the stringToFind tail
3040 while (str2Index
< findStrLen
) {
3041 str2Char
= CFStringGetCharacterFromInlineBuffer(&inlineBuf2
, str2Index
);
3042 if (CFUniCharIsSurrogateHighCharacter(str2Char
) && CFUniCharIsSurrogateLowCharacter((otherChar
= CFStringGetCharacterFromInlineBuffer(&inlineBuf2
, str2Index
+ 1)))) {
3043 str2Char
= CFUniCharGetLongCharacterForSurrogatePair(str2Char
, otherChar
);
3045 if (!CFCharacterSetInlineBufferIsLongCharacterMember(ignoredChars
, str2Char
)) break;
3046 str2Index
+= ((str2Char
< 0x10000) ? 1 : 2);
3050 if (str2Index
== findStrLen
) {
3053 if (strBuf1Len
> 0) {
3056 if ((compareOptions
& kCFCompareDiacriticInsensitive
) && (strBuf1
[0] < 0x0510)) {
3057 while (strBuf1Index
< strBuf1Len
) {
3058 if (!CFUniCharIsMemberOfBitmap(strBuf1
[strBuf1Index
], ((strBuf1
[strBuf1Index
] < 0x10000) ? graphemeBMP
: CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet
, (strBuf1
[strBuf1Index
] >> 16))))) break;
3062 if (strBuf1Index
== strBuf1Len
) {
3063 str1Index
+= str1UsedLen
;
3069 if (match
&& (compareOptions
& (kCFCompareDiacriticInsensitive
|kCFCompareNonliteral
)) && (str1Index
< maxStr1Index
)) {
3070 const uint8_t *nonBaseBitmap
;
3072 str1Char
= CFStringGetCharacterFromInlineBuffer(&inlineBuf1
, str1Index
);
3074 if (CFUniCharIsSurrogateHighCharacter(str1Char
) && CFUniCharIsSurrogateLowCharacter((otherChar
= CFStringGetCharacterFromInlineBuffer(&inlineBuf1
, str1Index
+ 1)))) {
3075 str1Char
= CFUniCharGetLongCharacterForSurrogatePair(str1Char
, otherChar
);
3076 nonBaseBitmap
= CFUniCharGetBitmapPtrForPlane(kCFUniCharGraphemeExtendCharacterSet
, (str1Char
>> 16));
3078 nonBaseBitmap
= graphemeBMP
;
3081 if (CFUniCharIsMemberOfBitmap(str1Char
, nonBaseBitmap
)) {
3082 if (diacriticsInsensitive
) {
3083 if (str1Char
< 0x10000) {
3084 CFIndex index
= str1Index
;
3087 str1Char
= CFStringGetCharacterFromInlineBuffer(&inlineBuf1
, --index
);
3088 } while (CFUniCharIsMemberOfBitmap(str1Char
, graphemeBMP
), (rangeToSearch
.location
< index
));
3090 if (str1Char
< 0x0510) {
3091 while (++str1Index
< maxStr1Index
) if (!CFUniCharIsMemberOfBitmap(CFStringGetCharacterFromInlineBuffer(&inlineBuf1
, str1Index
), graphemeBMP
)) break;
3097 } else if (!diacriticsInsensitive
) {
3098 otherChar
= CFStringGetCharacterFromInlineBuffer(&inlineBuf1
, str1Index
- 1);
3100 // this is assuming viramas are only in BMP ???
3101 if ((str1Char
== COMBINING_GRAPHEME_JOINER
) || (otherChar
== COMBINING_GRAPHEME_JOINER
) || (otherChar
== ZERO_WIDTH_JOINER
) || ((otherChar
>= HANGUL_CHOSEONG_START
) && (otherChar
<= HANGUL_JONGSEONG_END
)) || (CFUniCharGetCombiningPropertyForCharacter(otherChar
, combClassBMP
) == 9)) {
3102 CFRange clusterRange
= CFStringGetRangeOfCharacterClusterAtIndex(string
, str1Index
- 1, kCFStringGraphemeCluster
);
3104 if (str1Index
< (clusterRange
.location
+ clusterRange
.length
)) match
= false;
3110 if ((NULL
!= ignoredChars
) && backwardAnchor
&& (str1Index
< maxStr1Index
)) { // Process the anchor tail
3111 while (str1Index
< maxStr1Index
) {
3112 str1Char
= CFStringGetCharacterFromInlineBuffer(&inlineBuf1
, str1Index
);
3113 if (CFUniCharIsSurrogateHighCharacter(str1Char
) && CFUniCharIsSurrogateLowCharacter((otherChar
= CFStringGetCharacterFromInlineBuffer(&inlineBuf1
, str1Index
+ 1)))) {
3114 str1Char
= CFUniCharGetLongCharacterForSurrogatePair(str1Char
, otherChar
);
3116 if (!CFCharacterSetInlineBufferIsLongCharacterMember(ignoredChars
, str1Char
)) break;
3117 str1Index
+= ((str1Char
< 0x10000) ? 1 : 2);
3121 if (!backwardAnchor
|| (str1Index
== maxStr1Index
)) {
3123 if (NULL
!= result
) *result
= CFRangeMake(fromLoc
, str1Index
- fromLoc
);
3129 if (fromLoc
== toLoc
) break;
3134 str1Index
= fromLoc
;
3137 while (str2Index
< findStrLen
) {
3138 if (CFStringGetCharacterFromInlineBuffer(&inlineBuf1
, str1Index
) != CFStringGetCharacterFromInlineBuffer(&inlineBuf2
, str2Index
)) break;
3140 ++str1Index
; ++str2Index
;
3143 if (str2Index
== findStrLen
) {
3145 if (NULL
!= result
) *result
= CFRangeMake(fromLoc
, findStrLen
);
3149 if (fromLoc
== toLoc
) break;
3159 Boolean
CFStringFindWithOptions(CFStringRef string
, CFStringRef stringToFind
, CFRange rangeToSearch
, CFStringCompareFlags compareOptions
, CFRange
*result
) { return CFStringFindWithOptionsAndLocale(string
, stringToFind
, rangeToSearch
, compareOptions
, NULL
, result
); }
3161 // Functions to deal with special arrays of CFRange, CFDataRef, created by CFStringCreateArrayWithFindResults()
3163 static const void *__rangeRetain(CFAllocatorRef allocator
, const void *ptr
) {
3164 CFRetain(*(CFDataRef
*)((uint8_t *)ptr
+ sizeof(CFRange
)));
3168 static void __rangeRelease(CFAllocatorRef allocator
, const void *ptr
) {
3169 CFRelease(*(CFDataRef
*)((uint8_t *)ptr
+ sizeof(CFRange
)));
3172 static CFStringRef
__rangeCopyDescription(const void *ptr
) {
3173 CFRange range
= *(CFRange
*)ptr
;
3174 return CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("{%d, %d}"), range
.location
, range
.length
);
3177 static Boolean
__rangeEqual(const void *ptr1
, const void *ptr2
) {
3178 CFRange range1
= *(CFRange
*)ptr1
;
3179 CFRange range2
= *(CFRange
*)ptr2
;
3180 return (range1
.location
== range2
.location
) && (range1
.length
== range2
.length
);
3184 CFArrayRef
CFStringCreateArrayWithFindResults(CFAllocatorRef alloc
, CFStringRef string
, CFStringRef stringToFind
, CFRange rangeToSearch
, CFStringCompareFlags compareOptions
) {
3186 Boolean backwards
= ((compareOptions
& kCFCompareBackwards
) != 0);
3187 UInt32 endIndex
= rangeToSearch
.location
+ rangeToSearch
.length
;
3188 CFMutableDataRef rangeStorage
= NULL
; // Basically an array of CFRange, CFDataRef (packed)
3189 uint8_t *rangeStorageBytes
= NULL
;
3190 CFIndex foundCount
= 0;
3191 CFIndex capacity
= 0; // Number of CFRange, CFDataRef element slots in rangeStorage
3193 if (alloc
== NULL
) alloc
= __CFGetDefaultAllocator();
3195 while ((rangeToSearch
.length
> 0) && CFStringFindWithOptions(string
, stringToFind
, rangeToSearch
, compareOptions
, &foundRange
)) {
3196 // Determine the next range
3198 rangeToSearch
.length
= foundRange
.location
- rangeToSearch
.location
;
3200 rangeToSearch
.location
= foundRange
.location
+ foundRange
.length
;
3201 rangeToSearch
.length
= endIndex
- rangeToSearch
.location
;
3204 // If necessary, grow the data and squirrel away the found range
3205 if (foundCount
>= capacity
) {
3206 if (rangeStorage
== NULL
) rangeStorage
= CFDataCreateMutable(alloc
, 0);
3207 capacity
= (capacity
+ 4) * 2;
3208 CFDataSetLength(rangeStorage
, capacity
* (sizeof(CFRange
) + sizeof(CFDataRef
)));
3209 rangeStorageBytes
= (uint8_t *)CFDataGetMutableBytePtr(rangeStorage
) + foundCount
* (sizeof(CFRange
) + sizeof(CFDataRef
));
3211 memmove(rangeStorageBytes
, &foundRange
, sizeof(CFRange
)); // The range
3212 memmove(rangeStorageBytes
+ sizeof(CFRange
), &rangeStorage
, sizeof(CFDataRef
)); // The data
3213 rangeStorageBytes
+= (sizeof(CFRange
) + sizeof(CFDataRef
));
3217 if (foundCount
> 0) {
3219 CFMutableArrayRef array
;
3220 const CFArrayCallBacks callbacks
= {0, __rangeRetain
, __rangeRelease
, __rangeCopyDescription
, __rangeEqual
};
3222 CFDataSetLength(rangeStorage
, foundCount
* (sizeof(CFRange
) + sizeof(CFDataRef
))); // Tighten storage up
3223 rangeStorageBytes
= (uint8_t *)CFDataGetMutableBytePtr(rangeStorage
);
3225 array
= CFArrayCreateMutable(alloc
, foundCount
* sizeof(CFRange
*), &callbacks
);
3226 for (cnt
= 0; cnt
< foundCount
; cnt
++) {
3227 // Each element points to the appropriate CFRange in the CFData
3228 CFArrayAppendValue(array
, rangeStorageBytes
+ cnt
* (sizeof(CFRange
) + sizeof(CFDataRef
)));
3230 CFRelease(rangeStorage
); // We want the data to go away when all CFRanges inside it are released...
3238 CFRange
CFStringFind(CFStringRef string
, CFStringRef stringToFind
, CFStringCompareFlags compareOptions
) {
3241 if (CFStringFindWithOptions(string
, stringToFind
, CFRangeMake(0, CFStringGetLength(string
)), compareOptions
, &foundRange
)) {
3244 return CFRangeMake(kCFNotFound
, 0);
3248 Boolean
CFStringHasPrefix(CFStringRef string
, CFStringRef prefix
) {
3249 return CFStringFindWithOptions(string
, prefix
, CFRangeMake(0, CFStringGetLength(string
)), kCFCompareAnchored
, NULL
);
3252 Boolean
CFStringHasSuffix(CFStringRef string
, CFStringRef suffix
) {
3253 return CFStringFindWithOptions(string
, suffix
, CFRangeMake(0, CFStringGetLength(string
)), kCFCompareAnchored
|kCFCompareBackwards
, NULL
);
3256 #define MAX_TRANSCODING_LENGTH 4
3258 #define HANGUL_JONGSEONG_COUNT (28)
3260 CF_INLINE
bool _CFStringIsHangulLVT(UTF32Char character
) {
3261 return (((character
- HANGUL_SYLLABLE_START
) % HANGUL_JONGSEONG_COUNT
) ? true : false);
3264 static uint8_t __CFTranscodingHintLength
[] = {
3265 2, 3, 4, 4, 4, 4, 4, 2, 2, 2, 2, 4, 0, 0, 0, 0
3269 kCFStringHangulStateL
,
3270 kCFStringHangulStateV
,
3271 kCFStringHangulStateT
,
3272 kCFStringHangulStateLV
,
3273 kCFStringHangulStateLVT
,
3274 kCFStringHangulStateBreak
3277 static CFRange
_CFStringInlineBufferGetComposedRange(CFStringInlineBuffer
*buffer
, CFIndex start
, CFStringCharacterClusterType type
, const uint8_t *bmpBitmap
, CFIndex csetType
) {
3278 CFIndex end
= start
+ 1;
3279 const uint8_t *bitmap
= bmpBitmap
;
3280 UTF32Char character
;
3281 UTF16Char otherSurrogate
;
3284 character
= CFStringGetCharacterFromInlineBuffer(buffer
, start
);
3286 // We don't combine characters in Armenian ~ Limbu range for backward deletion
3287 if ((type
!= kCFStringBackwardDeletionCluster
) || (character
< 0x0530) || (character
> 0x194F)) {
3288 // Check if the current is surrogate
3289 if (CFUniCharIsSurrogateHighCharacter(character
) && CFUniCharIsSurrogateLowCharacter((otherSurrogate
= CFStringGetCharacterFromInlineBuffer(buffer
, start
+ 1)))) {
3291 character
= CFUniCharGetLongCharacterForSurrogatePair(character
, otherSurrogate
);
3292 bitmap
= CFUniCharGetBitmapPtrForPlane(csetType
, (character
>> 16));
3297 if ((type
== kCFStringBackwardDeletionCluster
) && (character
>= 0x0530) && (character
< 0x1950)) break;
3299 if (character
< 0x10000) { // the first round could be already be non-BMP
3300 if (CFUniCharIsSurrogateLowCharacter(character
) && CFUniCharIsSurrogateHighCharacter((otherSurrogate
= CFStringGetCharacterFromInlineBuffer(buffer
, start
- 1)))) {
3301 character
= CFUniCharGetLongCharacterForSurrogatePair(otherSurrogate
, character
);
3302 bitmap
= CFUniCharGetBitmapPtrForPlane(csetType
, (character
>> 16));
3309 if (!CFUniCharIsMemberOfBitmap(character
, bitmap
) && (character
!= 0xFF9E) && (character
!= 0xFF9F) && ((character
& 0x1FFFF0) != 0xF870)) break;
3313 character
= CFStringGetCharacterFromInlineBuffer(buffer
, start
);
3318 if (((character
>= HANGUL_CHOSEONG_START
) && (character
<= HANGUL_JONGSEONG_END
)) || ((character
>= HANGUL_SYLLABLE_START
) && (character
<= HANGUL_SYLLABLE_END
))) {
3320 uint8_t initialState
;
3322 if (character
< HANGUL_JUNGSEONG_START
) {
3323 state
= kCFStringHangulStateL
;
3324 } else if (character
< HANGUL_JONGSEONG_START
) {
3325 state
= kCFStringHangulStateV
;
3326 } else if (character
< HANGUL_SYLLABLE_START
) {
3327 state
= kCFStringHangulStateT
;
3329 state
= (_CFStringIsHangulLVT(character
) ? kCFStringHangulStateLVT
: kCFStringHangulStateLV
);
3331 initialState
= state
;
3334 while (((character
= CFStringGetCharacterFromInlineBuffer(buffer
, start
- 1)) >= HANGUL_CHOSEONG_START
) && (character
<= HANGUL_SYLLABLE_END
) && ((character
<= HANGUL_JONGSEONG_END
) || (character
>= HANGUL_SYLLABLE_START
))) {
3336 case kCFStringHangulStateV
:
3337 if (character
<= HANGUL_CHOSEONG_END
) {
3338 state
= kCFStringHangulStateL
;
3339 } else if ((character
>= HANGUL_SYLLABLE_START
) && (character
<= HANGUL_SYLLABLE_END
) && !_CFStringIsHangulLVT(character
)) {
3340 state
= kCFStringHangulStateLV
;
3341 } else if (character
> HANGUL_JUNGSEONG_END
) {
3342 state
= kCFStringHangulStateBreak
;
3346 case kCFStringHangulStateT
:
3347 if ((character
>= HANGUL_JUNGSEONG_START
) && (character
<= HANGUL_JUNGSEONG_END
)) {
3348 state
= kCFStringHangulStateV
;
3349 } else if ((character
>= HANGUL_SYLLABLE_START
) && (character
<= HANGUL_SYLLABLE_END
)) {
3350 state
= (_CFStringIsHangulLVT(character
) ? kCFStringHangulStateLVT
: kCFStringHangulStateLV
);
3351 } else if (character
< HANGUL_JUNGSEONG_START
) {
3352 state
= kCFStringHangulStateBreak
;
3357 state
= ((character
< HANGUL_JUNGSEONG_START
) ? kCFStringHangulStateL
: kCFStringHangulStateBreak
);
3361 if (state
== kCFStringHangulStateBreak
) break;
3366 state
= initialState
;
3367 while (((character
= CFStringGetCharacterFromInlineBuffer(buffer
, end
)) > 0) && (((character
>= HANGUL_CHOSEONG_START
) && (character
<= HANGUL_JONGSEONG_END
)) || ((character
>= HANGUL_SYLLABLE_START
) && (character
<= HANGUL_SYLLABLE_END
)))) {
3369 case kCFStringHangulStateLV
:
3370 case kCFStringHangulStateV
:
3371 if ((character
>= HANGUL_JUNGSEONG_START
) && (character
<= HANGUL_JONGSEONG_END
)) {
3372 state
= ((character
< HANGUL_JONGSEONG_START
) ? kCFStringHangulStateV
: kCFStringHangulStateT
);
3374 state
= kCFStringHangulStateBreak
;
3378 case kCFStringHangulStateLVT
:
3379 case kCFStringHangulStateT
:
3380 state
= (((character
>= HANGUL_JONGSEONG_START
) && (character
<= HANGUL_JONGSEONG_END
)) ? kCFStringHangulStateT
: kCFStringHangulStateBreak
);
3384 if (character
< HANGUL_JUNGSEONG_START
) {
3385 state
= kCFStringHangulStateL
;
3386 } else if (character
< HANGUL_JONGSEONG_START
) {
3387 state
= kCFStringHangulStateV
;
3388 } else if (character
>= HANGUL_SYLLABLE_START
) {
3389 state
= (_CFStringIsHangulLVT(character
) ? kCFStringHangulStateLVT
: kCFStringHangulStateLV
);
3391 state
= kCFStringHangulStateBreak
;
3396 if (state
== kCFStringHangulStateBreak
) break;
3402 while ((character
= CFStringGetCharacterFromInlineBuffer(buffer
, end
)) > 0) {
3403 if ((type
== kCFStringBackwardDeletionCluster
) && (character
>= 0x0530) && (character
< 0x1950)) break;
3405 if (CFUniCharIsSurrogateHighCharacter(character
) && CFUniCharIsSurrogateLowCharacter((otherSurrogate
= CFStringGetCharacterFromInlineBuffer(buffer
, end
+ 1)))) {
3406 character
= CFUniCharGetLongCharacterForSurrogatePair(character
, otherSurrogate
);
3407 bitmap
= CFUniCharGetBitmapPtrForPlane(csetType
, (character
>> 16));
3414 if (!CFUniCharIsMemberOfBitmap(character
, bitmap
) && (character
!= 0xFF9E) && (character
!= 0xFF9F) && ((character
& 0x1FFFF0) != 0xF870)) break;
3419 return CFRangeMake(start
, end
- start
);
3422 CF_INLINE
bool _CFStringIsVirama(UTF32Char character
, const uint8_t *combClassBMP
) {
3423 return ((character
== COMBINING_GRAPHEME_JOINER
) || (CFUniCharGetCombiningPropertyForCharacter(character
, (const uint8_t *)((character
< 0x10000) ? combClassBMP
: CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty
, (character
>> 16)))) == 9) ? true : false);
3426 CFRange
CFStringGetRangeOfCharacterClusterAtIndex(CFStringRef string
, CFIndex charIndex
, CFStringCharacterClusterType type
) {
3428 CFIndex currentIndex
;
3429 CFIndex length
= CFStringGetLength(string
);
3430 CFIndex csetType
= ((kCFStringGraphemeCluster
== type
) ? kCFUniCharGraphemeExtendCharacterSet
: kCFUniCharNonBaseCharacterSet
);
3431 CFStringInlineBuffer stringBuffer
;
3432 const uint8_t *bmpBitmap
;
3433 const uint8_t *letterBMP
;
3434 const uint8_t *combClassBMP
;
3435 UTF32Char character
;
3436 UTF16Char otherSurrogate
;
3438 if (charIndex
>= length
) return CFRangeMake(kCFNotFound
, 0);
3440 /* Fast case. If we're eight-bit, it's either the default encoding is cheap or the content is all ASCII. Watch out when (or if) adding more 8bit Mac-scripts in CFStringEncodingConverters
3442 if (!CF_IS_OBJC(__kCFStringTypeID
, string
) && __CFStrIsEightBit(string
)) return CFRangeMake(charIndex
, 1);
3444 bmpBitmap
= CFUniCharGetBitmapPtrForPlane(csetType
, 0);
3445 letterBMP
= CFUniCharGetBitmapPtrForPlane(kCFUniCharLetterCharacterSet
, 0);
3446 combClassBMP
= (const uint8_t *)CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty
, 0);
3448 CFStringInitInlineBuffer(string
, &stringBuffer
, CFRangeMake(0, length
));
3450 // Get composed character sequence first
3451 range
= _CFStringInlineBufferGetComposedRange(&stringBuffer
, charIndex
, type
, bmpBitmap
, csetType
);
3453 // Do grapheme joiners
3454 if (type
< kCFStringCursorMovementCluster
) {
3455 const uint8_t *letter
= letterBMP
;
3457 // Check to see if we have a letter at the beginning of initial cluster
3458 character
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, range
.location
);
3460 if ((range
.length
> 1) && CFUniCharIsSurrogateHighCharacter(character
) && CFUniCharIsSurrogateLowCharacter((otherSurrogate
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, range
.location
+ 1)))) {
3461 character
= CFUniCharGetLongCharacterForSurrogatePair(character
, otherSurrogate
);
3462 letter
= CFUniCharGetBitmapPtrForPlane(kCFUniCharLetterCharacterSet
, (character
>> 16));
3465 if ((character
== ZERO_WIDTH_JOINER
) || CFUniCharIsMemberOfBitmap(character
, letter
)) {
3468 // Check if preceded by grapheme joiners (U034F and viramas)
3469 otherRange
.location
= currentIndex
= range
.location
;
3471 while (currentIndex
> 1) {
3472 character
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, --currentIndex
);
3474 // ??? We're assuming viramas only in BMP
3475 if ((_CFStringIsVirama(character
, combClassBMP
) || ((character
== ZERO_WIDTH_JOINER
) && _CFStringIsVirama(CFStringGetCharacterFromInlineBuffer(&stringBuffer
, --currentIndex
), combClassBMP
))) && (currentIndex
> 0)) {
3481 currentIndex
= _CFStringInlineBufferGetComposedRange(&stringBuffer
, currentIndex
, type
, bmpBitmap
, csetType
).location
;
3483 character
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, currentIndex
);
3485 if (CFUniCharIsSurrogateLowCharacter(character
) && CFUniCharIsSurrogateHighCharacter((otherSurrogate
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, currentIndex
- 1)))) {
3486 character
= CFUniCharGetLongCharacterForSurrogatePair(character
, otherSurrogate
);
3487 letter
= CFUniCharGetBitmapPtrForPlane(kCFUniCharLetterCharacterSet
, (character
>> 16));
3493 if (!CFUniCharIsMemberOfBitmap(character
, letter
)) break;
3494 range
.location
= currentIndex
;
3497 range
.length
+= otherRange
.location
- range
.location
;
3499 // Check if followed by grapheme joiners
3500 if ((range
.length
> 1) && ((range
.location
+ range
.length
) < length
)) {
3502 currentIndex
= otherRange
.location
+ otherRange
.length
;
3505 character
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, currentIndex
- 1);
3507 // ??? We're assuming viramas only in BMP
3508 if ((character
!= ZERO_WIDTH_JOINER
) && !_CFStringIsVirama(character
, combClassBMP
)) break;
3510 character
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, currentIndex
);
3512 if (character
== ZERO_WIDTH_JOINER
) character
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, ++currentIndex
);
3514 if (CFUniCharIsSurrogateHighCharacter(character
) && CFUniCharIsSurrogateLowCharacter((otherSurrogate
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, currentIndex
+ 1)))) {
3515 character
= CFUniCharGetLongCharacterForSurrogatePair(character
, otherSurrogate
);
3516 letter
= CFUniCharGetBitmapPtrForPlane(kCFUniCharLetterCharacterSet
, (character
>> 16));
3521 // We only conjoin letters
3522 if (!CFUniCharIsMemberOfBitmap(character
, letter
)) break;
3523 otherRange
= _CFStringInlineBufferGetComposedRange(&stringBuffer
, currentIndex
, type
, bmpBitmap
, csetType
);
3524 currentIndex
= otherRange
.location
+ otherRange
.length
;
3525 } while ((otherRange
.location
+ otherRange
.length
) < length
);
3526 range
.length
= currentIndex
- range
.location
;
3531 // Check if we're part of prefix transcoding hints
3534 currentIndex
= (range
.location
+ range
.length
) - (MAX_TRANSCODING_LENGTH
+ 1);
3535 if (currentIndex
< 0) currentIndex
= 0;
3537 while (currentIndex
<= range
.location
) {
3538 character
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, currentIndex
);
3540 if ((character
& 0x1FFFF0) == 0xF860) { // transcoding hint
3541 otherIndex
= currentIndex
+ __CFTranscodingHintLength
[(character
- 0xF860)] + 1;
3542 if (otherIndex
>= (range
.location
+ range
.length
)) {
3543 if (otherIndex
<= length
) {
3544 range
.location
= currentIndex
;
3545 range
.length
= otherIndex
- currentIndex
;
3556 CFRange
CFStringGetRangeOfComposedCharactersAtIndex(CFStringRef theString
, CFIndex theIndex
) {
3557 return CFStringGetRangeOfCharacterClusterAtIndex(theString
, theIndex
, kCFStringComposedCharacterCluster
);
3561 @function CFStringFindCharacterFromSet
3562 Query the range of characters contained in the specified character set.
3563 @param theString The CFString which is to be searched. If this
3564 parameter is not a valid CFString, the behavior is
3566 @param theSet The CFCharacterSet against which the membership
3567 of characters is checked. If this parameter is not a valid
3568 CFCharacterSet, the behavior is undefined.
3569 @param range The range of characters within the string to search. If
3570 the range location or end point (defined by the location
3571 plus length minus 1) are outside the index space of the
3572 string (0 to N-1 inclusive, where N is the length of the
3573 string), the behavior is undefined. If the range length is
3574 negative, the behavior is undefined. The range may be empty
3575 (length 0), in which case no search is performed.
3576 @param searchOptions The bitwise-or'ed option flags to control
3577 the search behavior. The supported options are
3578 kCFCompareBackwards andkCFCompareAnchored.
3579 If other option flags are specified, the behavior
3581 @param result The pointer to a CFRange supplied by the caller in
3582 which the search result is stored. If a pointer to an invalid
3583 memory is specified, the behavior is undefined.
3584 @result true, if at least a character which is a member of the character
3585 set is found and result is filled, otherwise, false.
3587 #define SURROGATE_START 0xD800
3588 #define SURROGATE_END 0xDFFF
3590 CF_EXPORT Boolean
CFStringFindCharacterFromSet(CFStringRef theString
, CFCharacterSetRef theSet
, CFRange rangeToSearch
, CFStringCompareFlags searchOptions
, CFRange
*result
) {
3591 CFStringInlineBuffer stringBuffer
;
3592 CFCharacterSetInlineBuffer csetBuffer
;
3595 CFIndex fromLoc
, toLoc
, cnt
; // fromLoc and toLoc are inclusive
3596 Boolean found
= false;
3597 Boolean done
= false;
3599 //#warning FIX ME !! Should support kCFCompareNonliteral
3601 if ((rangeToSearch
.location
+ rangeToSearch
.length
> CFStringGetLength(theString
)) || (rangeToSearch
.length
== 0)) return false;
3603 if (searchOptions
& kCFCompareBackwards
) {
3604 fromLoc
= rangeToSearch
.location
+ rangeToSearch
.length
- 1;
3605 toLoc
= rangeToSearch
.location
;
3607 fromLoc
= rangeToSearch
.location
;
3608 toLoc
= rangeToSearch
.location
+ rangeToSearch
.length
- 1;
3610 if (searchOptions
& kCFCompareAnchored
) {
3614 step
= (fromLoc
<= toLoc
) ? 1 : -1;
3617 CFStringInitInlineBuffer(theString
, &stringBuffer
, rangeToSearch
);
3618 CFCharacterSetInitInlineBuffer(theSet
, &csetBuffer
);
3621 ch
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, cnt
- rangeToSearch
.location
);
3622 if ((ch
>= SURROGATE_START
) && (ch
<= SURROGATE_END
)) {
3623 int otherCharIndex
= cnt
+ step
;
3625 if (((step
< 0) && (otherCharIndex
< toLoc
)) || ((step
> 0) && (otherCharIndex
> toLoc
))) {
3629 UniChar lowChar
= CFStringGetCharacterFromInlineBuffer(&stringBuffer
, otherCharIndex
- rangeToSearch
.location
);
3631 if (cnt
< otherCharIndex
) {
3638 if (CFUniCharIsSurrogateHighCharacter(highChar
) && CFUniCharIsSurrogateLowCharacter(lowChar
) && CFCharacterSetInlineBufferIsLongCharacterMember(&csetBuffer
, CFUniCharGetLongCharacterForSurrogatePair(highChar
, lowChar
))) {
3639 if (result
) *result
= CFRangeMake((cnt
< otherCharIndex
? cnt
: otherCharIndex
), 2);
3641 } else if (otherCharIndex
== toLoc
) {
3644 cnt
= otherCharIndex
+ step
;
3647 } else if (CFCharacterSetInlineBufferIsLongCharacterMember(&csetBuffer
, ch
)) {
3648 done
= found
= true;
3649 } else if (cnt
== toLoc
) {
3656 if (found
&& result
) *result
= CFRangeMake(cnt
, 1);
3660 /* Line range code */
3662 #define CarriageReturn '\r' /* 0x0d */
3663 #define NewLine '\n' /* 0x0a */
3664 #define NextLine 0x0085
3665 #define LineSeparator 0x2028
3666 #define ParaSeparator 0x2029
3668 CF_INLINE Boolean
isALineSeparatorTypeCharacter(UniChar ch
, Boolean includeLineEndings
) {
3669 if (ch
> CarriageReturn
&& ch
< NextLine
) return false; /* Quick test to cover most chars */
3670 return (ch
== NewLine
|| ch
== CarriageReturn
|| ch
== ParaSeparator
|| (includeLineEndings
&& (ch
== NextLine
|| ch
== LineSeparator
))) ? true : false;
3673 static void __CFStringGetLineOrParagraphBounds(CFStringRef string
, CFRange range
, CFIndex
*lineBeginIndex
, CFIndex
*lineEndIndex
, CFIndex
*contentsEndIndex
, Boolean includeLineEndings
) {
3675 CFStringInlineBuffer buf
;
3678 __CFAssertIsString(string
);
3679 __CFAssertRangeIsInStringBounds(string
, range
.location
, range
.length
);
3681 len
= __CFStrLength(string
);
3683 if (lineBeginIndex
) {
3685 if (range
.location
== 0) {
3688 CFStringInitInlineBuffer(string
, &buf
, CFRangeMake(0, len
));
3689 CFIndex buf_idx
= range
.location
;
3691 /* Take care of the special case where start happens to fall right between \r and \n */
3692 ch
= CFStringGetCharacterFromInlineBuffer(&buf
, buf_idx
);
3694 if ((ch
== NewLine
) && (CFStringGetCharacterFromInlineBuffer(&buf
, buf_idx
) == CarriageReturn
)) {
3701 } else if (isALineSeparatorTypeCharacter(CFStringGetCharacterFromInlineBuffer(&buf
, buf_idx
), includeLineEndings
)) {
3702 start
= buf_idx
+ 1;
3709 *lineBeginIndex
= start
;
3712 /* Now find the ending point */
3713 if (lineEndIndex
|| contentsEndIndex
) {
3714 CFIndex endOfContents
, lineSeparatorLength
= 1; /* 1 by default */
3715 CFStringInitInlineBuffer(string
, &buf
, CFRangeMake(0, len
));
3716 CFIndex buf_idx
= range
.location
+ range
.length
- (range
.length
? 1 : 0);
3717 /* First look at the last char in the range (if the range is zero length, the char after the range) to see if we're already on or within a end of line sequence... */
3718 ch
= __CFStringGetCharacterFromInlineBufferAux(&buf
, buf_idx
);
3719 if (ch
== NewLine
) {
3720 endOfContents
= buf_idx
;
3722 if (__CFStringGetCharacterFromInlineBufferAux(&buf
, buf_idx
) == CarriageReturn
) {
3723 lineSeparatorLength
= 2;
3728 if (isALineSeparatorTypeCharacter(ch
, includeLineEndings
)) {
3729 endOfContents
= buf_idx
; /* This is actually end of contentsRange */
3730 buf_idx
++; /* OK for this to go past the end */
3731 if ((ch
== CarriageReturn
) && (__CFStringGetCharacterFromInlineBufferAux(&buf
, buf_idx
) == NewLine
)) {
3732 lineSeparatorLength
= 2;
3735 } else if (buf_idx
>= len
) {
3736 endOfContents
= len
;
3737 lineSeparatorLength
= 0;
3741 ch
= __CFStringGetCharacterFromInlineBufferAux(&buf
, buf_idx
);
3745 if (contentsEndIndex
) *contentsEndIndex
= endOfContents
;
3746 if (lineEndIndex
) *lineEndIndex
= endOfContents
+ lineSeparatorLength
;
3750 void CFStringGetLineBounds(CFStringRef string
, CFRange range
, CFIndex
*lineBeginIndex
, CFIndex
*lineEndIndex
, CFIndex
*contentsEndIndex
) {
3751 CF_OBJC_FUNCDISPATCH4(__kCFStringTypeID
, void, string
, "getLineStart:end:contentsEnd:forRange:", lineBeginIndex
, lineEndIndex
, contentsEndIndex
, CFRangeMake(range
.location
, range
.length
));
3752 __CFStringGetLineOrParagraphBounds(string
, range
, lineBeginIndex
, lineEndIndex
, contentsEndIndex
, true);
3755 void CFStringGetParagraphBounds(CFStringRef string
, CFRange range
, CFIndex
*parBeginIndex
, CFIndex
*parEndIndex
, CFIndex
*contentsEndIndex
) {
3756 CF_OBJC_FUNCDISPATCH4(__kCFStringTypeID
, void, string
, "getParagraphStart:end:contentsEnd:forRange:", parBeginIndex
, parEndIndex
, contentsEndIndex
, CFRangeMake(range
.location
, range
.length
));
3757 __CFStringGetLineOrParagraphBounds(string
, range
, parBeginIndex
, parEndIndex
, contentsEndIndex
, false);
3761 CFStringRef
CFStringCreateByCombiningStrings(CFAllocatorRef alloc
, CFArrayRef array
, CFStringRef separatorString
) {
3763 CFIndex separatorNumByte
;
3764 CFIndex stringCount
= CFArrayGetCount(array
);
3765 Boolean isSepCFString
= !CF_IS_OBJC(__kCFStringTypeID
, separatorString
);
3766 Boolean canBeEightbit
= isSepCFString
&& __CFStrIsEightBit(separatorString
);
3768 CFStringRef otherString
;
3771 const void *separatorContents
= NULL
;
3773 if (stringCount
== 0) {
3774 return CFStringCreateWithCharacters(alloc
, NULL
, 0);
3775 } else if (stringCount
== 1) {
3776 return (CFStringRef
)CFStringCreateCopy(alloc
, (CFStringRef
)CFArrayGetValueAtIndex(array
, 0));
3779 if (alloc
== NULL
) alloc
= __CFGetDefaultAllocator();
3781 numChars
= CFStringGetLength(separatorString
) * (stringCount
- 1);
3782 for (idx
= 0; idx
< stringCount
; idx
++) {
3783 otherString
= (CFStringRef
)CFArrayGetValueAtIndex(array
, idx
);
3784 numChars
+= CFStringGetLength(otherString
);
3785 // canBeEightbit is already false if the separator is an NSString...
3786 if (!CF_IS_OBJC(__kCFStringTypeID
, otherString
) && __CFStrIsUnicode(otherString
)) canBeEightbit
= false;
3789 buffer
= (uint8_t *)CFAllocatorAllocate(alloc
, canBeEightbit
? ((numChars
+ 1) * sizeof(uint8_t)) : (numChars
* sizeof(UniChar
)), 0);
3790 bufPtr
= (uint8_t *)buffer
;
3791 if (__CFOASafe
) __CFSetLastAllocationEventName(buffer
, "CFString (store)");
3792 separatorNumByte
= CFStringGetLength(separatorString
) * (canBeEightbit
? sizeof(uint8_t) : sizeof(UniChar
));
3794 for (idx
= 0; idx
< stringCount
; idx
++) {
3795 if (idx
) { // add separator here unless first string
3796 if (separatorContents
) {
3797 memmove(bufPtr
, separatorContents
, separatorNumByte
);
3799 if (!isSepCFString
) { // NSString
3800 CFStringGetCharacters(separatorString
, CFRangeMake(0, CFStringGetLength(separatorString
)), (UniChar
*)bufPtr
);
3801 } else if (canBeEightbit
|| __CFStrIsUnicode(separatorString
)) {
3802 memmove(bufPtr
, (const uint8_t *)__CFStrContents(separatorString
) + __CFStrSkipAnyLengthByte(separatorString
), separatorNumByte
);
3804 __CFStrConvertBytesToUnicode((uint8_t *)__CFStrContents(separatorString
) + __CFStrSkipAnyLengthByte(separatorString
), (UniChar
*)bufPtr
, __CFStrLength(separatorString
));
3806 separatorContents
= bufPtr
;
3808 bufPtr
+= separatorNumByte
;
3811 otherString
= (CFStringRef
)CFArrayGetValueAtIndex(array
, idx
);
3812 if (CF_IS_OBJC(__kCFStringTypeID
, otherString
)) {
3813 CFIndex otherLength
= CFStringGetLength(otherString
);
3814 CFStringGetCharacters(otherString
, CFRangeMake(0, otherLength
), (UniChar
*)bufPtr
);
3815 bufPtr
+= otherLength
* sizeof(UniChar
);
3817 const uint8_t * otherContents
= (const uint8_t *)__CFStrContents(otherString
);
3818 CFIndex otherNumByte
= __CFStrLength2(otherString
, otherContents
) * (canBeEightbit
? sizeof(uint8_t) : sizeof(UniChar
));
3820 if (canBeEightbit
|| __CFStrIsUnicode(otherString
)) {
3821 memmove(bufPtr
, otherContents
+ __CFStrSkipAnyLengthByte(otherString
), otherNumByte
);
3823 __CFStrConvertBytesToUnicode(otherContents
+ __CFStrSkipAnyLengthByte(otherString
), (UniChar
*)bufPtr
, __CFStrLength2(otherString
, otherContents
));
3825 bufPtr
+= otherNumByte
;
3828 if (canBeEightbit
) *bufPtr
= 0; // NULL byte;
3830 return canBeEightbit
?
3831 CFStringCreateWithCStringNoCopy(alloc
, (const char*)buffer
, __CFStringGetEightBitStringEncoding(), alloc
) :
3832 CFStringCreateWithCharactersNoCopy(alloc
, (UniChar
*)buffer
, numChars
, alloc
);
3836 CFArrayRef
CFStringCreateArrayBySeparatingStrings(CFAllocatorRef alloc
, CFStringRef string
, CFStringRef separatorString
) {
3837 CFArrayRef separatorRanges
;
3838 CFIndex length
= CFStringGetLength(string
);
3839 /* No objc dispatch needed here since CFStringCreateArrayWithFindResults() works with both CFString and NSString */
3840 if (!(separatorRanges
= CFStringCreateArrayWithFindResults(alloc
, string
, separatorString
, CFRangeMake(0, length
), 0))) {
3841 return CFArrayCreate(alloc
, (const void **)&string
, 1, & kCFTypeArrayCallBacks
);
3844 CFIndex count
= CFArrayGetCount(separatorRanges
);
3845 CFIndex startIndex
= 0;
3847 CFMutableArrayRef array
= CFArrayCreateMutable(alloc
, count
+ 2, & kCFTypeArrayCallBacks
);
3848 const CFRange
*currentRange
;
3849 CFStringRef substring
;
3851 for (idx
= 0;idx
< count
;idx
++) {
3852 currentRange
= (const CFRange
*)CFArrayGetValueAtIndex(separatorRanges
, idx
);
3853 numChars
= currentRange
->location
- startIndex
;
3854 substring
= CFStringCreateWithSubstring(alloc
, string
, CFRangeMake(startIndex
, numChars
));
3855 CFArrayAppendValue(array
, substring
);
3856 CFRelease(substring
);
3857 startIndex
= currentRange
->location
+ currentRange
->length
;
3859 substring
= CFStringCreateWithSubstring(alloc
, string
, CFRangeMake(startIndex
, length
- startIndex
));
3860 CFArrayAppendValue(array
, substring
);
3861 CFRelease(substring
);
3863 CFRelease(separatorRanges
);
3869 CFStringRef
CFStringCreateFromExternalRepresentation(CFAllocatorRef alloc
, CFDataRef data
, CFStringEncoding encoding
) {
3870 return CFStringCreateWithBytes(alloc
, CFDataGetBytePtr(data
), CFDataGetLength(data
), encoding
, true);
3874 CFDataRef
CFStringCreateExternalRepresentation(CFAllocatorRef alloc
, CFStringRef string
, CFStringEncoding encoding
, uint8_t lossByte
) {
3876 CFIndex guessedByteLength
;
3881 if (CF_IS_OBJC(__kCFStringTypeID
, string
)) { /* ??? Hope the compiler optimizes this away if OBJC_MAPPINGS is not on */
3882 length
= CFStringGetLength(string
);
3884 __CFAssertIsString(string
);
3885 length
= __CFStrLength(string
);
3886 if (__CFStrIsEightBit(string
) && ((__CFStringGetEightBitStringEncoding() == encoding
) || (__CFStringGetEightBitStringEncoding() == kCFStringEncodingASCII
&& __CFStringEncodingIsSupersetOfASCII(encoding
)))) { // Requested encoding is equal to the encoding in string
3887 return CFDataCreate(alloc
, ((uint8_t *)__CFStrContents(string
) + __CFStrSkipAnyLengthByte(string
)), __CFStrLength(string
));
3891 if (alloc
== NULL
) alloc
= __CFGetDefaultAllocator();
3893 if (((encoding
& 0x0FFF) == kCFStringEncodingUnicode
) && ((encoding
== kCFStringEncodingUnicode
) || ((encoding
> kCFStringEncodingUTF8
) && (encoding
<= kCFStringEncodingUTF32LE
)))) {
3894 guessedByteLength
= (length
+ 1) * ((((encoding
>> 26) & 2) == 0) ? sizeof(UTF16Char
) : sizeof(UTF32Char
)); // UTF32 format has the bit set
3895 } else if (((guessedByteLength
= CFStringGetMaximumSizeForEncoding(length
, encoding
)) > length
) && !CF_IS_OBJC(__kCFStringTypeID
, string
)) { // Multi byte encoding
3896 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
3897 if (__CFStrIsUnicode(string
)) {
3898 CFIndex aLength
= CFStringEncodingByteLengthForCharacters(encoding
, kCFStringEncodingPrependBOM
, __CFStrContents(string
), __CFStrLength(string
));
3899 if (aLength
> 0) guessedByteLength
= aLength
;
3902 result
= __CFStringEncodeByteStream(string
, 0, length
, true, encoding
, lossByte
, NULL
, LONG_MAX
, &guessedByteLength
);
3903 // if result == length, we always succeed
3904 // otherwise, if result == 0, we fail
3905 // otherwise, if there was a lossByte but still result != length, we fail
3906 if ((result
!= length
) && (!result
|| !lossByte
)) return NULL
;
3907 if (guessedByteLength
== length
&& __CFStrIsEightBit(string
) && __CFStringEncodingIsSupersetOfASCII(encoding
)) { // It's all ASCII !!
3908 return CFDataCreate(alloc
, ((uint8_t *)__CFStrContents(string
) + __CFStrSkipAnyLengthByte(string
)), __CFStrLength(string
));
3910 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
3914 bytes
= (uint8_t *)CFAllocatorAllocate(alloc
, guessedByteLength
, 0);
3915 if (__CFOASafe
) __CFSetLastAllocationEventName(bytes
, "CFData (store)");
3917 result
= __CFStringEncodeByteStream(string
, 0, length
, true, encoding
, lossByte
, bytes
, guessedByteLength
, &usedLength
);
3919 if ((result
!= length
) && (!result
|| !lossByte
)) { // see comment above about what this means
3920 CFAllocatorDeallocate(alloc
, bytes
);
3924 return CFDataCreateWithBytesNoCopy(alloc
, (uint8_t *)bytes
, usedLength
, alloc
);
3928 CFStringEncoding
CFStringGetSmallestEncoding(CFStringRef str
) {
3930 CF_OBJC_FUNCDISPATCH0(__kCFStringTypeID
, CFStringEncoding
, str
, "_smallestEncodingInCFStringEncoding");
3931 __CFAssertIsString(str
);
3933 if (__CFStrIsEightBit(str
)) return __CFStringGetEightBitStringEncoding();
3934 len
= __CFStrLength(str
);
3935 if (__CFStringEncodeByteStream(str
, 0, len
, false, __CFStringGetEightBitStringEncoding(), 0, NULL
, LONG_MAX
, NULL
) == len
) return __CFStringGetEightBitStringEncoding();
3936 if ((__CFStringGetEightBitStringEncoding() != __CFStringGetSystemEncoding()) && (__CFStringEncodeByteStream(str
, 0, len
, false, __CFStringGetSystemEncoding(), 0, NULL
, LONG_MAX
, NULL
) == len
)) return __CFStringGetSystemEncoding();
3937 return kCFStringEncodingUnicode
; /* ??? */
3941 CFStringEncoding
CFStringGetFastestEncoding(CFStringRef str
) {
3942 CF_OBJC_FUNCDISPATCH0(__kCFStringTypeID
, CFStringEncoding
, str
, "_fastestEncodingInCFStringEncoding");
3943 __CFAssertIsString(str
);
3944 return __CFStrIsEightBit(str
) ? __CFStringGetEightBitStringEncoding() : kCFStringEncodingUnicode
; /* ??? */
3948 SInt32
CFStringGetIntValue(CFStringRef str
) {
3952 CFStringInlineBuffer buf
;
3953 CFStringInitInlineBuffer(str
, &buf
, CFRangeMake(0, CFStringGetLength(str
)));
3954 success
= __CFStringScanInteger(&buf
, NULL
, &idx
, false, &result
);
3955 return success
? result
: 0;
3959 double CFStringGetDoubleValue(CFStringRef str
) {
3963 CFStringInlineBuffer buf
;
3964 CFStringInitInlineBuffer(str
, &buf
, CFRangeMake(0, CFStringGetLength(str
)));
3965 success
= __CFStringScanDouble(&buf
, NULL
, &idx
, &result
);
3966 return success
? result
: 0.0;
3970 /*** Mutable functions... ***/
3972 void CFStringSetExternalCharactersNoCopy(CFMutableStringRef string
, UniChar
*chars
, CFIndex length
, CFIndex capacity
) {
3973 __CFAssertIsNotNegative(length
);
3974 __CFAssertIsStringAndExternalMutable(string
);
3975 CFAssert4((length
<= capacity
) && ((capacity
== 0) || ((capacity
> 0) && chars
)), __kCFLogAssertion
, "%s(): Invalid args: characters %p length %d capacity %d", __PRETTY_FUNCTION__
, chars
, length
, capacity
);
3976 __CFStrSetContentPtr(string
, chars
);
3977 __CFStrSetExplicitLength(string
, length
);
3978 __CFStrSetCapacity(string
, capacity
* sizeof(UniChar
));
3979 __CFStrSetCapacityProvidedExternally(string
);
3984 void CFStringInsert(CFMutableStringRef str
, CFIndex idx
, CFStringRef insertedStr
) {
3985 CF_OBJC_FUNCDISPATCH2(__kCFStringTypeID
, void, str
, "insertString:atIndex:", insertedStr
, idx
);
3986 __CFAssertIsStringAndMutable(str
);
3987 CFAssert3(idx
>= 0 && idx
<= __CFStrLength(str
), __kCFLogAssertion
, "%s(): string index %d out of bounds (length %d)", __PRETTY_FUNCTION__
, idx
, __CFStrLength(str
));
3988 __CFStringReplace(str
, CFRangeMake(idx
, 0), insertedStr
);
3992 void CFStringDelete(CFMutableStringRef str
, CFRange range
) {
3993 CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID
, void, str
, "deleteCharactersInRange:", range
);
3994 __CFAssertIsStringAndMutable(str
);
3995 __CFAssertRangeIsInStringBounds(str
, range
.location
, range
.length
);
3996 __CFStringChangeSize(str
, range
, 0, false);
4000 void CFStringReplace(CFMutableStringRef str
, CFRange range
, CFStringRef replacement
) {
4001 CF_OBJC_FUNCDISPATCH2(__kCFStringTypeID
, void, str
, "replaceCharactersInRange:withString:", range
, replacement
);
4002 __CFAssertIsStringAndMutable(str
);
4003 __CFAssertRangeIsInStringBounds(str
, range
.location
, range
.length
);
4004 __CFStringReplace(str
, range
, replacement
);
4008 void CFStringReplaceAll(CFMutableStringRef str
, CFStringRef replacement
) {
4009 CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID
, void, str
, "setString:", replacement
);
4010 __CFAssertIsStringAndMutable(str
);
4011 __CFStringReplace(str
, CFRangeMake(0, __CFStrLength(str
)), replacement
);
4015 void CFStringAppend(CFMutableStringRef str
, CFStringRef appended
) {
4016 CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID
, void, str
, "appendString:", appended
);
4017 __CFAssertIsStringAndMutable(str
);
4018 __CFStringReplace(str
, CFRangeMake(__CFStrLength(str
), 0), appended
);
4022 void CFStringAppendCharacters(CFMutableStringRef str
, const UniChar
*chars
, CFIndex appendedLength
) {
4023 CFIndex strLength
, idx
;
4025 __CFAssertIsNotNegative(appendedLength
);
4027 CF_OBJC_FUNCDISPATCH2(__kCFStringTypeID
, void, str
, "appendCharacters:length:", chars
, appendedLength
);
4029 __CFAssertIsStringAndMutable(str
);
4031 strLength
= __CFStrLength(str
);
4032 if (__CFStringGetCompatibility(Bug2967272
) || __CFStrIsUnicode(str
)) {
4033 __CFStringChangeSize(str
, CFRangeMake(strLength
, 0), appendedLength
, true);
4034 memmove((UniChar
*)__CFStrContents(str
) + strLength
, chars
, appendedLength
* sizeof(UniChar
));
4037 bool isASCII
= true;
4038 for (idx
= 0; isASCII
&& idx
< appendedLength
; idx
++) isASCII
= (chars
[idx
] < 0x80);
4039 __CFStringChangeSize(str
, CFRangeMake(strLength
, 0), appendedLength
, !isASCII
);
4041 memmove((UniChar
*)__CFStrContents(str
) + strLength
, chars
, appendedLength
* sizeof(UniChar
));
4043 contents
= (uint8_t *)__CFStrContents(str
) + strLength
+ __CFStrSkipAnyLengthByte(str
);
4044 for (idx
= 0; idx
< appendedLength
; idx
++) contents
[idx
] = (uint8_t)chars
[idx
];
4050 static void __CFStringAppendBytes(CFMutableStringRef str
, const char *cStr
, CFIndex appendedLength
, CFStringEncoding encoding
) {
4051 Boolean appendedIsUnicode
= false;
4052 Boolean freeCStrWhenDone
= false;
4053 Boolean demoteAppendedUnicode
= false;
4054 CFVarWidthCharBuffer vBuf
;
4056 __CFAssertIsNotNegative(appendedLength
);
4058 if (encoding
== kCFStringEncodingASCII
|| encoding
== __CFStringGetEightBitStringEncoding()) {
4059 // appendedLength now denotes length in UniChars
4060 } else if (encoding
== kCFStringEncodingUnicode
) {
4061 UniChar
*chars
= (UniChar
*)cStr
;
4062 CFIndex idx
, length
= appendedLength
/ sizeof(UniChar
);
4063 bool isASCII
= true;
4064 for (idx
= 0; isASCII
&& idx
< length
; idx
++) isASCII
= (chars
[idx
] < 0x80);
4066 appendedIsUnicode
= true;
4068 demoteAppendedUnicode
= true;
4070 appendedLength
= length
;
4072 Boolean usingPassedInMemory
= false;
4074 vBuf
.allocator
= __CFGetDefaultAllocator(); // We don't want to use client's allocator for temp stuff
4075 vBuf
.chars
.unicode
= NULL
; // This will cause the decode function to allocate memory if necessary
4077 if (!__CFStringDecodeByteStream3((const uint8_t *)cStr
, appendedLength
, encoding
, __CFStrIsUnicode(str
), &vBuf
, &usingPassedInMemory
, 0)) {
4078 CFAssert1(0, __kCFLogAssertion
, "Supplied bytes could not be converted specified encoding %d", encoding
);
4082 // If not ASCII, appendedLength now denotes length in UniChars
4083 appendedLength
= vBuf
.numChars
;
4084 appendedIsUnicode
= !vBuf
.isASCII
;
4085 cStr
= (const char *)vBuf
.chars
.ascii
;
4086 freeCStrWhenDone
= !usingPassedInMemory
&& vBuf
.shouldFreeChars
;
4089 if (CF_IS_OBJC(__kCFStringTypeID
, str
)) {
4090 if (!appendedIsUnicode
&& !demoteAppendedUnicode
) {
4091 CF_OBJC_FUNCDISPATCH2(__kCFStringTypeID
, void, str
, "_cfAppendCString:length:", cStr
, appendedLength
);
4093 CF_OBJC_FUNCDISPATCH2(__kCFStringTypeID
, void, str
, "appendCharacters:length:", cStr
, appendedLength
);
4097 __CFAssertIsStringAndMutable(str
);
4098 strLength
= __CFStrLength(str
);
4100 __CFStringChangeSize(str
, CFRangeMake(strLength
, 0), appendedLength
, appendedIsUnicode
|| __CFStrIsUnicode(str
));
4102 if (__CFStrIsUnicode(str
)) {
4103 UniChar
*contents
= (UniChar
*)__CFStrContents(str
);
4104 if (appendedIsUnicode
) {
4105 memmove(contents
+ strLength
, cStr
, appendedLength
* sizeof(UniChar
));
4107 __CFStrConvertBytesToUnicode((const uint8_t *)cStr
, contents
+ strLength
, appendedLength
);
4110 if (demoteAppendedUnicode
) {
4111 UniChar
*chars
= (UniChar
*)cStr
;
4113 uint8_t *contents
= (uint8_t *)__CFStrContents(str
) + strLength
+ __CFStrSkipAnyLengthByte(str
);
4114 for (idx
= 0; idx
< appendedLength
; idx
++) contents
[idx
] = (uint8_t)chars
[idx
];
4116 uint8_t *contents
= (uint8_t *)__CFStrContents(str
);
4117 memmove(contents
+ strLength
+ __CFStrSkipAnyLengthByte(str
), cStr
, appendedLength
);
4122 if (freeCStrWhenDone
) CFAllocatorDeallocate(__CFGetDefaultAllocator(), (void *)cStr
);
4125 void CFStringAppendPascalString(CFMutableStringRef str
, ConstStringPtr pStr
, CFStringEncoding encoding
) {
4126 __CFStringAppendBytes(str
, (const char *)(pStr
+ 1), (CFIndex
)*pStr
, encoding
);
4129 void CFStringAppendCString(CFMutableStringRef str
, const char *cStr
, CFStringEncoding encoding
) {
4130 __CFStringAppendBytes(str
, cStr
, strlen(cStr
), encoding
);
4134 void CFStringAppendFormat(CFMutableStringRef str
, CFDictionaryRef formatOptions
, CFStringRef format
, ...) {
4137 va_start(argList
, format
);
4138 CFStringAppendFormatAndArguments(str
, formatOptions
, format
, argList
);
4143 CFIndex
CFStringFindAndReplace(CFMutableStringRef string
, CFStringRef stringToFind
, CFStringRef replacementString
, CFRange rangeToSearch
, CFStringCompareFlags compareOptions
) {
4145 Boolean backwards
= ((compareOptions
& kCFCompareBackwards
) != 0);
4146 UInt32 endIndex
= rangeToSearch
.location
+ rangeToSearch
.length
;
4147 #define MAX_RANGES_ON_STACK (1000 / sizeof(CFRange))
4148 CFRange rangeBuffer
[MAX_RANGES_ON_STACK
]; // Used to avoid allocating memory
4149 CFRange
*ranges
= rangeBuffer
;
4150 CFIndex foundCount
= 0;
4151 CFIndex capacity
= MAX_RANGES_ON_STACK
;
4153 __CFAssertIsStringAndMutable(string
);
4154 __CFAssertRangeIsInStringBounds(string
, rangeToSearch
.location
, rangeToSearch
.length
);
4156 // Note: This code is very similar to the one in CFStringCreateArrayWithFindResults().
4157 while ((rangeToSearch
.length
> 0) && CFStringFindWithOptions(string
, stringToFind
, rangeToSearch
, compareOptions
, &foundRange
)) {
4158 // Determine the next range
4160 rangeToSearch
.length
= foundRange
.location
- rangeToSearch
.location
;
4162 rangeToSearch
.location
= foundRange
.location
+ foundRange
.length
;
4163 rangeToSearch
.length
= endIndex
- rangeToSearch
.location
;
4166 // If necessary, grow the array
4167 if (foundCount
>= capacity
) {
4168 bool firstAlloc
= (ranges
== rangeBuffer
) ? true : false;
4169 capacity
= (capacity
+ 4) * 2;
4170 // Note that reallocate with NULL previous pointer is same as allocate
4171 ranges
= (CFRange
*)CFAllocatorReallocate(kCFAllocatorSystemDefault
, firstAlloc
? NULL
: ranges
, capacity
* sizeof(CFRange
), 0);
4172 if (firstAlloc
) memmove(ranges
, rangeBuffer
, MAX_RANGES_ON_STACK
* sizeof(CFRange
));
4174 ranges
[foundCount
] = foundRange
;
4178 if (foundCount
> 0) {
4179 if (backwards
) { // Reorder the ranges to be incrementing (better to do this here, then to check other places)
4181 int tail
= foundCount
- 1;
4182 while (head
< tail
) {
4183 CFRange temp
= ranges
[head
];
4184 ranges
[head
] = ranges
[tail
];
4185 ranges
[tail
] = temp
;
4190 __CFStringReplaceMultiple(string
, ranges
, foundCount
, replacementString
);
4191 if (ranges
!= rangeBuffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, ranges
);
4198 // This function is here for NSString purposes
4199 // It allows checking for mutability before mutating; this allows NSString to catch invalid mutations
4201 int __CFStringCheckAndReplace(CFMutableStringRef str
, CFRange range
, CFStringRef replacement
) {
4202 if (!__CFStrIsMutable(str
)) return _CFStringErrNotMutable
; // These three ifs are always here, for NSString usage
4203 if (!replacement
&& __CFStringNoteErrors()) return _CFStringErrNilArg
;
4204 // We use unsigneds as that is what NSRanges do; we use uint64_t do make sure the sum doesn't wrap (otherwise we'd need to do 3 separate checks). This allows catching bad ranges as described in 3375535. (-1,1)
4205 if (((uint64_t)((unsigned)range
.location
)) + ((uint64_t)((unsigned)range
.length
)) > (uint64_t)__CFStrLength(str
) && __CFStringNoteErrors()) return _CFStringErrBounds
;
4206 __CFAssertIsStringAndMutable(str
);
4207 __CFAssertRangeIsInStringBounds(str
, range
.location
, range
.length
);
4208 __CFStringReplace(str
, range
, replacement
);
4209 return _CFStringErrNone
;
4212 // This function determines whether errors which would cause string exceptions should
4213 // be ignored or not
4215 Boolean
__CFStringNoteErrors(void) {
4221 void CFStringPad(CFMutableStringRef string
, CFStringRef padString
, CFIndex length
, CFIndex indexIntoPad
) {
4222 CFIndex originalLength
;
4224 __CFAssertIsNotNegative(length
);
4225 __CFAssertIsNotNegative(indexIntoPad
);
4227 CF_OBJC_FUNCDISPATCH3(__kCFStringTypeID
, void, string
, "_cfPad:length:padIndex:", padString
, length
, indexIntoPad
);
4229 __CFAssertIsStringAndMutable(string
);
4231 originalLength
= __CFStrLength(string
);
4232 if (length
< originalLength
) {
4233 __CFStringChangeSize(string
, CFRangeMake(length
, originalLength
- length
), 0, false);
4234 } else if (originalLength
< length
) {
4238 CFIndex padStringLength
;
4240 CFIndex padRemaining
= length
- originalLength
;
4242 if (CF_IS_OBJC(__kCFStringTypeID
, padString
)) { /* ??? Hope the compiler optimizes this away if OBJC_MAPPINGS is not on */
4243 padStringLength
= CFStringGetLength(padString
);
4244 isUnicode
= true; /* !!! Bad for now */
4246 __CFAssertIsString(padString
);
4247 padStringLength
= __CFStrLength(padString
);
4248 isUnicode
= __CFStrIsUnicode(string
) || __CFStrIsUnicode(padString
);
4251 charSize
= isUnicode
? sizeof(UniChar
) : sizeof(uint8_t);
4253 __CFStringChangeSize(string
, CFRangeMake(originalLength
, 0), padRemaining
, isUnicode
);
4255 contents
= (uint8_t *)__CFStrContents(string
) + charSize
* originalLength
+ __CFStrSkipAnyLengthByte(string
);
4256 padLength
= padStringLength
- indexIntoPad
;
4257 padLength
= padRemaining
< padLength
? padRemaining
: padLength
;
4259 while (padRemaining
> 0) {
4261 CFStringGetCharacters(padString
, CFRangeMake(indexIntoPad
, padLength
), (UniChar
*)contents
);
4263 CFStringGetBytes(padString
, CFRangeMake(indexIntoPad
, padLength
), __CFStringGetEightBitStringEncoding(), 0, false, contents
, padRemaining
* charSize
, NULL
);
4265 contents
+= padLength
* charSize
;
4266 padRemaining
-= padLength
;
4268 padLength
= padRemaining
< padLength
? padRemaining
: padStringLength
;
4273 void CFStringTrim(CFMutableStringRef string
, CFStringRef trimString
) {
4275 CFIndex newStartIndex
;
4278 CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID
, void, string
, "_cfTrim:", trimString
);
4280 __CFAssertIsStringAndMutable(string
);
4281 __CFAssertIsString(trimString
);
4284 length
= __CFStrLength(string
);
4286 while (CFStringFindWithOptions(string
, trimString
, CFRangeMake(newStartIndex
, length
- newStartIndex
), kCFCompareAnchored
, &range
)) {
4287 newStartIndex
= range
.location
+ range
.length
;
4290 if (newStartIndex
< length
) {
4291 CFIndex charSize
= __CFStrIsUnicode(string
) ? sizeof(UniChar
) : sizeof(uint8_t);
4292 uint8_t *contents
= (uint8_t *)__CFStrContents(string
) + __CFStrSkipAnyLengthByte(string
);
4294 length
-= newStartIndex
;
4295 if (__CFStrLength(trimString
) < length
) {
4296 while (CFStringFindWithOptions(string
, trimString
, CFRangeMake(newStartIndex
, length
), kCFCompareAnchored
|kCFCompareBackwards
, &range
)) {
4297 length
= range
.location
- newStartIndex
;
4300 memmove(contents
, contents
+ newStartIndex
* charSize
, length
* charSize
);
4301 __CFStringChangeSize(string
, CFRangeMake(length
, __CFStrLength(string
) - length
), 0, false);
4302 } else { // Only trimString in string, trim all
4303 __CFStringChangeSize(string
, CFRangeMake(0, length
), 0, false);
4307 void CFStringTrimWhitespace(CFMutableStringRef string
) {
4308 CFIndex newStartIndex
;
4310 CFStringInlineBuffer buffer
;
4312 CF_OBJC_FUNCDISPATCH0(__kCFStringTypeID
, void, string
, "_cfTrimWS");
4314 __CFAssertIsStringAndMutable(string
);
4317 length
= __CFStrLength(string
);
4319 CFStringInitInlineBuffer(string
, &buffer
, CFRangeMake(0, length
));
4320 CFIndex buffer_idx
= 0;
4322 while (buffer_idx
< length
&& CFUniCharIsMemberOf(__CFStringGetCharacterFromInlineBufferQuick(&buffer
, buffer_idx
), kCFUniCharWhitespaceAndNewlineCharacterSet
))
4324 newStartIndex
= buffer_idx
;
4326 if (newStartIndex
< length
) {
4327 uint8_t *contents
= (uint8_t *)__CFStrContents(string
) + __CFStrSkipAnyLengthByte(string
);
4328 CFIndex charSize
= (__CFStrIsUnicode(string
) ? sizeof(UniChar
) : sizeof(uint8_t));
4330 buffer_idx
= length
- 1;
4331 while (0 <= buffer_idx
&& CFUniCharIsMemberOf(__CFStringGetCharacterFromInlineBufferQuick(&buffer
, buffer_idx
), kCFUniCharWhitespaceAndNewlineCharacterSet
))
4333 length
= buffer_idx
- newStartIndex
+ 1;
4335 memmove(contents
, contents
+ newStartIndex
* charSize
, length
* charSize
);
4336 __CFStringChangeSize(string
, CFRangeMake(length
, __CFStrLength(string
) - length
), 0, false);
4337 } else { // Whitespace only string
4338 __CFStringChangeSize(string
, CFRangeMake(0, length
), 0, false);
4342 void CFStringLowercase(CFMutableStringRef string
, CFLocaleRef locale
) {
4343 CFIndex currentIndex
= 0;
4345 const uint8_t *langCode
;
4346 Boolean isEightBit
= __CFStrIsEightBit(string
);
4348 CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID
, void, string
, "_cfLowercase:", locale
);
4350 __CFAssertIsStringAndMutable(string
);
4352 length
= __CFStrLength(string
);
4354 langCode
= (const uint8_t *)(_CFCanUseLocale(locale
) ? _CFStrGetLanguageIdentifierForLocale(locale
) : NULL
);
4356 if (!langCode
&& isEightBit
) {
4357 uint8_t *contents
= (uint8_t *)__CFStrContents(string
) + __CFStrSkipAnyLengthByte(string
);
4358 for (;currentIndex
< length
;currentIndex
++) {
4359 if (contents
[currentIndex
] >= 'A' && contents
[currentIndex
] <= 'Z') {
4360 contents
[currentIndex
] += 'a' - 'A';
4361 } else if (contents
[currentIndex
] > 127) {
4367 if (currentIndex
< length
) {
4368 UTF16Char
*contents
;
4369 UniChar mappedCharacters
[MAX_CASE_MAPPING_BUF
];
4370 CFIndex mappedLength
;
4371 UTF32Char currentChar
;
4374 if (isEightBit
) __CFStringChangeSize(string
, CFRangeMake(0, 0), 0, true);
4376 contents
= (UniChar
*)__CFStrContents(string
);
4378 for (;currentIndex
< length
;currentIndex
++) {
4380 if (CFUniCharIsSurrogateHighCharacter(contents
[currentIndex
]) && (currentIndex
+ 1 < length
) && CFUniCharIsSurrogateLowCharacter(contents
[currentIndex
+ 1])) {
4381 currentChar
= CFUniCharGetLongCharacterForSurrogatePair(contents
[currentIndex
], contents
[currentIndex
+ 1]);
4383 currentChar
= contents
[currentIndex
];
4385 flags
= ((langCode
|| (currentChar
== 0x03A3)) ? CFUniCharGetConditionalCaseMappingFlags(currentChar
, contents
, currentIndex
, length
, kCFUniCharToLowercase
, langCode
, flags
) : 0);
4387 mappedLength
= CFUniCharMapCaseTo(currentChar
, mappedCharacters
, MAX_CASE_MAPPING_BUF
, kCFUniCharToLowercase
, flags
, langCode
);
4388 if (mappedLength
> 0) contents
[currentIndex
] = *mappedCharacters
;
4390 if (currentChar
> 0xFFFF) { // Non-BMP char
4391 switch (mappedLength
) {
4393 __CFStringChangeSize(string
, CFRangeMake(currentIndex
, 2), 0, true);
4394 contents
= (UniChar
*)__CFStrContents(string
);
4399 __CFStringChangeSize(string
, CFRangeMake(currentIndex
+ 1, 1), 0, true);
4400 contents
= (UniChar
*)__CFStrContents(string
);
4405 contents
[++currentIndex
] = mappedCharacters
[1];
4409 --mappedLength
; // Skip the current char
4410 __CFStringChangeSize(string
, CFRangeMake(currentIndex
+ 1, 0), mappedLength
- 1, true);
4411 contents
= (UniChar
*)__CFStrContents(string
);
4412 memmove(contents
+ currentIndex
+ 1, mappedCharacters
+ 1, mappedLength
* sizeof(UniChar
));
4413 length
+= (mappedLength
- 1);
4414 currentIndex
+= mappedLength
;
4417 } else if (mappedLength
== 0) {
4418 __CFStringChangeSize(string
, CFRangeMake(currentIndex
, 1), 0, true);
4419 contents
= (UniChar
*)__CFStrContents(string
);
4421 } else if (mappedLength
> 1) {
4422 --mappedLength
; // Skip the current char
4423 __CFStringChangeSize(string
, CFRangeMake(currentIndex
+ 1, 0), mappedLength
, true);
4424 contents
= (UniChar
*)__CFStrContents(string
);
4425 memmove(contents
+ currentIndex
+ 1, mappedCharacters
+ 1, mappedLength
* sizeof(UniChar
));
4426 length
+= mappedLength
;
4427 currentIndex
+= mappedLength
;
4433 void CFStringUppercase(CFMutableStringRef string
, CFLocaleRef locale
) {
4434 CFIndex currentIndex
= 0;
4436 const uint8_t *langCode
;
4437 Boolean isEightBit
= __CFStrIsEightBit(string
);
4439 CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID
, void, string
, "_cfUppercase:", locale
);
4441 __CFAssertIsStringAndMutable(string
);
4443 length
= __CFStrLength(string
);
4445 langCode
= (const uint8_t *)(_CFCanUseLocale(locale
) ? _CFStrGetLanguageIdentifierForLocale(locale
) : NULL
);
4447 if (!langCode
&& isEightBit
) {
4448 uint8_t *contents
= (uint8_t *)__CFStrContents(string
) + __CFStrSkipAnyLengthByte(string
);
4449 for (;currentIndex
< length
;currentIndex
++) {
4450 if (contents
[currentIndex
] >= 'a' && contents
[currentIndex
] <= 'z') {
4451 contents
[currentIndex
] -= 'a' - 'A';
4452 } else if (contents
[currentIndex
] > 127) {
4458 if (currentIndex
< length
) {
4460 UniChar mappedCharacters
[MAX_CASE_MAPPING_BUF
];
4461 CFIndex mappedLength
;
4462 UTF32Char currentChar
;
4465 if (isEightBit
) __CFStringChangeSize(string
, CFRangeMake(0, 0), 0, true);
4467 contents
= (UniChar
*)__CFStrContents(string
);
4469 for (;currentIndex
< length
;currentIndex
++) {
4470 if (CFUniCharIsSurrogateHighCharacter(contents
[currentIndex
]) && (currentIndex
+ 1 < length
) && CFUniCharIsSurrogateLowCharacter(contents
[currentIndex
+ 1])) {
4471 currentChar
= CFUniCharGetLongCharacterForSurrogatePair(contents
[currentIndex
], contents
[currentIndex
+ 1]);
4473 currentChar
= contents
[currentIndex
];
4476 flags
= (langCode
? CFUniCharGetConditionalCaseMappingFlags(currentChar
, contents
, currentIndex
, length
, kCFUniCharToUppercase
, langCode
, flags
) : 0);
4478 mappedLength
= CFUniCharMapCaseTo(currentChar
, mappedCharacters
, MAX_CASE_MAPPING_BUF
, kCFUniCharToUppercase
, flags
, langCode
);
4479 if (mappedLength
> 0) contents
[currentIndex
] = *mappedCharacters
;
4481 if (currentChar
> 0xFFFF) { // Non-BMP char
4482 switch (mappedLength
) {
4484 __CFStringChangeSize(string
, CFRangeMake(currentIndex
, 2), 0, true);
4485 contents
= (UniChar
*)__CFStrContents(string
);
4490 __CFStringChangeSize(string
, CFRangeMake(currentIndex
+ 1, 1), 0, true);
4491 contents
= (UniChar
*)__CFStrContents(string
);
4496 contents
[++currentIndex
] = mappedCharacters
[1];
4500 --mappedLength
; // Skip the current char
4501 __CFStringChangeSize(string
, CFRangeMake(currentIndex
+ 1, 0), mappedLength
- 1, true);
4502 contents
= (UniChar
*)__CFStrContents(string
);
4503 memmove(contents
+ currentIndex
+ 1, mappedCharacters
+ 1, mappedLength
* sizeof(UniChar
));
4504 length
+= (mappedLength
- 1);
4505 currentIndex
+= mappedLength
;
4508 } else if (mappedLength
== 0) {
4509 __CFStringChangeSize(string
, CFRangeMake(currentIndex
, 1), 0, true);
4510 contents
= (UniChar
*)__CFStrContents(string
);
4512 } else if (mappedLength
> 1) {
4513 --mappedLength
; // Skip the current char
4514 __CFStringChangeSize(string
, CFRangeMake(currentIndex
+ 1, 0), mappedLength
, true);
4515 contents
= (UniChar
*)__CFStrContents(string
);
4516 memmove(contents
+ currentIndex
+ 1, mappedCharacters
+ 1, mappedLength
* sizeof(UniChar
));
4517 length
+= mappedLength
;
4518 currentIndex
+= mappedLength
;
4525 void CFStringCapitalize(CFMutableStringRef string
, CFLocaleRef locale
) {
4526 CFIndex currentIndex
= 0;
4528 const uint8_t *langCode
;
4529 Boolean isEightBit
= __CFStrIsEightBit(string
);
4530 Boolean isLastCased
= false;
4531 const uint8_t *caseIgnorableForBMP
;
4533 CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID
, void, string
, "_cfCapitalize:", locale
);
4535 __CFAssertIsStringAndMutable(string
);
4537 length
= __CFStrLength(string
);
4539 caseIgnorableForBMP
= CFUniCharGetBitmapPtrForPlane(kCFUniCharCaseIgnorableCharacterSet
, 0);
4541 langCode
= (const uint8_t *)(_CFCanUseLocale(locale
) ? _CFStrGetLanguageIdentifierForLocale(locale
) : NULL
);
4543 if (!langCode
&& isEightBit
) {
4544 uint8_t *contents
= (uint8_t *)__CFStrContents(string
) + __CFStrSkipAnyLengthByte(string
);
4545 for (;currentIndex
< length
;currentIndex
++) {
4546 if (contents
[currentIndex
] > 127) {
4548 } else if (contents
[currentIndex
] >= 'A' && contents
[currentIndex
] <= 'Z') {
4549 contents
[currentIndex
] += (isLastCased
? 'a' - 'A' : 0);
4551 } else if (contents
[currentIndex
] >= 'a' && contents
[currentIndex
] <= 'z') {
4552 contents
[currentIndex
] -= (!isLastCased
? 'a' - 'A' : 0);
4554 } else if (!CFUniCharIsMemberOfBitmap(contents
[currentIndex
], caseIgnorableForBMP
)) {
4555 isLastCased
= false;
4560 if (currentIndex
< length
) {
4562 UniChar mappedCharacters
[MAX_CASE_MAPPING_BUF
];
4563 CFIndex mappedLength
;
4564 UTF32Char currentChar
;
4567 if (isEightBit
) __CFStringChangeSize(string
, CFRangeMake(0, 0), 0, true);
4569 contents
= (UniChar
*)__CFStrContents(string
);
4571 for (;currentIndex
< length
;currentIndex
++) {
4572 if (CFUniCharIsSurrogateHighCharacter(contents
[currentIndex
]) && (currentIndex
+ 1 < length
) && CFUniCharIsSurrogateLowCharacter(contents
[currentIndex
+ 1])) {
4573 currentChar
= CFUniCharGetLongCharacterForSurrogatePair(contents
[currentIndex
], contents
[currentIndex
+ 1]);
4575 currentChar
= contents
[currentIndex
];
4577 flags
= ((langCode
|| ((currentChar
== 0x03A3) && isLastCased
)) ? CFUniCharGetConditionalCaseMappingFlags(currentChar
, contents
, currentIndex
, length
, (isLastCased
? kCFUniCharToLowercase
: kCFUniCharToTitlecase
), langCode
, flags
) : 0);
4579 mappedLength
= CFUniCharMapCaseTo(currentChar
, mappedCharacters
, MAX_CASE_MAPPING_BUF
, (isLastCased
? kCFUniCharToLowercase
: kCFUniCharToTitlecase
), flags
, langCode
);
4580 if (mappedLength
> 0) contents
[currentIndex
] = *mappedCharacters
;
4582 if (currentChar
> 0xFFFF) { // Non-BMP char
4583 switch (mappedLength
) {
4585 __CFStringChangeSize(string
, CFRangeMake(currentIndex
, 2), 0, true);
4586 contents
= (UniChar
*)__CFStrContents(string
);
4591 __CFStringChangeSize(string
, CFRangeMake(currentIndex
+ 1, 1), 0, true);
4592 contents
= (UniChar
*)__CFStrContents(string
);
4597 contents
[++currentIndex
] = mappedCharacters
[1];
4601 --mappedLength
; // Skip the current char
4602 __CFStringChangeSize(string
, CFRangeMake(currentIndex
+ 1, 0), mappedLength
- 1, true);
4603 contents
= (UniChar
*)__CFStrContents(string
);
4604 memmove(contents
+ currentIndex
+ 1, mappedCharacters
+ 1, mappedLength
* sizeof(UniChar
));
4605 length
+= (mappedLength
- 1);
4606 currentIndex
+= mappedLength
;
4609 } else if (mappedLength
== 0) {
4610 __CFStringChangeSize(string
, CFRangeMake(currentIndex
, 1), 0, true);
4611 contents
= (UniChar
*)__CFStrContents(string
);
4613 } else if (mappedLength
> 1) {
4614 --mappedLength
; // Skip the current char
4615 __CFStringChangeSize(string
, CFRangeMake(currentIndex
+ 1, 0), mappedLength
, true);
4616 contents
= (UniChar
*)__CFStrContents(string
);
4617 memmove(contents
+ currentIndex
+ 1, mappedCharacters
+ 1, mappedLength
* sizeof(UniChar
));
4618 length
+= mappedLength
;
4619 currentIndex
+= mappedLength
;
4622 if (!((currentChar
> 0xFFFF) ? CFUniCharIsMemberOf(currentChar
, kCFUniCharCaseIgnorableCharacterSet
) : CFUniCharIsMemberOfBitmap(currentChar
, caseIgnorableForBMP
))) { // We have non-caseignorable here
4623 isLastCased
= ((CFUniCharIsMemberOf(currentChar
, kCFUniCharUppercaseLetterCharacterSet
) || CFUniCharIsMemberOf(currentChar
, kCFUniCharLowercaseLetterCharacterSet
)) ? true : false);
4630 #define MAX_DECOMP_BUF 64
4632 #define HANGUL_SBASE 0xAC00
4633 #define HANGUL_LBASE 0x1100
4634 #define HANGUL_VBASE 0x1161
4635 #define HANGUL_TBASE 0x11A7
4636 #define HANGUL_SCOUNT 11172
4637 #define HANGUL_LCOUNT 19
4638 #define HANGUL_VCOUNT 21
4639 #define HANGUL_TCOUNT 28
4640 #define HANGUL_NCOUNT (HANGUL_VCOUNT * HANGUL_TCOUNT)
4642 CF_INLINE
uint32_t __CFGetUTF16Length(const UTF32Char
*characters
, uint32_t utf32Length
) {
4643 const UTF32Char
*limit
= characters
+ utf32Length
;
4644 uint32_t length
= 0;
4646 while (characters
< limit
) length
+= (*(characters
++) > 0xFFFF ? 2 : 1);
4651 CF_INLINE
void __CFFillInUTF16(const UTF32Char
*characters
, UTF16Char
*dst
, uint32_t utf32Length
) {
4652 const UTF32Char
*limit
= characters
+ utf32Length
;
4653 UTF32Char currentChar
;
4655 while (characters
< limit
) {
4656 currentChar
= *(characters
++);
4657 if (currentChar
> 0xFFFF) {
4658 currentChar
-= 0x10000;
4659 *(dst
++) = (UTF16Char
)((currentChar
>> 10) + 0xD800UL
);
4660 *(dst
++) = (UTF16Char
)((currentChar
& 0x3FF) + 0xDC00UL
);
4662 *(dst
++) = currentChar
;
4667 void CFStringNormalize(CFMutableStringRef string
, CFStringNormalizationForm theForm
) {
4668 CFIndex currentIndex
= 0;
4670 bool needToReorder
= true;
4672 CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID
, void, string
, "_cfNormalize:", theForm
);
4674 __CFAssertIsStringAndMutable(string
);
4676 length
= __CFStrLength(string
);
4678 if (__CFStrIsEightBit(string
)) {
4681 if (theForm
== kCFStringNormalizationFormC
) return; // 8bit form has no decomposition
4683 contents
= (uint8_t *)__CFStrContents(string
) + __CFStrSkipAnyLengthByte(string
);
4685 for (;currentIndex
< length
;currentIndex
++) {
4686 if (contents
[currentIndex
] > 127) {
4687 __CFStringChangeSize(string
, CFRangeMake(0, 0), 0, true); // need to do harm way
4688 needToReorder
= false;
4694 if (currentIndex
< length
) {
4695 UTF16Char
*limit
= (UTF16Char
*)__CFStrContents(string
) + length
;
4696 UTF16Char
*contents
= (UTF16Char
*)__CFStrContents(string
) + currentIndex
;
4697 UTF32Char buffer
[MAX_DECOMP_BUF
];
4698 UTF32Char
*mappedCharacters
= buffer
;
4699 CFIndex allocatedLength
= MAX_DECOMP_BUF
;
4700 CFIndex mappedLength
;
4701 CFIndex currentLength
;
4702 UTF32Char currentChar
;
4703 const uint8_t *decompBMP
= CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet
, 0);
4704 const uint8_t *nonBaseBMP
= CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet
, 0);
4705 const uint8_t *combiningBMP
= (const uint8_t *)CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty
, 0);
4707 while (contents
< limit
) {
4708 if (CFUniCharIsSurrogateHighCharacter(*contents
) && (contents
+ 1 < limit
) && CFUniCharIsSurrogateLowCharacter(*(contents
+ 1))) {
4709 currentChar
= CFUniCharGetLongCharacterForSurrogatePair(*contents
, *(contents
+ 1));
4713 currentChar
= *(contents
++);
4719 if (CFUniCharIsMemberOfBitmap(currentChar
, ((currentChar
< 0x10000) ? decompBMP
: CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet
, (currentChar
>> 16)))) && (0 == CFUniCharGetCombiningPropertyForCharacter(currentChar
, ((currentChar
< 0x10000) ? combiningBMP
: (const uint8_t *)CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty
, (currentChar
>> 16)))))) {
4720 if ((theForm
& kCFStringNormalizationFormC
) == 0 || currentChar
< HANGUL_SBASE
|| currentChar
> (HANGUL_SBASE
+ HANGUL_SCOUNT
)) { // We don't have to decompose Hangul Syllables if we're precomposing again
4721 mappedLength
= CFUniCharDecomposeCharacter(currentChar
, mappedCharacters
, MAX_DECOMP_BUF
);
4725 if ((needToReorder
|| (theForm
& kCFStringNormalizationFormC
)) && ((contents
< limit
) || (mappedLength
== 0))) {
4726 if (mappedLength
> 0) {
4727 if (CFUniCharIsSurrogateHighCharacter(*contents
) && (contents
+ 1 < limit
) && CFUniCharIsSurrogateLowCharacter(*(contents
+ 1))) {
4728 currentChar
= CFUniCharGetLongCharacterForSurrogatePair(*contents
, *(contents
+ 1));
4730 currentChar
= *contents
;
4734 if (0 != CFUniCharGetCombiningPropertyForCharacter(currentChar
, (const uint8_t *)((currentChar
< 0x10000) ? combiningBMP
: CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty
, (currentChar
>> 16))))) {
4735 uint32_t decompLength
;
4737 if (mappedLength
== 0) {
4738 contents
-= (currentChar
& 0xFFFF0000 ? 2 : 1);
4739 if (currentIndex
> 0) {
4740 if (CFUniCharIsSurrogateLowCharacter(*(contents
- 1)) && (currentIndex
> 1) && CFUniCharIsSurrogateHighCharacter(*(contents
- 2))) {
4741 *mappedCharacters
= CFUniCharGetLongCharacterForSurrogatePair(*(contents
- 2), *(contents
- 1));
4745 *mappedCharacters
= *(contents
- 1);
4752 currentLength
+= (currentChar
& 0xFFFF0000 ? 2 : 1);
4754 contents
+= (currentChar
& 0xFFFF0000 ? 2 : 1);
4756 if (CFUniCharIsMemberOfBitmap(currentChar
, ((currentChar
< 0x10000) ? decompBMP
: CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet
, (currentChar
>> 16))))) { // Vietnamese accent, etc.
4757 decompLength
= CFUniCharDecomposeCharacter(currentChar
, mappedCharacters
+ mappedLength
, MAX_DECOMP_BUF
- mappedLength
);
4758 mappedLength
+= decompLength
;
4760 mappedCharacters
[mappedLength
++] = currentChar
;
4763 while (contents
< limit
) {
4764 if (CFUniCharIsSurrogateHighCharacter(*contents
) && (contents
+ 1 < limit
) && CFUniCharIsSurrogateLowCharacter(*(contents
+ 1))) {
4765 currentChar
= CFUniCharGetLongCharacterForSurrogatePair(*contents
, *(contents
+ 1));
4767 currentChar
= *contents
;
4769 if (0 == CFUniCharGetCombiningPropertyForCharacter(currentChar
, (const uint8_t *)((currentChar
< 0x10000) ? combiningBMP
: CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty
, (currentChar
>> 16))))) break;
4770 if (currentChar
& 0xFFFF0000) {
4777 if (mappedLength
== allocatedLength
) {
4778 allocatedLength
+= MAX_DECOMP_BUF
;
4779 if (mappedCharacters
== buffer
) {
4780 mappedCharacters
= (UTF32Char
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, allocatedLength
* sizeof(UTF32Char
), 0);
4781 memmove(mappedCharacters
, buffer
, MAX_DECOMP_BUF
* sizeof(UTF32Char
));
4783 mappedCharacters
= (UTF32Char
*)CFAllocatorReallocate(kCFAllocatorSystemDefault
, mappedCharacters
, allocatedLength
* sizeof(UTF32Char
), 0);
4786 if (CFUniCharIsMemberOfBitmap(currentChar
, ((currentChar
< 0x10000) ? decompBMP
: CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet
, (currentChar
>> 16))))) { // Vietnamese accent, etc.
4787 decompLength
= CFUniCharDecomposeCharacter(currentChar
, mappedCharacters
+ mappedLength
, MAX_DECOMP_BUF
- mappedLength
);
4788 mappedLength
+= decompLength
;
4790 mappedCharacters
[mappedLength
++] = currentChar
;
4794 if (needToReorder
&& mappedLength
> 1) CFUniCharPrioritySort(mappedCharacters
, mappedLength
);
4797 if (theForm
& kCFStringNormalizationFormKD
) {
4798 CFIndex newLength
= 0;
4800 if (mappedLength
== 0 && CFUniCharIsMemberOf(currentChar
, kCFUniCharCompatibilityDecomposableCharacterSet
)) {
4801 mappedCharacters
[mappedLength
++] = currentChar
;
4803 while (newLength
< mappedLength
) {
4804 newLength
= CFUniCharCompatibilityDecompose(mappedCharacters
, mappedLength
, allocatedLength
);
4805 if (newLength
== 0) {
4806 allocatedLength
+= MAX_DECOMP_BUF
;
4807 if (mappedCharacters
== buffer
) {
4808 mappedCharacters
= (UTF32Char
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, allocatedLength
* sizeof(UTF32Char
), 0);
4809 memmove(mappedCharacters
, buffer
, MAX_DECOMP_BUF
* sizeof(UTF32Char
));
4811 mappedCharacters
= (UTF32Char
*)CFAllocatorReallocate(kCFAllocatorSystemDefault
, mappedCharacters
, allocatedLength
* sizeof(UTF32Char
), 0);
4815 mappedLength
= newLength
;
4818 if (theForm
& kCFStringNormalizationFormC
) {
4821 if (mappedLength
> 1) {
4822 CFIndex consumedLength
= 1;
4823 UTF32Char
*currentBase
= mappedCharacters
;
4824 uint8_t currentClass
, lastClass
= 0;
4825 bool didCombine
= false;
4827 currentChar
= *mappedCharacters
;
4829 while (consumedLength
< mappedLength
) {
4830 nextChar
= mappedCharacters
[consumedLength
];
4831 currentClass
= CFUniCharGetCombiningPropertyForCharacter(nextChar
, (const uint8_t *)((nextChar
< 0x10000) ? combiningBMP
: CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty
, (nextChar
>> 16))));
4833 if (theForm
& kCFStringNormalizationFormKD
) {
4834 if ((currentChar
>= HANGUL_LBASE
) && (currentChar
< (HANGUL_LBASE
+ 0xFF))) {
4835 SInt8 lIndex
= currentChar
- HANGUL_LBASE
;
4837 if ((0 <= lIndex
) && (lIndex
<= HANGUL_LCOUNT
)) {
4838 SInt16 vIndex
= nextChar
- HANGUL_VBASE
;
4840 if ((vIndex
>= 0) && (vIndex
<= HANGUL_VCOUNT
)) {
4842 CFIndex usedLength
= mappedLength
;
4844 mappedCharacters
[consumedLength
++] = 0xFFFD;
4846 if (consumedLength
< mappedLength
) {
4847 tIndex
= mappedCharacters
[consumedLength
] - HANGUL_TBASE
;
4848 if ((tIndex
< 0) || (tIndex
> HANGUL_TCOUNT
)) {
4851 mappedCharacters
[consumedLength
++] = 0xFFFD;
4854 *currentBase
= (lIndex
* HANGUL_VCOUNT
+ vIndex
) * HANGUL_TCOUNT
+ tIndex
+ HANGUL_SBASE
;
4856 while (--usedLength
> 0) {
4857 if (mappedCharacters
[usedLength
] == 0xFFFD) {
4860 memmove(mappedCharacters
+ usedLength
, mappedCharacters
+ usedLength
+ 1, (mappedLength
- usedLength
) * sizeof(UTF32Char
));
4863 currentBase
= mappedCharacters
+ consumedLength
;
4864 currentChar
= *currentBase
;
4871 if (!CFUniCharIsMemberOfBitmap(nextChar
, ((nextChar
< 0x10000) ? nonBaseBMP
: CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet
, (nextChar
>> 16))))) {
4872 *currentBase
= currentChar
;
4873 currentBase
= mappedCharacters
+ consumedLength
;
4874 currentChar
= nextChar
;
4880 if ((lastClass
== 0) || (currentClass
> lastClass
)) {
4881 nextChar
= CFUniCharPrecomposeCharacter(currentChar
, nextChar
);
4882 if (nextChar
== 0xFFFD) {
4883 lastClass
= currentClass
;
4885 mappedCharacters
[consumedLength
] = 0xFFFD;
4887 currentChar
= nextChar
;
4893 *currentBase
= currentChar
;
4895 consumedLength
= mappedLength
;
4896 while (--consumedLength
> 0) {
4897 if (mappedCharacters
[consumedLength
] == 0xFFFD) {
4899 memmove(mappedCharacters
+ consumedLength
, mappedCharacters
+ consumedLength
+ 1, (mappedLength
- consumedLength
) * sizeof(UTF32Char
));
4903 } else if ((currentChar
>= HANGUL_LBASE
) && (currentChar
< (HANGUL_LBASE
+ 0xFF))) { // Hangul Jamo
4904 SInt8 lIndex
= currentChar
- HANGUL_LBASE
;
4906 if ((contents
< limit
) && (0 <= lIndex
) && (lIndex
<= HANGUL_LCOUNT
)) {
4907 SInt16 vIndex
= *contents
- HANGUL_VBASE
;
4909 if ((vIndex
>= 0) && (vIndex
<= HANGUL_VCOUNT
)) {
4912 ++contents
; ++currentLength
;
4914 if (contents
< limit
) {
4915 tIndex
= *contents
- HANGUL_TBASE
;
4916 if ((tIndex
< 0) || (tIndex
> HANGUL_TCOUNT
)) {
4919 ++contents
; ++currentLength
;
4922 *mappedCharacters
= (lIndex
* HANGUL_VCOUNT
+ vIndex
) * HANGUL_TCOUNT
+ tIndex
+ HANGUL_SBASE
;
4926 } else { // collect class 0 non-base characters
4927 while (contents
< limit
) {
4928 nextChar
= *contents
;
4929 if (CFUniCharIsSurrogateHighCharacter(nextChar
) && ((contents
+ 1) < limit
) && CFUniCharIsSurrogateLowCharacter(*(contents
+ 1))) {
4930 nextChar
= CFUniCharGetLongCharacterForSurrogatePair(nextChar
, *(contents
+ 1));
4931 if (!CFUniCharIsMemberOfBitmap(nextChar
, (const uint8_t *)CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet
, (nextChar
>> 16))) || (0 != CFUniCharGetCombiningPropertyForCharacter(nextChar
, (const uint8_t *)CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty
, (nextChar
>> 16))))) break;
4933 if (!CFUniCharIsMemberOfBitmap(nextChar
, nonBaseBMP
) || (0 != CFUniCharGetCombiningPropertyForCharacter(nextChar
, combiningBMP
))) break;
4935 currentChar
= CFUniCharPrecomposeCharacter(currentChar
, nextChar
);
4936 if (0xFFFD == currentChar
) break;
4938 if (nextChar
< 0x10000) {
4939 ++contents
; ++currentLength
;
4945 *mappedCharacters
= currentChar
;
4951 if (mappedLength
> 0) {
4952 CFIndex utf16Length
= __CFGetUTF16Length(mappedCharacters
, mappedLength
);
4954 if (utf16Length
!= currentLength
) {
4955 __CFStringChangeSize(string
, CFRangeMake(currentIndex
, currentLength
), utf16Length
, true);
4956 currentLength
= utf16Length
;
4958 contents
= (UTF16Char
*)__CFStrContents(string
);
4959 limit
= contents
+ __CFStrLength(string
);
4960 contents
+= currentIndex
;
4961 __CFFillInUTF16(mappedCharacters
, contents
, mappedLength
);
4962 contents
+= utf16Length
;
4964 currentIndex
+= currentLength
;
4967 if (mappedCharacters
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, mappedCharacters
);
4971 void CFStringFold(CFMutableStringRef theString
, CFStringCompareFlags theFlags
, CFLocaleRef locale
) {
4972 CFStringInlineBuffer stringBuffer
;
4973 CFIndex length
= CFStringGetLength(theString
);
4974 CFIndex currentIndex
= 0;
4975 CFIndex bufferLength
= 0;
4976 UTF32Char buffer
[kCFStringStackBufferLength
];
4977 const uint8_t *cString
;
4978 const uint8_t *langCode
;
4979 CFStringEncoding eightBitEncoding
;
4980 bool caseInsensitive
= ((theFlags
& kCFCompareCaseInsensitive
) ? true : false);
4981 bool isObjc
= CF_IS_OBJC(__kCFStringTypeID
, theString
);
4982 CFLocaleRef theLocale
= locale
;
4984 if ((theFlags
& kCFCompareLocalized
) && (NULL
== locale
)) {
4985 theLocale
= CFLocaleCopyCurrent();
4988 theFlags
&= (kCFCompareCaseInsensitive
|kCFCompareDiacriticInsensitive
|kCFCompareWidthInsensitive
);
4990 if ((0 == theFlags
) || (0 == length
)) goto bail
; // nothing to do
4992 langCode
= ((NULL
== theLocale
) ? NULL
: (const uint8_t *)_CFStrGetLanguageIdentifierForLocale(theLocale
));
4994 eightBitEncoding
= __CFStringGetEightBitStringEncoding();
4995 cString
= (const uint8_t *)CFStringGetCStringPtr(theString
, eightBitEncoding
);
4997 if ((NULL
!= cString
) && !caseInsensitive
&& (kCFStringEncodingASCII
== eightBitEncoding
)) goto bail
; // All ASCII
4999 CFStringInitInlineBuffer(theString
, &stringBuffer
, CFRangeMake(0, length
));
5001 if ((NULL
!= cString
) && (theFlags
& (kCFCompareCaseInsensitive
|kCFCompareDiacriticInsensitive
))) {
5002 const uint8_t *cStringPtr
= cString
;
5003 const uint8_t *cStringLimit
= cString
+ length
;
5004 uint8_t *cStringContents
= (isObjc
? NULL
: (uint8_t *)__CFStrContents(theString
) + __CFStrSkipAnyLengthByte(theString
));
5006 while (cStringPtr
< cStringLimit
) {
5007 if ((*cStringPtr
< 0x80) && (NULL
== langCode
)) {
5008 if (caseInsensitive
&& (*cStringPtr
>= 'A') && (*cStringPtr
<= 'Z')) {
5009 if (NULL
== cStringContents
) {
5012 cStringContents
[cStringPtr
- cString
] += ('a' - 'A');
5016 if ((bufferLength
= __CFStringFoldCharacterClusterAtIndex((UTF32Char
)__CFCharToUniCharTable
[*cStringPtr
], &stringBuffer
, cStringPtr
- cString
, theFlags
, langCode
, buffer
, kCFStringStackBufferLength
, NULL
)) > 0) {
5017 if ((*buffer
> 0x7F) || (bufferLength
> 1) || (NULL
== cStringContents
)) break;
5018 cStringContents
[cStringPtr
- cString
] = *buffer
;
5024 currentIndex
= cStringPtr
- cString
;
5027 if (currentIndex
< length
) {
5028 UTF16Char
*contents
;
5031 CFMutableStringRef cfString
;
5032 CFRange range
= CFRangeMake(currentIndex
, length
- currentIndex
);
5034 contents
= (UTF16Char
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, sizeof(UTF16Char
) * range
.length
, 0);
5036 CFStringGetCharacters(theString
, range
, contents
);
5038 cfString
= CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorSystemDefault
, contents
, range
.length
, range
.length
, NULL
);
5040 CFStringFold(cfString
, theFlags
, theLocale
);
5042 CFStringReplace(theString
, range
, cfString
);
5044 CFRelease(cfString
);
5046 const UTF32Char
*characters
;
5047 const UTF32Char
*charactersLimit
;
5048 UTF32Char character
;
5049 CFIndex consumedLength
;
5053 if (bufferLength
> 0) {
5054 __CFStringChangeSize(theString
, CFRangeMake(currentIndex
+ 1, 0), bufferLength
- 1, true);
5055 length
= __CFStrLength(theString
);
5056 CFStringInitInlineBuffer(theString
, &stringBuffer
, CFRangeMake(0, length
));
5058 contents
= (UTF16Char
*)__CFStrContents(theString
) + currentIndex
;
5059 characters
= buffer
;
5060 charactersLimit
= characters
+ bufferLength
;
5061 while (characters
< charactersLimit
) *(contents
++) = (UTF16Char
)*(characters
++);
5065 while (currentIndex
< length
) {
5066 character
= __CFStringGetCharacterFromInlineBufferQuick(&stringBuffer
, currentIndex
);
5070 if ((NULL
== langCode
) && (character
< 0x80) && (0 == (theFlags
& kCFCompareDiacriticInsensitive
))) {
5071 if (caseInsensitive
&& (character
>= 'A') && (character
<= 'Z')) {
5074 *buffer
= character
+ ('a' - 'A');
5077 if (CFUniCharIsSurrogateHighCharacter(character
) && ((currentIndex
+ 1) < length
)) {
5078 UTF16Char lowSurrogate
= __CFStringGetCharacterFromInlineBufferQuick(&stringBuffer
, currentIndex
+ 1);
5079 if (CFUniCharIsSurrogateLowCharacter(lowSurrogate
)) character
= CFUniCharGetLongCharacterForSurrogatePair(character
, lowSurrogate
);
5082 bufferLength
= __CFStringFoldCharacterClusterAtIndex(character
, &stringBuffer
, currentIndex
, theFlags
, langCode
, buffer
, kCFStringStackBufferLength
, &consumedLength
);
5085 if (consumedLength
> 0) {
5086 CFIndex utf16Length
= bufferLength
;
5088 characters
= buffer
;
5089 charactersLimit
= characters
+ bufferLength
;
5091 while (characters
< charactersLimit
) if (*(characters
++) > 0xFFFF) ++utf16Length
; // Extend bufferLength to the UTF-16 length
5093 if ((utf16Length
!= consumedLength
) || __CFStrIsEightBit(theString
)) {
5095 CFIndex insertLength
;
5097 if (consumedLength
< utf16Length
) { // Need to expand
5098 range
= CFRangeMake(currentIndex
+ consumedLength
, 0);
5099 insertLength
= utf16Length
- consumedLength
;
5101 range
= CFRangeMake(currentIndex
+ utf16Length
, consumedLength
- utf16Length
);
5104 __CFStringChangeSize(theString
, range
, insertLength
, true);
5105 length
= __CFStrLength(theString
);
5106 CFStringInitInlineBuffer(theString
, &stringBuffer
, CFRangeMake(0, length
));
5109 (void)CFUniCharFromUTF32(buffer
, bufferLength
, (UTF16Char
*)__CFStrContents(theString
) + currentIndex
, true, __CF_BIG_ENDIAN__
);
5111 currentIndex
+= utf16Length
;
5120 if (NULL
== locale
&& theLocale
) {
5121 CFRelease(theLocale
);
5126 kCFStringFormatZeroFlag
= (1 << 0), // if not, padding is space char
5127 kCFStringFormatMinusFlag
= (1 << 1), // if not, no flag implied
5128 kCFStringFormatPlusFlag
= (1 << 2), // if not, no flag implied, overrides space
5129 kCFStringFormatSpaceFlag
= (1 << 3) // if not, no flag implied
5152 #if LONG_DOUBLE_SUPPORT
5153 long double longDoubleValue
;
5160 CFFormatDefaultSize
= 0,
5167 CFFormatSizeLong
= CFFormatSize8
,
5168 CFFormatSizePointer
= CFFormatSize8
5170 CFFormatSizeLong
= CFFormatSize4
,
5171 CFFormatSizePointer
= CFFormatSize4
5178 CFFormatLiteralType
= 32,
5179 CFFormatLongType
= 33,
5180 CFFormatDoubleType
= 34,
5181 CFFormatPointerType
= 35,
5182 CFFormatObjectType
= 36, /* handled specially */ /* ??? not used anymore, can be removed? */
5183 CFFormatCFType
= 37, /* handled specially */
5184 CFFormatUnicharsType
= 38, /* handled specially */
5185 CFFormatCharsType
= 39, /* handled specially */
5186 CFFormatPascalCharsType
= 40, /* handled specially */
5187 CFFormatSingleUnicharType
= 41, /* handled specially */
5188 CFFormatDummyPointerType
= 42 /* special case for %n */
5191 CF_INLINE
void __CFParseFormatSpec(const UniChar
*uformat
, const uint8_t *cformat
, SInt32
*fmtIdx
, SInt32 fmtLen
, CFFormatSpec
*spec
) {
5192 Boolean seenDot
= false;
5195 if (fmtLen
<= *fmtIdx
) return; /* no type */
5196 if (cformat
) ch
= (UniChar
)cformat
[(*fmtIdx
)++]; else ch
= uformat
[(*fmtIdx
)++];
5197 reswtch
:switch (ch
) {
5198 case '#': // ignored for now
5201 if (!(spec
->flags
& kCFStringFormatPlusFlag
)) spec
->flags
|= kCFStringFormatSpaceFlag
;
5204 spec
->flags
|= kCFStringFormatMinusFlag
;
5205 spec
->flags
&= ~kCFStringFormatZeroFlag
; // remove zero flag
5208 spec
->flags
|= kCFStringFormatPlusFlag
;
5209 spec
->flags
&= ~kCFStringFormatSpaceFlag
; // remove space flag
5212 if (!(spec
->flags
& kCFStringFormatMinusFlag
)) spec
->flags
|= kCFStringFormatZeroFlag
;
5215 spec
->size
= CFFormatSize2
;
5218 if (*fmtIdx
< fmtLen
) {
5219 // fetch next character, don't increment fmtIdx
5220 if (cformat
) ch
= (UniChar
)cformat
[(*fmtIdx
)]; else ch
= uformat
[(*fmtIdx
)];
5221 if ('l' == ch
) { // 'll' for long long, like 'q'
5223 spec
->size
= CFFormatSize8
;
5227 spec
->size
= CFFormatSizeLong
; // 4 or 8 depending on LP64
5229 #if LONG_DOUBLE_SUPPORT
5231 spec
->size
= CFFormatSize16
;
5235 spec
->size
= CFFormatSize8
;
5238 spec
->size
= CFFormatSizeLong
; // 4 or 8 depending on LP64
5241 spec
->size
= CFFormatSize8
;
5244 spec
->type
= CFFormatLongType
;
5245 spec
->size
= CFFormatSize1
;
5247 case 'O': case 'o': case 'D': case 'd': case 'i': case 'U': case 'u': case 'x': case 'X':
5248 spec
->type
= CFFormatLongType
;
5249 // Seems like if spec->size == 0, we should spec->size = CFFormatSize4. However, 0 is handled correctly.
5251 case 'a': case 'A': case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
5252 spec
->type
= CFFormatDoubleType
;
5253 if (spec
->size
!= CFFormatSize16
) spec
->size
= CFFormatSize8
;
5255 case 'n': /* %n is not handled correctly; for Leopard or newer apps, we disable it further */
5256 spec
->type
= _CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard
) ? CFFormatDummyPointerType
: CFFormatPointerType
;
5257 spec
->size
= CFFormatSizePointer
; // 4 or 8 depending on LP64
5260 spec
->type
= CFFormatPointerType
;
5261 spec
->size
= CFFormatSizePointer
; // 4 or 8 depending on LP64
5264 spec
->type
= CFFormatCharsType
;
5265 spec
->size
= CFFormatSizePointer
; // 4 or 8 depending on LP64
5268 spec
->type
= CFFormatUnicharsType
;
5269 spec
->size
= CFFormatSizePointer
; // 4 or 8 depending on LP64
5272 spec
->type
= CFFormatSingleUnicharType
;
5273 spec
->size
= CFFormatSize2
;
5276 spec
->type
= CFFormatPascalCharsType
;
5277 spec
->size
= CFFormatSizePointer
; // 4 or 8 depending on LP64
5280 spec
->type
= CFFormatCFType
;
5281 spec
->size
= CFFormatSizePointer
; // 4 or 8 depending on LP64
5283 case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': {
5286 number
= 10 * number
+ (ch
- '0');
5287 if (cformat
) ch
= (UniChar
)cformat
[(*fmtIdx
)++]; else ch
= uformat
[(*fmtIdx
)++];
5288 } while ((UInt32
)(ch
- '0') <= 9);
5290 if (-2 == spec
->precArgNum
) {
5291 spec
->precArgNum
= (int8_t)number
- 1; // Arg numbers start from 1
5292 } else if (-2 == spec
->widthArgNum
) {
5293 spec
->widthArgNum
= (int8_t)number
- 1; // Arg numbers start from 1
5295 spec
->mainArgNum
= (int8_t)number
- 1; // Arg numbers start from 1
5298 } else if (seenDot
) { /* else it's either precision or width */
5299 spec
->precArg
= (SInt32
)number
;
5301 spec
->widthArg
= (SInt32
)number
;
5306 spec
->widthArgNum
= -2;
5310 if (cformat
) ch
= (UniChar
)cformat
[(*fmtIdx
)++]; else ch
= uformat
[(*fmtIdx
)++];
5312 spec
->precArgNum
= -2;
5317 spec
->type
= CFFormatLiteralType
;
5323 /* ??? It ignores the formatOptions argument.
5324 ??? %s depends on handling of encodings by __CFStringAppendBytes
5326 void CFStringAppendFormatAndArguments(CFMutableStringRef outputString
, CFDictionaryRef formatOptions
, CFStringRef formatString
, va_list args
) {
5327 _CFStringAppendFormatAndArgumentsAux(outputString
, NULL
, formatOptions
, formatString
, args
);
5330 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
5331 #define SNPRINTF(TYPE, WHAT) { \
5332 TYPE value = (TYPE) WHAT; \
5333 if (-1 != specs[curSpec].widthArgNum) { \
5334 if (-1 != specs[curSpec].precArgNum) { \
5335 snprintf_l(buffer, 255, NULL, formatBuffer, width, precision, value); \
5337 snprintf_l(buffer, 255, NULL, formatBuffer, width, value); \
5340 if (-1 != specs[curSpec].precArgNum) { \
5341 snprintf_l(buffer, 255, NULL, formatBuffer, precision, value); \
5343 snprintf_l(buffer, 255, NULL, formatBuffer, value); \
5347 #define SNPRINTF(TYPE, WHAT) { \
5348 TYPE value = (TYPE) WHAT; \
5349 if (-1 != specs[curSpec].widthArgNum) { \
5350 if (-1 != specs[curSpec].precArgNum) { \
5351 sprintf(buffer, formatBuffer, width, precision, value); \
5353 sprintf(buffer, formatBuffer, width, value); \
5356 if (-1 != specs[curSpec].precArgNum) { \
5357 sprintf(buffer, formatBuffer, precision, value); \
5359 sprintf(buffer, formatBuffer, value); \
5364 void _CFStringAppendFormatAndArgumentsAux(CFMutableStringRef outputString
, CFStringRef (*copyDescFunc
)(void *, const void *), CFDictionaryRef formatOptions
, CFStringRef formatString
, va_list args
) {
5365 SInt32 numSpecs
, sizeSpecs
, sizeArgNum
, formatIdx
, curSpec
, argNum
;
5367 #define FORMAT_BUFFER_LEN 400
5368 const uint8_t *cformat
= NULL
;
5369 const UniChar
*uformat
= NULL
;
5370 UniChar
*formatChars
= NULL
;
5371 UniChar localFormatBuffer
[FORMAT_BUFFER_LEN
];
5373 #define VPRINTF_BUFFER_LEN 61
5374 CFFormatSpec localSpecsBuffer
[VPRINTF_BUFFER_LEN
];
5375 CFFormatSpec
*specs
;
5376 CFPrintValue localValuesBuffer
[VPRINTF_BUFFER_LEN
];
5377 CFPrintValue
*values
;
5378 CFAllocatorRef tmpAlloc
= NULL
;
5380 intmax_t dummyLocation
; // A place for %n to do its thing in; should be the widest possible int value
5388 formatLen
= CFStringGetLength(formatString
);
5389 if (!CF_IS_OBJC(__kCFStringTypeID
, formatString
)) {
5390 __CFAssertIsString(formatString
);
5391 if (!__CFStrIsUnicode(formatString
)) {
5392 cformat
= (const uint8_t *)__CFStrContents(formatString
);
5393 if (cformat
) cformat
+= __CFStrSkipAnyLengthByte(formatString
);
5395 uformat
= (const UniChar
*)__CFStrContents(formatString
);
5398 if (!cformat
&& !uformat
) {
5399 formatChars
= (formatLen
> FORMAT_BUFFER_LEN
) ? (UniChar
*)CFAllocatorAllocate(tmpAlloc
= __CFGetDefaultAllocator(), formatLen
* sizeof(UniChar
), 0) : localFormatBuffer
;
5400 if (formatChars
!= localFormatBuffer
&& __CFOASafe
) __CFSetLastAllocationEventName(formatChars
, "CFString (temp)");
5401 CFStringGetCharacters(formatString
, CFRangeMake(0, formatLen
), formatChars
);
5402 uformat
= formatChars
;
5405 /* Compute an upper bound for the number of format specifications */
5407 for (formatIdx
= 0; formatIdx
< formatLen
; formatIdx
++) if ('%' == cformat
[formatIdx
]) sizeSpecs
++;
5409 for (formatIdx
= 0; formatIdx
< formatLen
; formatIdx
++) if ('%' == uformat
[formatIdx
]) sizeSpecs
++;
5411 tmpAlloc
= __CFGetDefaultAllocator();
5412 specs
= ((2 * sizeSpecs
+ 1) > VPRINTF_BUFFER_LEN
) ? (CFFormatSpec
*)CFAllocatorAllocate(tmpAlloc
, (2 * sizeSpecs
+ 1) * sizeof(CFFormatSpec
), 0) : localSpecsBuffer
;
5413 if (specs
!= localSpecsBuffer
&& __CFOASafe
) __CFSetLastAllocationEventName(specs
, "CFString (temp)");
5415 /* Collect format specification information from the format string */
5416 for (curSpec
= 0, formatIdx
= 0; formatIdx
< formatLen
; curSpec
++) {
5418 specs
[curSpec
].loc
= formatIdx
;
5419 specs
[curSpec
].len
= 0;
5420 specs
[curSpec
].size
= 0;
5421 specs
[curSpec
].type
= 0;
5422 specs
[curSpec
].flags
= 0;
5423 specs
[curSpec
].widthArg
= -1;
5424 specs
[curSpec
].precArg
= -1;
5425 specs
[curSpec
].mainArgNum
= -1;
5426 specs
[curSpec
].precArgNum
= -1;
5427 specs
[curSpec
].widthArgNum
= -1;
5429 for (newFmtIdx
= formatIdx
; newFmtIdx
< formatLen
&& '%' != cformat
[newFmtIdx
]; newFmtIdx
++);
5431 for (newFmtIdx
= formatIdx
; newFmtIdx
< formatLen
&& '%' != uformat
[newFmtIdx
]; newFmtIdx
++);
5433 if (newFmtIdx
!= formatIdx
) { /* Literal chunk */
5434 specs
[curSpec
].type
= CFFormatLiteralType
;
5435 specs
[curSpec
].len
= newFmtIdx
- formatIdx
;
5437 newFmtIdx
++; /* Skip % */
5438 __CFParseFormatSpec(uformat
, cformat
, &newFmtIdx
, formatLen
, &(specs
[curSpec
]));
5439 if (CFFormatLiteralType
== specs
[curSpec
].type
) {
5440 specs
[curSpec
].loc
= formatIdx
+ 1;
5441 specs
[curSpec
].len
= 1;
5443 specs
[curSpec
].len
= newFmtIdx
- formatIdx
;
5446 formatIdx
= newFmtIdx
;
5448 // fprintf(stderr, "specs[%d] = {\n size = %d,\n type = %d,\n loc = %d,\n len = %d,\n mainArgNum = %d,\n precArgNum = %d,\n widthArgNum = %d\n}\n", curSpec, specs[curSpec].size, specs[curSpec].type, specs[curSpec].loc, specs[curSpec].len, specs[curSpec].mainArgNum, specs[curSpec].precArgNum, specs[curSpec].widthArgNum);
5452 // Max of three args per spec, reasoning thus: 1 width, 1 prec, 1 value
5453 values
= ((3 * sizeSpecs
+ 1) > VPRINTF_BUFFER_LEN
) ? (CFPrintValue
*)CFAllocatorAllocate(tmpAlloc
, (3 * sizeSpecs
+ 1) * sizeof(CFPrintValue
), 0) : localValuesBuffer
;
5454 if (values
!= localValuesBuffer
&& __CFOASafe
) __CFSetLastAllocationEventName(values
, "CFString (temp)");
5455 memset(values
, 0, (3 * sizeSpecs
+ 1) * sizeof(CFPrintValue
));
5456 sizeArgNum
= (3 * sizeSpecs
+ 1);
5458 /* Compute values array */
5460 for (curSpec
= 0; curSpec
< numSpecs
; curSpec
++) {
5461 SInt32 newMaxArgNum
;
5462 if (0 == specs
[curSpec
].type
) continue;
5463 if (CFFormatLiteralType
== specs
[curSpec
].type
) continue;
5464 newMaxArgNum
= sizeArgNum
;
5465 if (newMaxArgNum
< specs
[curSpec
].mainArgNum
) {
5466 newMaxArgNum
= specs
[curSpec
].mainArgNum
;
5468 if (newMaxArgNum
< specs
[curSpec
].precArgNum
) {
5469 newMaxArgNum
= specs
[curSpec
].precArgNum
;
5471 if (newMaxArgNum
< specs
[curSpec
].widthArgNum
) {
5472 newMaxArgNum
= specs
[curSpec
].widthArgNum
;
5474 if (sizeArgNum
< newMaxArgNum
) {
5475 if (specs
!= localSpecsBuffer
) CFAllocatorDeallocate(tmpAlloc
, specs
);
5476 if (values
!= localValuesBuffer
) CFAllocatorDeallocate(tmpAlloc
, values
);
5477 if (formatChars
&& (formatChars
!= localFormatBuffer
)) CFAllocatorDeallocate(tmpAlloc
, formatChars
);
5478 return; // more args than we expected!
5480 /* It is actually incorrect to reorder some specs and not all; we just do some random garbage here */
5481 if (-2 == specs
[curSpec
].widthArgNum
) {
5482 specs
[curSpec
].widthArgNum
= argNum
++;
5484 if (-2 == specs
[curSpec
].precArgNum
) {
5485 specs
[curSpec
].precArgNum
= argNum
++;
5487 if (-1 == specs
[curSpec
].mainArgNum
) {
5488 specs
[curSpec
].mainArgNum
= argNum
++;
5490 values
[specs
[curSpec
].mainArgNum
].size
= specs
[curSpec
].size
;
5491 values
[specs
[curSpec
].mainArgNum
].type
= specs
[curSpec
].type
;
5492 if (-1 != specs
[curSpec
].widthArgNum
) {
5493 values
[specs
[curSpec
].widthArgNum
].size
= 0;
5494 values
[specs
[curSpec
].widthArgNum
].type
= CFFormatLongType
;
5496 if (-1 != specs
[curSpec
].precArgNum
) {
5497 values
[specs
[curSpec
].precArgNum
].size
= 0;
5498 values
[specs
[curSpec
].precArgNum
].type
= CFFormatLongType
;
5502 /* Collect the arguments in correct type from vararg list */
5503 for (argNum
= 0; argNum
< sizeArgNum
; argNum
++) {
5504 switch (values
[argNum
].type
) {
5506 case CFFormatLiteralType
:
5508 case CFFormatLongType
:
5509 case CFFormatSingleUnicharType
:
5510 if (CFFormatSize1
== values
[argNum
].size
) {
5511 values
[argNum
].value
.int64Value
= (int64_t)(int8_t)va_arg(args
, int);
5512 } else if (CFFormatSize2
== values
[argNum
].size
) {
5513 values
[argNum
].value
.int64Value
= (int64_t)(int16_t)va_arg(args
, int);
5514 } else if (CFFormatSize4
== values
[argNum
].size
) {
5515 values
[argNum
].value
.int64Value
= (int64_t)va_arg(args
, int32_t);
5516 } else if (CFFormatSize8
== values
[argNum
].size
) {
5517 values
[argNum
].value
.int64Value
= (int64_t)va_arg(args
, int64_t);
5519 values
[argNum
].value
.int64Value
= (int64_t)va_arg(args
, int);
5522 case CFFormatDoubleType
:
5523 #if LONG_DOUBLE_SUPPORT
5524 if (CFFormatSize16
== values
[argNum
].size
) {
5525 values
[argNum
].value
.longDoubleValue
= va_arg(args
, long double);
5529 values
[argNum
].value
.doubleValue
= va_arg(args
, double);
5532 case CFFormatPointerType
:
5533 case CFFormatObjectType
:
5534 case CFFormatCFType
:
5535 case CFFormatUnicharsType
:
5536 case CFFormatCharsType
:
5537 case CFFormatPascalCharsType
:
5538 values
[argNum
].value
.pointerValue
= va_arg(args
, void *);
5540 case CFFormatDummyPointerType
:
5541 (void)va_arg(args
, void *); // Skip the provided argument
5542 values
[argNum
].value
.pointerValue
= &dummyLocation
;
5548 /* Format the pieces together */
5549 for (curSpec
= 0; curSpec
< numSpecs
; curSpec
++) {
5550 SInt32 width
= 0, precision
= 0;
5552 Boolean hasWidth
= false, hasPrecision
= false;
5554 // widthArgNum and widthArg are never set at the same time; same for precArg*
5555 if (-1 != specs
[curSpec
].widthArgNum
) {
5556 width
= (SInt32
)values
[specs
[curSpec
].widthArgNum
].value
.int64Value
;
5559 if (-1 != specs
[curSpec
].precArgNum
) {
5560 precision
= (SInt32
)values
[specs
[curSpec
].precArgNum
].value
.int64Value
;
5561 hasPrecision
= true;
5563 if (-1 != specs
[curSpec
].widthArg
) {
5564 width
= specs
[curSpec
].widthArg
;
5567 if (-1 != specs
[curSpec
].precArg
) {
5568 precision
= specs
[curSpec
].precArg
;
5569 hasPrecision
= true;
5572 switch (specs
[curSpec
].type
) {
5573 case CFFormatLongType
:
5574 case CFFormatDoubleType
:
5575 case CFFormatPointerType
: {
5576 char formatBuffer
[128];
5577 #if defined(__GNUC__)
5578 char buffer
[256 + width
+ precision
];
5580 char stackBuffer
[512];
5581 char *dynamicBuffer
= NULL
;
5582 char *buffer
= stackBuffer
;
5583 if (256+width
+precision
> 512) {
5584 dynamicBuffer
= (char *)CFAllocatorAllocate(kCFAllocatorSystemDefault
, 256+width
+precision
, 0);
5585 buffer
= dynamicBuffer
;
5588 SInt32 cidx
, idx
, loc
;
5589 Boolean appended
= false;
5590 loc
= specs
[curSpec
].loc
;
5591 // In preparation to call snprintf(), copy the format string out
5593 for (idx
= 0, cidx
= 0; cidx
< specs
[curSpec
].len
; idx
++, cidx
++) {
5594 if ('$' == cformat
[loc
+ cidx
]) {
5595 for (idx
--; '0' <= formatBuffer
[idx
] && formatBuffer
[idx
] <= '9'; idx
--);
5597 formatBuffer
[idx
] = cformat
[loc
+ cidx
];
5601 for (idx
= 0, cidx
= 0; cidx
< specs
[curSpec
].len
; idx
++, cidx
++) {
5602 if ('$' == uformat
[loc
+ cidx
]) {
5603 for (idx
--; '0' <= formatBuffer
[idx
] && formatBuffer
[idx
] <= '9'; idx
--);
5605 formatBuffer
[idx
] = (int8_t)uformat
[loc
+ cidx
];
5609 formatBuffer
[idx
] = '\0';
5610 // Should modify format buffer here if necessary; for example, to translate %qd to
5611 // the equivalent, on architectures which do not have %q.
5612 buffer
[sizeof(buffer
) - 1] = '\0';
5613 switch (specs
[curSpec
].type
) {
5614 case CFFormatLongType
:
5615 if (CFFormatSize8
== specs
[curSpec
].size
) {
5616 SNPRINTF(int64_t, values
[specs
[curSpec
].mainArgNum
].value
.int64Value
)
5618 SNPRINTF(SInt32
, values
[specs
[curSpec
].mainArgNum
].value
.int64Value
)
5621 case CFFormatPointerType
:
5622 case CFFormatDummyPointerType
:
5623 SNPRINTF(void *, values
[specs
[curSpec
].mainArgNum
].value
.pointerValue
)
5626 case CFFormatDoubleType
:
5627 #if LONG_DOUBLE_SUPPORT
5628 if (CFFormatSize16
== specs
[curSpec
].size
) {
5629 SNPRINTF(long double, values
[specs
[curSpec
].mainArgNum
].value
.longDoubleValue
)
5633 SNPRINTF(double, values
[specs
[curSpec
].mainArgNum
].value
.doubleValue
)
5635 // See if we need to localize the decimal point
5636 if (formatOptions
) { // We have localization info
5637 CFStringRef decimalSeparator
= (CFGetTypeID(formatOptions
) == CFLocaleGetTypeID()) ? (CFStringRef
)CFLocaleGetValue((CFLocaleRef
)formatOptions
, kCFLocaleDecimalSeparatorKey
) : (CFStringRef
)CFDictionaryGetValue(formatOptions
, CFSTR("NSDecimalSeparator"));
5638 if (decimalSeparator
!= NULL
) { // We have a decimal separator in there
5639 CFIndex decimalPointLoc
= 0;
5640 while (buffer
[decimalPointLoc
] != 0 && buffer
[decimalPointLoc
] != '.') decimalPointLoc
++;
5641 if (buffer
[decimalPointLoc
] == '.') { // And we have a decimal point in the formatted string
5642 buffer
[decimalPointLoc
] = 0;
5643 CFStringAppendCString(outputString
, (const char *)buffer
, __CFStringGetEightBitStringEncoding());
5644 CFStringAppend(outputString
, decimalSeparator
);
5645 CFStringAppendCString(outputString
, (const char *)(buffer
+ decimalPointLoc
+ 1), __CFStringGetEightBitStringEncoding());
5652 if (!appended
) CFStringAppendCString(outputString
, (const char *)buffer
, __CFStringGetEightBitStringEncoding());
5653 #if !defined(__GNUC__)
5654 if (dynamicBuffer
) {
5655 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, dynamicBuffer
);
5660 case CFFormatLiteralType
:
5662 __CFStringAppendBytes(outputString
, (const char *)(cformat
+specs
[curSpec
].loc
), specs
[curSpec
].len
, __CFStringGetEightBitStringEncoding());
5664 CFStringAppendCharacters(outputString
, uformat
+specs
[curSpec
].loc
, specs
[curSpec
].len
);
5667 case CFFormatPascalCharsType
:
5668 case CFFormatCharsType
:
5669 if (values
[specs
[curSpec
].mainArgNum
].value
.pointerValue
== NULL
) {
5670 CFStringAppendCString(outputString
, "(null)", kCFStringEncodingASCII
);
5673 const char *str
= (const char *)values
[specs
[curSpec
].mainArgNum
].value
.pointerValue
;
5674 if (specs
[curSpec
].type
== CFFormatPascalCharsType
) { // Pascal string case
5675 len
= ((unsigned char *)str
)[0];
5677 if (hasPrecision
&& precision
< len
) len
= precision
;
5678 } else { // C-string case
5679 if (!hasPrecision
) { // No precision, so rely on the terminating null character
5681 } else { // Don't blindly call strlen() if there is a precision; the string might not have a terminating null (3131988)
5682 const char *terminatingNull
= (const char *)memchr(str
, 0, precision
); // Basically strlen() on only the first precision characters of str
5683 if (terminatingNull
) { // There was a null in the first precision characters
5684 len
= terminatingNull
- str
;
5690 // Since the spec says the behavior of the ' ', '0', '#', and '+' flags is undefined for
5691 // '%s', and since we have ignored them in the past, the behavior is hereby cast in stone
5692 // to ignore those flags (and, say, never pad with '0' instead of space).
5693 if (specs
[curSpec
].flags
& kCFStringFormatMinusFlag
) {
5694 __CFStringAppendBytes(outputString
, str
, len
, __CFStringGetSystemEncoding());
5695 if (hasWidth
&& width
> len
) {
5696 int w
= width
- len
; // We need this many spaces; do it ten at a time
5697 do {__CFStringAppendBytes(outputString
, " ", (w
> 10 ? 10 : w
), kCFStringEncodingASCII
);} while ((w
-= 10) > 0);
5700 if (hasWidth
&& width
> len
) {
5701 int w
= width
- len
; // We need this many spaces; do it ten at a time
5702 do {__CFStringAppendBytes(outputString
, " ", (w
> 10 ? 10 : w
), kCFStringEncodingASCII
);} while ((w
-= 10) > 0);
5704 __CFStringAppendBytes(outputString
, str
, len
, __CFStringGetSystemEncoding());
5708 case CFFormatSingleUnicharType
:
5709 ch
= (UniChar
)values
[specs
[curSpec
].mainArgNum
].value
.int64Value
;
5710 CFStringAppendCharacters(outputString
, &ch
, 1);
5712 case CFFormatUnicharsType
:
5713 //??? need to handle width, precision, and padding arguments
5714 up
= (UniChar
*)values
[specs
[curSpec
].mainArgNum
].value
.pointerValue
;
5716 CFStringAppendCString(outputString
, "(null)", kCFStringEncodingASCII
);
5719 for (len
= 0; 0 != up
[len
]; len
++);
5720 // Since the spec says the behavior of the ' ', '0', '#', and '+' flags is undefined for
5721 // '%s', and since we have ignored them in the past, the behavior is hereby cast in stone
5722 // to ignore those flags (and, say, never pad with '0' instead of space).
5723 if (hasPrecision
&& precision
< len
) len
= precision
;
5724 if (specs
[curSpec
].flags
& kCFStringFormatMinusFlag
) {
5725 CFStringAppendCharacters(outputString
, up
, len
);
5726 if (hasWidth
&& width
> len
) {
5727 int w
= width
- len
; // We need this many spaces; do it ten at a time
5728 do {__CFStringAppendBytes(outputString
, " ", (w
> 10 ? 10 : w
), kCFStringEncodingASCII
);} while ((w
-= 10) > 0);
5731 if (hasWidth
&& width
> len
) {
5732 int w
= width
- len
; // We need this many spaces; do it ten at a time
5733 do {__CFStringAppendBytes(outputString
, " ", (w
> 10 ? 10 : w
), kCFStringEncodingASCII
);} while ((w
-= 10) > 0);
5735 CFStringAppendCharacters(outputString
, up
, len
);
5739 case CFFormatCFType
:
5740 case CFFormatObjectType
:
5741 if (NULL
!= values
[specs
[curSpec
].mainArgNum
].value
.pointerValue
) {
5742 CFStringRef str
= NULL
;
5744 str
= copyDescFunc(values
[specs
[curSpec
].mainArgNum
].value
.pointerValue
, formatOptions
);
5746 str
= __CFCopyFormattingDescription(values
[specs
[curSpec
].mainArgNum
].value
.pointerValue
, formatOptions
);
5748 str
= CFCopyDescription(values
[specs
[curSpec
].mainArgNum
].value
.pointerValue
);
5752 CFStringAppend(outputString
, str
);
5755 CFStringAppendCString(outputString
, "(null description)", kCFStringEncodingASCII
);
5758 CFStringAppendCString(outputString
, "(null)", kCFStringEncodingASCII
);
5764 if (specs
!= localSpecsBuffer
) CFAllocatorDeallocate(tmpAlloc
, specs
);
5765 if (values
!= localValuesBuffer
) CFAllocatorDeallocate(tmpAlloc
, values
);
5766 if (formatChars
&& (formatChars
!= localFormatBuffer
)) CFAllocatorDeallocate(tmpAlloc
, formatChars
);
5772 void CFShowStr(CFStringRef str
) {
5773 CFAllocatorRef alloc
;
5776 fprintf(stdout
, "(null)\n");
5780 if (CF_IS_OBJC(__kCFStringTypeID
, str
)) {
5781 fprintf(stdout
, "This is an NSString, not CFString\n");
5785 alloc
= CFGetAllocator(str
);
5787 fprintf(stdout
, "\nLength %d\nIsEightBit %d\n", (int)__CFStrLength(str
), __CFStrIsEightBit(str
));
5788 fprintf(stdout
, "HasLengthByte %d\nHasNullByte %d\nInlineContents %d\n",
5789 __CFStrHasLengthByte(str
), __CFStrHasNullByte(str
), __CFStrIsInline(str
));
5791 fprintf(stdout
, "Allocator ");
5792 if (alloc
!= kCFAllocatorSystemDefault
) {
5793 fprintf(stdout
, "%p\n", (void *)alloc
);
5795 fprintf(stdout
, "SystemDefault\n");
5797 fprintf(stdout
, "Mutable %d\n", __CFStrIsMutable(str
));
5798 if (!__CFStrIsMutable(str
) && __CFStrHasContentsDeallocator(str
)) {
5799 if (__CFStrContentsDeallocator(str
)) fprintf(stdout
, "ContentsDeallocatorFunc %p\n", (void *)__CFStrContentsDeallocator(str
));
5800 else fprintf(stdout
, "ContentsDeallocatorFunc None\n");
5801 } else if (__CFStrIsMutable(str
) && __CFStrHasContentsAllocator(str
)) {
5802 fprintf(stdout
, "ExternalContentsAllocator %p\n", (void *)__CFStrContentsAllocator((CFMutableStringRef
)str
));
5805 if (__CFStrIsMutable(str
)) {
5806 fprintf(stdout
, "CurrentCapacity %d\n%sCapacity %d\n", (int)__CFStrCapacity(str
), __CFStrIsFixed(str
) ? "Fixed" : "Desired", (int)__CFStrDesiredCapacity(str
));
5808 fprintf(stdout
, "Contents %p\n", (void *)__CFStrContents(str
));
5817 #undef HANGUL_SCOUNT
5818 #undef HANGUL_LCOUNT
5819 #undef HANGUL_VCOUNT
5820 #undef HANGUL_TCOUNT
5821 #undef HANGUL_NCOUNT