-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathCFString.c
6950 lines (5936 loc) · 335 KB
/
CFString.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* CFString.c
Copyright (c) 1998-2016, Apple Inc. and the Swift project authors
Portions Copyright (c) 2014-2016 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception
See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
Responsibility: Ali Ozer
!!! For performance reasons, it's important that all functions marked CF_INLINE in this file are inlined.
*/
#include <CoreFoundation/CFBase.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFDictionary.h>
#include <CoreFoundation/CFStringEncodingConverterExt.h>
#include <CoreFoundation/CFUniChar.h>
#include <CoreFoundation/CFUnicodeDecomposition.h>
#include <CoreFoundation/CFUnicodePrecomposition.h>
#include <CoreFoundation/CFPriv.h>
#include <CoreFoundation/CFNumber.h>
#include <CoreFoundation/CFNumberFormatter.h>
#include "CFInternal.h"
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
#include "CFLocaleInternal.h"
#include "CFStringLocalizedFormattingInternal.h"
#endif
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
#include <unistd.h>
#endif
#if defined(__GNUC__)
#define LONG_DOUBLE_SUPPORT 1
#else
#define LONG_DOUBLE_SUPPORT 0
#endif
#define USE_STRING_ROM 0
#ifndef INSTRUMENT_SHARED_STRINGS
#define INSTRUMENT_SHARED_STRINGS 0
#endif
extern CF_PRIVATE const CFStringRef __kCFLocaleCollatorID;
#if INSTRUMENT_SHARED_STRINGS
#include <sys/stat.h> /* for umask() */
static void __CFRecordStringAllocationEvent(const char *encoding, const char *bytes, CFIndex byteCount) {
static CFLock_t lock = CFLockInit;
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
__CFLock(&lock);
static int fd;
if (! fd) {
extern char **_NSGetProgname(void);
const char *name = *_NSGetProgname();
if (! name) name = "UNKNOWN";
umask(0);
char path[1024];
snprintf(path, sizeof(path), "/tmp/CFSharedStringInstrumentation_%s_%d.txt", name, getpid());
fd = open(path, O_WRONLY | O_APPEND | O_CREAT, 0666);
if (fd <= 0) {
int error = errno;
const char *errString = strerror(error);
fprintf(stderr, "open() failed with error %d (%s)\n", error, errString);
}
}
if (fd > 0) {
char *buffer = NULL;
char formatString[256];
snprintf(formatString, sizeof(formatString), "%%-8d\t%%-16s\t%%.%lds\n", byteCount);
int resultCount = asprintf(&buffer, formatString, getpid(), encoding, bytes);
if (buffer && resultCount > 0) write(fd, buffer, resultCount);
else puts("Couldn't record allocation event");
free(buffer);
}
__CFUnlock(&lock);
}
#endif //INSTRUMENT_SHARED_STRINGS
typedef Boolean (*UNI_CHAR_FUNC)(UInt32 flags, UInt8 ch, UniChar *unicodeChar);
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
extern size_t malloc_good_size(size_t size);
#endif
extern void __CFStrConvertBytesToUnicode(const uint8_t *bytes, UniChar *buffer, CFIndex numChars);
static void __CFStringAppendFormatCore(CFMutableStringRef outputString, CFStringRef (*copyDescFunc)(void *, const void *), CFStringRef (*contextDescFunc)(void *, const void *, const void *, bool, bool *), CFDictionaryRef formatOptions, CFDictionaryRef stringsDictConfig, CFStringRef formatString, CFIndex initialArgPosition, const void *origValues, CFIndex originalValuesSize, va_list args);
#if defined(DEBUG)
// We put this into C & Pascal strings if we can't convert
#define CONVERSIONFAILURESTR "CFString conversion failed"
// We set this to true when purging the constant string table, so CFStringDeallocate doesn't assert
static Boolean __CFConstantStringTableBeingFreed = false;
#endif
// Two constant strings used by CFString; these are initialized in CFStringInitialize
CONST_STRING_DECL(kCFEmptyString, "")
// This is separate for C++
struct __notInlineMutable {
void *buffer;
CFIndex length;
CFIndex capacity; // Capacity in bytes
unsigned int hasGap:1; // Currently unused
unsigned int isFixedCapacity:1;
unsigned int isExternalMutable:1;
unsigned int capacityProvidedExternally:1;
#if __LP64__
unsigned long desiredCapacity:60;
#else
unsigned long desiredCapacity:28;
#endif
CFAllocatorRef contentsAllocator; // Optional
}; // The only mutable variant for CFString
/* !!! Never do sizeof(CFString); the union is here just to make it easier to access some fields.
*/
struct __CFString {
CFRuntimeBase base;
union { // In many cases the allocated structs are smaller than these
struct __inline1 {
CFIndex length;
} inline1; // Bytes follow the length
struct __notInlineImmutable1 {
void *buffer; // Note that the buffer is in the same place for all non-inline variants of CFString
CFIndex length;
CFAllocatorRef contentsDeallocator; // Optional; just the dealloc func is used
} notInlineImmutable1; // This is the usual not-inline immutable CFString
struct __notInlineImmutable2 {
void *buffer;
CFAllocatorRef contentsDeallocator; // Optional; just the dealloc func is used
} notInlineImmutable2; // This is the not-inline immutable CFString when length is stored with the contents (first byte)
struct __notInlineMutable notInlineMutable;
} variants;
};
/*
I = is immutable
E = not inline contents
U = is Unicode
N = has NULL byte
L = has length byte
D = explicit deallocator for contents (for mutable objects, allocator)
C = length field is CFIndex (rather than UInt32); only meaningful for 64-bit, really
if needed this bit (valuable real-estate) can be given up for another bit elsewhere, since this info is needed just for 64-bit
Also need (only for mutable)
F = is fixed
G = has gap
Cap, DesCap = capacity
B7 B6 B5 B4 B3 B2 B1 B0
U N L C I
B6 B5
0 0 inline contents
0 1 E (freed with default allocator)
1 0 E (not freed)
1 1 E D
!!! Note: Constant CFStrings use the bit patterns:
C8 (11001000 = default allocator, not inline, not freed contents; 8-bit; has NULL byte; doesn't have length; is immutable)
D0 (11010000 = default allocator, not inline, not freed contents; Unicode; is immutable)
The bit usages should not be modified in a way that would effect these bit patterns.
Note that some of the bit patterns in the enum below overlap and are duplicated. Keep this in mind as you do searches for use cases.
*/
enum {
__kCFFreeContentsWhenDoneMask = 0x020,
__kCFFreeContentsWhenDone = 0x020,
__kCFContentsMask = 0x060,
__kCFHasInlineContents = 0x000,
__kCFNotInlineContentsNoFree = 0x040, // Don't free
__kCFNotInlineContentsDefaultFree = 0x020, // Use allocator's free function
__kCFNotInlineContentsCustomFree = 0x060, // Use a specially provided free function
__kCFHasContentsAllocatorMask = 0x060,
__kCFHasContentsAllocator = 0x060, // (For mutable strings) use a specially provided allocator
__kCFHasContentsDeallocatorMask = 0x060,
__kCFHasContentsDeallocator = 0x060,
__kCFIsMutableMask = 0x01,
__kCFIsMutable = 0x01,
__kCFIsUnicodeMask = 0x10,
__kCFIsUnicode = 0x10,
__kCFHasNullByteMask = 0x08,
__kCFHasNullByte = 0x08,
__kCFHasLengthByteMask = 0x04,
__kCFHasLengthByte = 0x04,
// !!! Bit 0x02 has been freed up
};
// !!! Assumptions:
// Mutable strings are not inline
// Compile-time constant strings are not inline
// Mutable strings always have explicit length (but they might also have length byte and null byte)
// If there is an explicit length, always use that instead of the length byte (length byte is useful for quickly returning pascal strings)
// Never look at the length byte for the length; use __CFStrLength or __CFStrLength2
/* The following set of functions and macros need to be updated on change to the bit configuration
*/
CF_INLINE Boolean __CFStrIsMutable(CFStringRef str) {return (str->base._cfinfo[CF_INFO_BITS] & __kCFIsMutableMask) == __kCFIsMutable;}
CF_INLINE Boolean __CFStrIsInline(CFStringRef str) {return (str->base._cfinfo[CF_INFO_BITS] & __kCFContentsMask) == __kCFHasInlineContents;}
CF_INLINE Boolean __CFStrFreeContentsWhenDone(CFStringRef str) {return (str->base._cfinfo[CF_INFO_BITS] & __kCFFreeContentsWhenDoneMask) == __kCFFreeContentsWhenDone;}
CF_INLINE Boolean __CFStrHasContentsDeallocator(CFStringRef str) {return (str->base._cfinfo[CF_INFO_BITS] & __kCFHasContentsDeallocatorMask) == __kCFHasContentsDeallocator;}
CF_INLINE Boolean __CFStrIsUnicode(CFStringRef str) {return (str->base._cfinfo[CF_INFO_BITS] & __kCFIsUnicodeMask) == __kCFIsUnicode;}
CF_INLINE Boolean __CFStrIsEightBit(CFStringRef str) {return (str->base._cfinfo[CF_INFO_BITS] & __kCFIsUnicodeMask) != __kCFIsUnicode;}
CF_INLINE Boolean __CFStrHasNullByte(CFStringRef str) {return (str->base._cfinfo[CF_INFO_BITS] & __kCFHasNullByteMask) == __kCFHasNullByte;}
CF_INLINE Boolean __CFStrHasLengthByte(CFStringRef str) {return (str->base._cfinfo[CF_INFO_BITS] & __kCFHasLengthByteMask) == __kCFHasLengthByte;}
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
CF_INLINE Boolean __CFStrIsConstant(CFStringRef str) {
#if DEPLOYMENT_RUNTIME_SWIFT
return str->base._swift_strong_rc & _CF_SWIFT_RC_PINNED_FLAG;
#else
#if __LP64__
return str->base._rc == 0;
#else
return (str->base._cfinfo[CF_RC_BITS]) == 0;
#endif
#endif
}
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
/* Returns ptr to the buffer (which might include the length byte).
*/
CF_INLINE const void *__CFStrContents(CFStringRef str) {
if (__CFStrIsInline(str)) {
return (const void *)(((uintptr_t)&(str->variants)) + (__CFStrHasExplicitLength(str) ? sizeof(CFIndex) : 0));
} else { // Not inline; pointer is always word 2
return str->variants.notInlineImmutable1.buffer;
}
}
static CFAllocatorRef *__CFStrContentsDeallocatorPtr(CFStringRef str) {
return __CFStrHasExplicitLength(str) ? &(((CFMutableStringRef)str)->variants.notInlineImmutable1.contentsDeallocator) : &(((CFMutableStringRef)str)->variants.notInlineImmutable2.contentsDeallocator); }
// Assumption: Called with immutable strings only, and on strings that are known to have a contentsDeallocator
CF_INLINE CFAllocatorRef __CFStrContentsDeallocator(CFStringRef str) {
return *__CFStrContentsDeallocatorPtr(str);
}
// Assumption: Called with immutable strings only, and on strings that are known to have a contentsDeallocator
CF_INLINE void __CFStrSetContentsDeallocator(CFStringRef str, CFAllocatorRef allocator) {
if (!(0 || 0)) CFRetain(allocator);
*__CFStrContentsDeallocatorPtr(str) = allocator;
}
static CFAllocatorRef *__CFStrContentsAllocatorPtr(CFStringRef str) {
CFAssert(!__CFStrIsInline(str), __kCFLogAssertion, "Asking for contents allocator of inline string");
CFAssert(__CFStrIsMutable(str), __kCFLogAssertion, "Asking for contents allocator of an immutable string");
return (CFAllocatorRef *)&(str->variants.notInlineMutable.contentsAllocator);
}
// Assumption: Called with strings that have a contents allocator; also, contents allocator follows custom
CF_INLINE CFAllocatorRef __CFStrContentsAllocator(CFMutableStringRef str) {
return *(__CFStrContentsAllocatorPtr(str));
}
// Assumption: Called with strings that have a contents allocator; also, contents allocator follows custom
CF_INLINE void __CFStrSetContentsAllocator(CFMutableStringRef str, CFAllocatorRef allocator) {
if (!(0 || 0)) CFRetain(allocator);
*(__CFStrContentsAllocatorPtr(str)) = allocator;
}
/* Returns length; use __CFStrLength2 if contents buffer pointer has already been computed.
*/
CF_INLINE CFIndex __CFStrLength(CFStringRef str) {
if (__CFStrHasExplicitLength(str)) {
if (__CFStrIsInline(str)) {
return str->variants.inline1.length;
} else {
return str->variants.notInlineImmutable1.length;
}
} else {
return (CFIndex)(*((uint8_t *)__CFStrContents(str)));
}
}
CF_INLINE CFIndex __CFStrLength2(CFStringRef str, const void *buffer) {
if (__CFStrHasExplicitLength(str)) {
if (__CFStrIsInline(str)) {
return str->variants.inline1.length;
} else {
return str->variants.notInlineImmutable1.length;
}
} else {
return (CFIndex)(*((uint8_t *)buffer));
}
}
Boolean __CFStringIsEightBit(CFStringRef str) {
return __CFStrIsEightBit(str);
}
/* Sets the content pointer for immutable or mutable strings.
*/
CF_INLINE void __CFStrSetContentPtr(CFStringRef str, const void *p) {
// XXX_PCB catch all writes for mutable string case.
*((void **)&((CFMutableStringRef)str)->variants.notInlineImmutable1.buffer) = (void *)p;
}
CF_INLINE void __CFStrSetInfoBits(CFStringRef str, UInt32 v) {__CFBitfieldSetValue(((CFMutableStringRef)str)->base._cfinfo[CF_INFO_BITS], 6, 0, v);}
CF_INLINE void __CFStrSetExplicitLength(CFStringRef str, CFIndex v) {
if (__CFStrIsInline(str)) {
((CFMutableStringRef)str)->variants.inline1.length = v;
} else {
((CFMutableStringRef)str)->variants.notInlineImmutable1.length = v;
}
}
CF_INLINE void __CFStrSetUnicode(CFMutableStringRef str) {str->base._cfinfo[CF_INFO_BITS] |= __kCFIsUnicode;}
CF_INLINE void __CFStrClearUnicode(CFMutableStringRef str) {str->base._cfinfo[CF_INFO_BITS] &= ~__kCFIsUnicode;}
CF_INLINE void __CFStrSetHasLengthAndNullBytes(CFMutableStringRef str) {str->base._cfinfo[CF_INFO_BITS] |= (__kCFHasLengthByte | __kCFHasNullByte);}
CF_INLINE void __CFStrClearHasLengthAndNullBytes(CFMutableStringRef str) {str->base._cfinfo[CF_INFO_BITS] &= ~(__kCFHasLengthByte | __kCFHasNullByte);}
// Assumption: The following set of inlines (using str->variants.notInlineMutable) are called with mutable strings only
CF_INLINE Boolean __CFStrIsFixed(CFStringRef str) {return str->variants.notInlineMutable.isFixedCapacity;}
CF_INLINE Boolean __CFStrIsExternalMutable(CFStringRef str) {return str->variants.notInlineMutable.isExternalMutable;}
CF_INLINE Boolean __CFStrHasContentsAllocator(CFStringRef str) {return (str->base._cfinfo[CF_INFO_BITS] & __kCFHasContentsAllocatorMask) == __kCFHasContentsAllocator;}
CF_INLINE void __CFStrSetIsFixed(CFMutableStringRef str) {str->variants.notInlineMutable.isFixedCapacity = 1;}
CF_INLINE void __CFStrSetIsExternalMutable(CFMutableStringRef str) {str->variants.notInlineMutable.isExternalMutable = 1;}
//CF_INLINE void __CFStrSetHasGap(CFMutableStringRef str) {str->variants.notInlineMutable.hasGap = 1;} currently unused
// If capacity is provided externally, we only change it when we need to grow beyond it
CF_INLINE Boolean __CFStrCapacityProvidedExternally(CFStringRef str) {return str->variants.notInlineMutable.capacityProvidedExternally;}
CF_INLINE void __CFStrSetCapacityProvidedExternally(CFMutableStringRef str) {str->variants.notInlineMutable.capacityProvidedExternally = 1;}
CF_INLINE void __CFStrClearCapacityProvidedExternally(CFMutableStringRef str) {str->variants.notInlineMutable.capacityProvidedExternally = 0;}
// "Capacity" is stored in number of bytes, not characters. It indicates the total number of bytes in the contents buffer.
CF_INLINE CFIndex __CFStrCapacity(CFStringRef str) {return str->variants.notInlineMutable.capacity;}
CF_INLINE void __CFStrSetCapacity(CFMutableStringRef str, CFIndex cap) {str->variants.notInlineMutable.capacity = cap;}
// "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.
CF_INLINE CFIndex __CFStrDesiredCapacity(CFStringRef str) {return str->variants.notInlineMutable.desiredCapacity;}
CF_INLINE void __CFStrSetDesiredCapacity(CFMutableStringRef str, CFIndex size) {str->variants.notInlineMutable.desiredCapacity = size;}
static void *__CFStrAllocateMutableContents(CFMutableStringRef str, CFIndex size) {
void *ptr;
CFAllocatorRef alloc = (__CFStrHasContentsAllocator(str)) ? __CFStrContentsAllocator(str) : __CFGetAllocator(str);
ptr = CFAllocatorAllocate(alloc, size, 0);
if (__CFOASafe) __CFSetLastAllocationEventName(ptr, "CFString (store)");
return ptr;
}
static void __CFStrDeallocateMutableContents(CFMutableStringRef str, void *buffer) {
CFAllocatorRef alloc = (__CFStrHasContentsAllocator(str)) ? __CFStrContentsAllocator(str) : __CFGetAllocator(str);
if (__CFStrIsMutable(str) && __CFStrHasContentsAllocator(str) && (0)) {
// do nothing
} else {
CFAllocatorDeallocate(alloc, buffer);
}
}
#if 0
// Mark contents from not freed at all to being managed by default allocator (happens on mutation of externally mutable buffer string which was set to no free)
CF_INLINE void __CFStrEnsureContentsFreeable(CFMutableStringRef str) {
if ((str->base._cfinfo[CF_INFO_BITS] & __kCFContentsMask) == __kCFNotInlineContentsNoFree) {
str->base._cfinfo[CF_INFO_BITS] &= ~__kCFContentsMask;
str->base._cfinfo[CF_INFO_BITS] |= __kCFNotInlineContentsDefaultFree;
}
}
#endif
/* CFString specific init flags
Note that you cannot count on the external buffer not being copied.
Also, if you specify an external buffer, you should not change it behind the CFString's back.
*/
enum {
__kCFThinUnicodeIfPossible = 0x1000000, /* See if the Unicode contents can be thinned down to 8-bit */
kCFStringPascal = 0x10000, /* Indicating that the string data has a Pascal string structure (length byte at start) */
kCFStringNoCopyProvidedContents = 0x20000, /* Don't copy the provided string contents if possible; free it when no longer needed */
kCFStringNoCopyNoFreeProvidedContents = 0x30000 /* Don't copy the provided string contents if possible; don't free it when no longer needed */
};
/* System Encoding.
*/
static CFStringEncoding __CFDefaultSystemEncoding = kCFStringEncodingInvalidId;
static CFStringEncoding __CFDefaultFileSystemEncoding = kCFStringEncodingInvalidId;
CFStringEncoding __CFDefaultEightBitStringEncoding = kCFStringEncodingInvalidId;
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
#define __defaultEncoding kCFStringEncodingMacRoman
#elif DEPLOYMENT_TARGET_LINUX
#define __defaultEncoding kCFStringEncodingUTF8
#elif DEPLOYMENT_TARGET_WINDOWS
#define __defaultEncoding kCFStringEncodingWindowsLatin1
#else
#warning This value must match __CFGetConverter condition in CFStringEncodingConverter.c
#define __defaultEncoding kCFStringEncodingISOLatin1
#endif
CFStringEncoding CFStringGetSystemEncoding(void) {
if (__CFDefaultSystemEncoding == kCFStringEncodingInvalidId) {
__CFDefaultSystemEncoding = __defaultEncoding;
const CFStringEncodingConverter *converter = CFStringEncodingGetConverter(__CFDefaultSystemEncoding);
__CFSetCharToUniCharFunc(converter->encodingClass == kCFStringEncodingConverterCheapEightBit ? (UNI_CHAR_FUNC)converter->toUnicode : NULL);
}
return __CFDefaultSystemEncoding;
}
// Fast version for internal use
CF_INLINE CFStringEncoding __CFStringGetSystemEncoding(void) {
if (__CFDefaultSystemEncoding == kCFStringEncodingInvalidId) (void)CFStringGetSystemEncoding();
return __CFDefaultSystemEncoding;
}
CFStringEncoding CFStringFileSystemEncoding(void) {
if (__CFDefaultFileSystemEncoding == kCFStringEncodingInvalidId) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_WINDOWS
__CFDefaultFileSystemEncoding = kCFStringEncodingUTF8;
#else
__CFDefaultFileSystemEncoding = CFStringGetSystemEncoding();
#endif
}
return __CFDefaultFileSystemEncoding;
}
/* ??? Is returning length when no other answer is available the right thing?
!!! All of the (length > (LONG_MAX / N)) type checks are to avoid wrap-around and eventual malloc overflow in the client
*/
CFIndex CFStringGetMaximumSizeForEncoding(CFIndex length, CFStringEncoding encoding) {
if (encoding == kCFStringEncodingUTF8) {
return (length > (LONG_MAX / 3)) ? kCFNotFound : (length * 3);
} else if ((encoding == kCFStringEncodingUTF32) || (encoding == kCFStringEncodingUTF32BE) || (encoding == kCFStringEncodingUTF32LE)) { // UTF-32
return (length > (LONG_MAX / sizeof(UTF32Char))) ? kCFNotFound : (length * sizeof(UTF32Char));
} else {
encoding &= 0xFFF; // Mask off non-base part
}
switch (encoding) {
case kCFStringEncodingUnicode:
return (length > (LONG_MAX / sizeof(UniChar))) ? kCFNotFound : (length * sizeof(UniChar));
case kCFStringEncodingNonLossyASCII:
return (length > (LONG_MAX / 6)) ? kCFNotFound : (length * 6); // 1 Unichar can expand to 6 bytes
case kCFStringEncodingMacRoman:
case kCFStringEncodingWindowsLatin1:
case kCFStringEncodingISOLatin1:
case kCFStringEncodingNextStepLatin:
case kCFStringEncodingASCII:
return length / sizeof(uint8_t);
default:
return length / sizeof(uint8_t);
}
}
/* Returns whether the indicated encoding can be stored in 8-bit chars
*/
CF_INLINE Boolean __CFStrEncodingCanBeStoredInEightBit(CFStringEncoding encoding) {
switch (encoding & 0xFFF) { // just use encoding base
case kCFStringEncodingInvalidId:
case kCFStringEncodingUnicode:
case kCFStringEncodingNonLossyASCII:
return false;
case kCFStringEncodingMacRoman:
case kCFStringEncodingWindowsLatin1:
case kCFStringEncodingISOLatin1:
case kCFStringEncodingNextStepLatin:
case kCFStringEncodingASCII:
return true;
default: return false;
}
}
/* Returns the encoding used in eight bit CFStrings (can't be any encoding which isn't 1-to-1 with Unicode)
For 10.9-linked apps, we've set this encoding to ASCII for all cases; see <rdar://problem/3597233>
*/
CFStringEncoding __CFStringComputeEightBitStringEncoding(void) {
// This flag prevents recursive entry into __CFStringComputeEightBitStringEncoding
static Boolean __CFStringIsBeingInitialized2 = false;
if (__CFStringIsBeingInitialized2) return kCFStringEncodingASCII;
__CFStringIsBeingInitialized2 = true;
Boolean useAscii = true;
__CFStringIsBeingInitialized2 = false;
if (useAscii) {
__CFDefaultEightBitStringEncoding = kCFStringEncodingASCII;
} else {
if (__CFDefaultEightBitStringEncoding == kCFStringEncodingInvalidId) {
CFStringEncoding systemEncoding = CFStringGetSystemEncoding();
if (systemEncoding == kCFStringEncodingInvalidId) { // We're right in the middle of querying system encoding from default database. Delaying to set until system encoding is determined.
return kCFStringEncodingASCII;
} else if (__CFStrEncodingCanBeStoredInEightBit(systemEncoding)) {
__CFDefaultEightBitStringEncoding = systemEncoding;
} else {
__CFDefaultEightBitStringEncoding = kCFStringEncodingASCII;
}
}
}
return __CFDefaultEightBitStringEncoding;
}
/* Returns whether the provided bytes can be stored in ASCII
*/
CF_INLINE Boolean __CFBytesInASCII(const uint8_t *bytes, CFIndex len) {
#if __LP64__
/* A bit of unrolling; go by 32s, 16s, and 8s first */
while (len >= 32) {
uint64_t val = *(const uint64_t *)bytes;
uint64_t hiBits = (val & 0x8080808080808080ULL); // More efficient to collect this rather than do a conditional at every step
bytes += 8;
val = *(const uint64_t *)bytes;
hiBits |= (val & 0x8080808080808080ULL);
bytes += 8;
val = *(const uint64_t *)bytes;
hiBits |= (val & 0x8080808080808080ULL);
bytes += 8;
val = *(const uint64_t *)bytes;
if (hiBits | (val & 0x8080808080808080ULL)) return false;
bytes += 8;
len -= 32;
}
while (len >= 16) {
uint64_t val = *(const uint64_t *)bytes;
uint64_t hiBits = (val & 0x8080808080808080ULL);
bytes += 8;
val = *(const uint64_t *)bytes;
if (hiBits | (val & 0x8080808080808080ULL)) return false;
bytes += 8;
len -= 16;
}
while (len >= 8) {
uint64_t val = *(const uint64_t *)bytes;
if (val & 0x8080808080808080ULL) return false;
bytes += 8;
len -= 8;
}
#endif
/* Go by 4s */
while (len >= 4) {
uint32_t val = *(const uint32_t *)bytes;
if (val & 0x80808080U) return false;
bytes += 4;
len -= 4;
}
/* Handle the rest one byte at a time */
while (len--) {
if (*bytes++ & 0x80) return false;
}
return true;
}
/* Returns whether the provided 8-bit string in the specified encoding can be stored in an 8-bit CFString.
*/
CF_INLINE Boolean __CFCanUseEightBitCFStringForBytes(const uint8_t *bytes, CFIndex len, CFStringEncoding encoding) {
// If the encoding is the same as the 8-bit CFString encoding, we can just use the bytes as-is.
// One exception is ASCII, which unfortunately needs to mean ISOLatin1 for compatibility reasons <rdar://problem/5458321>.
if (encoding == __CFStringGetEightBitStringEncoding() && encoding != kCFStringEncodingASCII) return true;
if (__CFStringEncodingIsSupersetOfASCII(encoding) && __CFBytesInASCII(bytes, len)) return true;
return false;
}
/* Returns whether a length byte can be tacked on to a string of the indicated length.
*/
CF_INLINE Boolean __CFCanUseLengthByte(CFIndex len) {
#define __kCFMaxPascalStrLen 255
return (len <= __kCFMaxPascalStrLen) ? true : false;
}
/* Various string assertions
*/
#define __CFAssertIsString(cf) __CFGenericValidateType(cf, __kCFStringTypeID)
#define __CFAssertIndexIsInStringBounds(cf, idx) CFAssert3((idx) >= 0 && (idx) < __CFStrLength(cf), __kCFLogAssertion, "%s(): string index %ld out of bounds (length %ld)", __PRETTY_FUNCTION__, idx, __CFStrLength(cf))
#define __CFAssertRangeIsInStringBounds(cf, idx, count) CFAssert4((idx) >= 0 && (idx + count) <= __CFStrLength(cf), __kCFLogAssertion, "%s(): string range %ld,%ld out of bounds (length %ld)", __PRETTY_FUNCTION__, idx, count, __CFStrLength(cf))
#define __CFAssertIsStringAndMutable(cf) {__CFGenericValidateType(cf, __kCFStringTypeID); CFAssert1(__CFStrIsMutable(cf), __kCFLogAssertion, "%s(): string not mutable", __PRETTY_FUNCTION__);}
#define __CFAssertIsStringAndExternalMutable(cf) {__CFGenericValidateType(cf, __kCFStringTypeID); CFAssert1(__CFStrIsMutable(cf) && __CFStrIsExternalMutable(cf), __kCFLogAssertion, "%s(): string not external mutable", __PRETTY_FUNCTION__);}
#define __CFAssertIsNotNegative(idx) CFAssert2(idx >= 0, __kCFLogAssertion, "%s(): index %ld is negative", __PRETTY_FUNCTION__, idx)
#define __CFAssertIfFixedLengthIsOK(cf, reqLen) CFAssert2(!__CFStrIsFixed(cf) || (reqLen <= __CFStrDesiredCapacity(cf)), __kCFLogAssertion, "%s(): length %ld too large", __PRETTY_FUNCTION__, reqLen)
/* 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).
Additional complications are applied in the following order:
- desiredCapacity, which is the minimum (except initially things can be at zero)
- rounding up to factor of 8
- compressing (to fit the number if 16 bits), which effectively rounds up to factor of 256
- 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
*/
#define SHRINKFACTOR(c) (c / 2)
#if __LP64__
#define GROWFACTOR(c) ((c * 3 + 1) / 2)
#else
#define GROWFACTOR(c) (((c) >= (ULONG_MAX / 3UL)) ? __CFMax(LONG_MAX - 4095, (c)) : (((unsigned long)c * 3 + 1) / 2))
#endif
CF_INLINE CFIndex __CFStrNewCapacity(CFMutableStringRef str, unsigned long reqCapacity, CFIndex capacity, Boolean leaveExtraRoom, CFIndex charSize) {
if (capacity != 0 || reqCapacity != 0) { /* If initially zero, and space not needed, leave it at that... */
if ((capacity < reqCapacity) || /* We definitely need the room... */
(!__CFStrCapacityProvidedExternally(str) && /* Assuming we control the capacity... */
((reqCapacity < SHRINKFACTOR(capacity)) || /* ...we have too much room! */
(!leaveExtraRoom && (reqCapacity < capacity))))) { /* ...we need to eliminate the extra space... */
if (reqCapacity > LONG_MAX) return -1; /* Too big any way you cut it */
unsigned long newCapacity = leaveExtraRoom ? GROWFACTOR(reqCapacity) : reqCapacity; /* Grow by 3/2 if extra room is desired */
CFIndex desiredCapacity = __CFStrDesiredCapacity(str) * charSize;
if (newCapacity < desiredCapacity) { /* If less than desired, bump up to desired */
newCapacity = desiredCapacity;
} else if (__CFStrIsFixed(str)) { /* Otherwise, if fixed, no need to go above the desired (fixed) capacity */
newCapacity = __CFMax(desiredCapacity, reqCapacity); /* !!! So, fixed is not really fixed, but "tight" */
}
if (__CFStrHasContentsAllocator(str)) { /* Also apply any preferred size from the allocator */
newCapacity = CFAllocatorGetPreferredSizeForSize(__CFStrContentsAllocator(str), newCapacity, 0);
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
} else {
newCapacity = malloc_good_size(newCapacity);
#endif
}
return (newCapacity > LONG_MAX) ? -1 : (CFIndex)newCapacity; // If packing: __CFStrUnpackNumber(__CFStrPackNumber(newCapacity));
}
}
return capacity;
}
/* 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.
numBlocks is current total number of blocks within buffer.
blockSize is the size of each block in bytes
ranges and numRanges hold the ranges that are no longer needed; ranges are stored sorted in increasing order, and don't overlap
insertLength is the final spacing between the remaining blocks
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
if insertLength = 4, result = A B ? ? ? ? D ? ? ? ? G H
if insertLength = 0, result = A B D G H
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
if insertLength = 3, result = A ? ? ? C ? ? ? E ? ? ? Q ? ? ? S ? ? ? U
*/
typedef struct _CFStringDeferredRange {
CFIndex beginning;
CFIndex length;
CFIndex shift;
} CFStringDeferredRange;
typedef struct _CFStringStackInfo {
CFIndex capacity; // Capacity (if capacity == count, need to realloc to add another)
CFIndex count; // Number of elements actually stored
CFStringDeferredRange *stack;
Boolean hasMalloced; // Indicates "stack" is allocated and needs to be deallocated when done
char _padding[3];
} CFStringStackInfo;
CF_INLINE void pop (CFStringStackInfo *si, CFStringDeferredRange *topRange) {
si->count = si->count - 1;
*topRange = si->stack[si->count];
}
CF_INLINE void push (CFStringStackInfo *si, const CFStringDeferredRange *newRange) {
if (si->count == si->capacity) {
// increase size of the stack
si->capacity = (si->capacity + 4) * 2;
if (si->hasMalloced) {
si->stack = (CFStringDeferredRange *)CFAllocatorReallocate(kCFAllocatorSystemDefault, si->stack, si->capacity * sizeof(CFStringDeferredRange), 0);
} else {
CFStringDeferredRange *newStack = (CFStringDeferredRange *)CFAllocatorAllocate(kCFAllocatorSystemDefault, si->capacity * sizeof(CFStringDeferredRange), 0);
memmove(newStack, si->stack, si->count * sizeof(CFStringDeferredRange));
si->stack = newStack;
si->hasMalloced = true;
}
}
si->stack[si->count] = *newRange;
si->count = si->count + 1;
}
static void rearrangeBlocks(
uint8_t *buffer,
CFIndex numBlocks,
CFIndex blockSize,
const CFRange *ranges,
CFIndex numRanges,
CFIndex insertLength) {
#define origStackSize 10
CFStringDeferredRange origStack[origStackSize];
CFStringStackInfo si = {origStackSize, 0, origStack, false, {0, 0, 0}};
CFStringDeferredRange currentNonRange = {0, 0, 0};
CFIndex currentRange = 0;
CFIndex amountShifted = 0;
// must have at least 1 range left.
while (currentRange < numRanges) {
currentNonRange.beginning = (ranges[currentRange].location + ranges[currentRange].length) * blockSize;
if ((numRanges - currentRange) == 1) {
// at the end.
currentNonRange.length = numBlocks * blockSize - currentNonRange.beginning;
if (currentNonRange.length == 0) break;
} else {
currentNonRange.length = (ranges[currentRange + 1].location * blockSize) - currentNonRange.beginning;
}
currentNonRange.shift = amountShifted + (insertLength * blockSize) - (ranges[currentRange].length * blockSize);
amountShifted = currentNonRange.shift;
if (amountShifted <= 0) {
// process current item and rest of stack
if (currentNonRange.shift && currentNonRange.length) memmove (&buffer[currentNonRange.beginning + currentNonRange.shift], &buffer[currentNonRange.beginning], currentNonRange.length);
while (si.count > 0) {
pop (&si, ¤tNonRange); // currentNonRange now equals the top element of the stack.
if (currentNonRange.shift && currentNonRange.length) memmove (&buffer[currentNonRange.beginning + currentNonRange.shift], &buffer[currentNonRange.beginning], currentNonRange.length);
}
} else {
// add currentNonRange to stack.
push (&si, ¤tNonRange);
}
currentRange++;
}
// no more ranges. if anything is on the stack, process.
while (si.count > 0) {
pop (&si, ¤tNonRange); // currentNonRange now equals the top element of the stack.
if (currentNonRange.shift && currentNonRange.length) memmove (&buffer[currentNonRange.beginning + currentNonRange.shift], &buffer[currentNonRange.beginning], currentNonRange.length);
}
if (si.hasMalloced) CFAllocatorDeallocate (kCFAllocatorSystemDefault, si.stack);
}
/* 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.)
*/
static void copyBlocks(
const uint8_t *srcBuffer,
uint8_t *dstBuffer,
CFIndex srcLength,
Boolean srcIsUnicode,
Boolean dstIsUnicode,
const CFRange *ranges,
CFIndex numRanges,
CFIndex insertLength) {
CFIndex srcLocationInBytes = 0; // in order to avoid multiplying all the time, this is in terms of bytes, not blocks
CFIndex dstLocationInBytes = 0; // ditto
CFIndex srcBlockSize = srcIsUnicode ? sizeof(UniChar) : sizeof(uint8_t);
CFIndex insertLengthInBytes = insertLength * (dstIsUnicode ? sizeof(UniChar) : sizeof(uint8_t));
CFIndex rangeIndex = 0;
CFIndex srcToDstMultiplier = (srcIsUnicode == dstIsUnicode) ? 1 : (sizeof(UniChar) / sizeof(uint8_t));
// Loop over the ranges, copying the range to be preserved (right before each range)
while (rangeIndex < numRanges) {
CFIndex srcLengthInBytes = ranges[rangeIndex].location * srcBlockSize - srcLocationInBytes; // srcLengthInBytes is in terms of bytes, not blocks; represents length of region to be preserved
if (srcLengthInBytes > 0) {
if (srcIsUnicode == dstIsUnicode) {
memmove(dstBuffer + dstLocationInBytes, srcBuffer + srcLocationInBytes, srcLengthInBytes);
} else {
__CFStrConvertBytesToUnicode(srcBuffer + srcLocationInBytes, (UniChar *)(dstBuffer + dstLocationInBytes), srcLengthInBytes);
}
}
srcLocationInBytes += srcLengthInBytes + ranges[rangeIndex].length * srcBlockSize; // Skip over the just-copied and to-be-deleted stuff
dstLocationInBytes += srcLengthInBytes * srcToDstMultiplier + insertLengthInBytes;
rangeIndex++;
}
// Do last range (the one beyond last range)
if (srcLocationInBytes < srcLength * srcBlockSize) {
if (srcIsUnicode == dstIsUnicode) {
memmove(dstBuffer + dstLocationInBytes, srcBuffer + srcLocationInBytes, srcLength * srcBlockSize - srcLocationInBytes);
} else {
__CFStrConvertBytesToUnicode(srcBuffer + srcLocationInBytes, (UniChar *)(dstBuffer + dstLocationInBytes), srcLength * srcBlockSize - srcLocationInBytes);
}
}
}
/* Call the callback; if it doesn't exist or returns false, then log
*/
static void __CFStringHandleOutOfMemory(CFTypeRef obj) {
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.");
{
CFLog(kCFLogLevelCritical, CFSTR("%@"), msg);
}
}
/* 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.)
*/
static void __CFStringChangeSizeMultiple(CFMutableStringRef str, const CFRange *deleteRanges, CFIndex numDeleteRanges, CFIndex insertLength, Boolean makeUnicode) {
const uint8_t *curContents = (uint8_t *)__CFStrContents(str);
CFIndex curLength = curContents ? __CFStrLength2(str, curContents) : 0;
unsigned long newLength; // We use unsigned to better keep track of overflow
// Compute new length of the string
if (numDeleteRanges == 1) {
newLength = curLength + insertLength - deleteRanges[0].length;
} else {
CFIndex cnt;
newLength = curLength + insertLength * numDeleteRanges;
for (cnt = 0; cnt < numDeleteRanges; cnt++) newLength -= deleteRanges[cnt].length;
}
// Disabled: <rdar://problem/23208702> Questionable assert in CFString.c
//__CFAssertIfFixedLengthIsOK(str, newLength);
if (newLength == 0) {
// An somewhat optimized code-path for this special case, with the following implicit values:
// newIsUnicode = false
// useLengthAndNullBytes = false
// newCharSize = sizeof(uint8_t)
// If the newCapacity happens to be the same as the old, we don't free the buffer; otherwise we just free it totally
// instead of doing a potentially useless reallocation (as the needed capacity later might turn out to be different anyway)
CFIndex curCapacity = __CFStrCapacity(str);
CFIndex newCapacity = __CFStrNewCapacity(str, 0, curCapacity, true, sizeof(uint8_t));
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
if (curContents) __CFStrDeallocateMutableContents(str, (uint8_t *)curContents);
__CFStrSetContentPtr(str, NULL);
__CFStrSetCapacity(str, 0);
__CFStrClearCapacityProvidedExternally(str);
__CFStrClearHasLengthAndNullBytes(str);
if (!__CFStrIsExternalMutable(str)) __CFStrClearUnicode(str); // External mutable implies Unicode
} else {
if (!__CFStrIsExternalMutable(str)) {
__CFStrClearUnicode(str);
if (curCapacity >= (int)(sizeof(uint8_t) * 2)) { // If there's room
__CFStrSetHasLengthAndNullBytes(str);
((uint8_t *)curContents)[0] = ((uint8_t *)curContents)[1] = 0;
} else {
__CFStrClearHasLengthAndNullBytes(str);
}
}
}
__CFStrSetExplicitLength(str, 0);
} else { /* This else-clause assumes newLength > 0 */
Boolean oldIsUnicode = __CFStrIsUnicode(str);
Boolean newIsUnicode = makeUnicode || (oldIsUnicode /* && (newLength > 0) - implicit */ ) || __CFStrIsExternalMutable(str);
CFIndex newCharSize = newIsUnicode ? sizeof(UniChar) : sizeof(uint8_t);
Boolean useLengthAndNullBytes = !newIsUnicode /* && (newLength > 0) - implicit */;
CFIndex numExtraBytes = useLengthAndNullBytes ? 2 : 0; /* 2 extra bytes to keep the length byte & null... */
CFIndex curCapacity = __CFStrCapacity(str);
if (newLength > (LONG_MAX - numExtraBytes) / newCharSize) __CFStringHandleOutOfMemory(str); // Does not return
CFIndex newCapacity = __CFStrNewCapacity(str, newLength * newCharSize + numExtraBytes, curCapacity, true, newCharSize);
if (newCapacity == -1) __CFStringHandleOutOfMemory(str); // Does not return
Boolean allocNewBuffer = (newCapacity != curCapacity) || (curLength > 0 && !oldIsUnicode && newIsUnicode); /* We alloc new buffer if oldIsUnicode != newIsUnicode because the contents have to be copied */
uint8_t *newContents;
if (allocNewBuffer) {
newContents = (uint8_t *)__CFStrAllocateMutableContents(str, newCapacity);
if (!newContents) { // Try allocating without extra room
newCapacity = __CFStrNewCapacity(str, newLength * newCharSize + numExtraBytes, curCapacity, false, newCharSize);
// Since we checked for this above, it shouldn't be the case here, but just in case
if (newCapacity == -1) __CFStringHandleOutOfMemory(str); // Does not return
newContents = (uint8_t *)__CFStrAllocateMutableContents(str, newCapacity);
if (!newContents) __CFStringHandleOutOfMemory(str); // Does not return
}
} else {
newContents = (uint8_t *)curContents;
}
Boolean hasLengthAndNullBytes = __CFStrHasLengthByte(str);
CFAssert1(hasLengthAndNullBytes == __CFStrHasNullByte(str), __kCFLogAssertion, "%s(): Invalid state in 8-bit string", __PRETTY_FUNCTION__);
// Calculate pointers to the actual string content (skipping over the length byte, if present). Note that keeping a reference to the base is needed for newContents under GC, since the copy may take a long time.
const uint8_t *curContentsBody = hasLengthAndNullBytes ? (curContents+1) : curContents;
uint8_t *newContentsBody = useLengthAndNullBytes ? (newContents+1) : newContents;
if (curContents) {
if (oldIsUnicode == newIsUnicode) {
if (newContentsBody == curContentsBody) {
rearrangeBlocks(newContentsBody, curLength, newCharSize, deleteRanges, numDeleteRanges, insertLength);
} else {
copyBlocks(curContentsBody, newContentsBody, curLength, oldIsUnicode, newIsUnicode, deleteRanges, numDeleteRanges, insertLength);
}
} else if (newIsUnicode) { /* this implies we have a new buffer */
copyBlocks(curContentsBody, newContentsBody, curLength, oldIsUnicode, newIsUnicode, deleteRanges, numDeleteRanges, insertLength);
}
if (allocNewBuffer && __CFStrFreeContentsWhenDone(str)) __CFStrDeallocateMutableContents(str, (void *)curContents);
}
if (!newIsUnicode) {
if (useLengthAndNullBytes) {
newContentsBody[newLength] = 0; /* Always have null byte, if not unicode */
newContents[0] = __CFCanUseLengthByte(newLength) ? (uint8_t)newLength : 0;
if (!hasLengthAndNullBytes) __CFStrSetHasLengthAndNullBytes(str);
} else {
if (hasLengthAndNullBytes) __CFStrClearHasLengthAndNullBytes(str);
}
if (oldIsUnicode) __CFStrClearUnicode(str);
} else { // New is unicode...
if (!oldIsUnicode) __CFStrSetUnicode(str);
if (hasLengthAndNullBytes) __CFStrClearHasLengthAndNullBytes(str);
}
__CFStrSetExplicitLength(str, newLength);
if (allocNewBuffer) {
__CFStrSetCapacity(str, newCapacity);
__CFStrClearCapacityProvidedExternally(str);
// __CFStrEnsureContentsFreeable(str); // Commented out until we clarify: <rdar://problem/27151105>. Until then will leak: <rdar://problem/26346533>
__CFStrSetContentPtr(str, newContents);
}
}
}
/* Same as above, but takes one range (very common case)
*/
CF_INLINE void __CFStringChangeSize(CFMutableStringRef str, CFRange range, CFIndex insertLength, Boolean makeUnicode) {
__CFStringChangeSizeMultiple(str, &range, 1, insertLength, makeUnicode);
}
#if defined(DEBUG)
static Boolean __CFStrIsConstantString(CFStringRef str);
#endif
static void __CFStringDeallocate(CFTypeRef cf) {
CFStringRef str = (CFStringRef)cf;
// If in DEBUG mode, check to see if the string a CFSTR, and complain.
CFAssert1(__CFConstantStringTableBeingFreed || !__CFStrIsConstantString((CFStringRef)cf), __kCFLogAssertion, "Tried to deallocate CFSTR(\"%@\")", str);
if (!__CFStrIsInline(str)) {
uint8_t *contents;
Boolean isMutable = __CFStrIsMutable(str);
if (__CFStrFreeContentsWhenDone(str) && (contents = (uint8_t *)__CFStrContents(str))) {
if (isMutable) {
__CFStrDeallocateMutableContents((CFMutableStringRef)str, contents);
} else {
if (__CFStrHasContentsDeallocator(str)) {
CFAllocatorRef allocator = __CFStrContentsDeallocator(str);
CFAllocatorDeallocate(allocator, contents);
if (!(0 || 0 )) CFRelease(allocator);
} else {
CFAllocatorRef alloc = __CFGetAllocator(str);
CFAllocatorDeallocate(alloc, contents);
}
}
}
if (isMutable && __CFStrHasContentsAllocator(str)) {
CFAllocatorRef allocator = __CFStrContentsAllocator((CFMutableStringRef)str);
if (!(0 || 0)) CFRelease(allocator);
}
}
}
static Boolean __CFStringEqual(CFTypeRef cf1, CFTypeRef cf2) {
CFStringRef str1 = (CFStringRef)cf1;
CFStringRef str2 = (CFStringRef)cf2;
const uint8_t *contents1;
const uint8_t *contents2;
CFIndex len1;
/* !!! We do not need IsString assertions, as the CFBase runtime assures this */
/* !!! We do not need == test, as the CFBase runtime assures this */
contents1 = (uint8_t *)__CFStrContents(str1);
contents2 = (uint8_t *)__CFStrContents(str2);
len1 = __CFStrLength2(str1, contents1);
if (len1 != __CFStrLength2(str2, contents2)) return false;
contents1 += __CFStrSkipAnyLengthByte(str1);
contents2 += __CFStrSkipAnyLengthByte(str2);
if (__CFStrIsEightBit(str1) && __CFStrIsEightBit(str2)) {
return memcmp((const char *)contents1, (const char *)contents2, len1) ? false : true;
} else if (__CFStrIsEightBit(str1)) { /* One string has Unicode contents */
CFStringInlineBuffer buf;
CFIndex buf_idx = 0;
CFStringInitInlineBuffer(str1, &buf, CFRangeMake(0, len1));
for (buf_idx = 0; buf_idx < len1; buf_idx++) {
if (__CFStringGetCharacterFromInlineBufferQuick(&buf, buf_idx) != ((UniChar *)contents2)[buf_idx]) return false;
}
} else if (__CFStrIsEightBit(str2)) { /* One string has Unicode contents */
CFStringInlineBuffer buf;
CFIndex buf_idx = 0;
CFStringInitInlineBuffer(str2, &buf, CFRangeMake(0, len1));
for (buf_idx = 0; buf_idx < len1; buf_idx++) {
if (__CFStringGetCharacterFromInlineBufferQuick(&buf, buf_idx) != ((UniChar *)contents1)[buf_idx]) return false;
}
} else { /* Both strings have Unicode contents */
CFIndex idx;
for (idx = 0; idx < len1; idx++) {
if (((UniChar *)contents1)[idx] != ((UniChar *)contents2)[idx]) return false;
}
}
return true;
}