]> git.saurik.com Git - apple/cf.git/blob - CFBinaryPList.c
CF-635.tar.gz
[apple/cf.git] / CFBinaryPList.c
1 /*
2 * Copyright (c) 2011 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /* CFBinaryPList.c
25 Copyright (c) 2000-2011, Apple Inc. All rights reserved.
26 Responsibility: Tony Parker
27 */
28
29
30 #include <CoreFoundation/CFString.h>
31 #include <CoreFoundation/CFNumber.h>
32 #include <CoreFoundation/CFDate.h>
33 #include <CoreFoundation/CFData.h>
34 #include <CoreFoundation/CFError.h>
35 #include <CoreFoundation/CFArray.h>
36 #include <CoreFoundation/CFDictionary.h>
37 #include <CoreFoundation/CFSet.h>
38 #include <CoreFoundation/CFPropertyList.h>
39 #include <CoreFoundation/CFByteOrder.h>
40 #include <CoreFoundation/CFRuntime.h>
41 #include <stdio.h>
42 #include <limits.h>
43 #include <string.h>
44 #include "CFInternal.h"
45 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
46 #include <CoreFoundation/CFStream.h>
47 #endif
48
49 typedef struct {
50 int64_t high;
51 uint64_t low;
52 } CFSInt128Struct;
53
54 enum {
55 kCFNumberSInt128Type = 17
56 };
57
58 CF_EXPORT CFNumberType _CFNumberGetType2(CFNumberRef number);
59 __private_extern__ CFErrorRef __CFPropertyListCreateError(CFIndex code, CFStringRef debugString, ...);
60
61 enum {
62 CF_NO_ERROR = 0,
63 CF_OVERFLOW_ERROR = (1 << 0),
64 };
65
66 CF_INLINE uint32_t __check_uint32_add_unsigned_unsigned(uint32_t x, uint32_t y, int32_t* err) {
67 if((UINT_MAX - y) < x)
68 *err = *err | CF_OVERFLOW_ERROR;
69 return x + y;
70 };
71
72 CF_INLINE uint64_t __check_uint64_add_unsigned_unsigned(uint64_t x, uint64_t y, int32_t* err) {
73 if((ULLONG_MAX - y) < x)
74 *err = *err | CF_OVERFLOW_ERROR;
75 return x + y;
76 };
77
78 CF_INLINE uint32_t __check_uint32_mul_unsigned_unsigned(uint32_t x, uint32_t y, int32_t* err) {
79 uint64_t tmp = (uint64_t) x * (uint64_t) y;
80 /* If any of the upper 32 bits touched, overflow */
81 if(tmp & 0xffffffff00000000ULL)
82 *err = *err | CF_OVERFLOW_ERROR;
83 return (uint32_t) tmp;
84 };
85
86 CF_INLINE uint64_t __check_uint64_mul_unsigned_unsigned(uint64_t x, uint64_t y, int32_t* err) {
87 if(x == 0) return 0;
88 if(ULLONG_MAX/x < y)
89 *err = *err | CF_OVERFLOW_ERROR;
90 return x * y;
91 };
92
93 #if __LP64__
94 #define check_ptr_add(p, a, err) (const uint8_t *)__check_uint64_add_unsigned_unsigned((uintptr_t)p, (uintptr_t)a, err)
95 #define check_size_t_mul(b, a, err) (size_t)__check_uint64_mul_unsigned_unsigned((size_t)b, (size_t)a, err)
96 #else
97 #define check_ptr_add(p, a, err) (const uint8_t *)__check_uint32_add_unsigned_unsigned((uintptr_t)p, (uintptr_t)a, err)
98 #define check_size_t_mul(b, a, err) (size_t)__check_uint32_mul_unsigned_unsigned((size_t)b, (size_t)a, err)
99 #endif
100
101
102 struct __CFKeyedArchiverUID {
103 CFRuntimeBase _base;
104 uint32_t _value;
105 };
106
107 static CFStringRef __CFKeyedArchiverUIDCopyDescription(CFTypeRef cf) {
108 CFKeyedArchiverUIDRef uid = (CFKeyedArchiverUIDRef)cf;
109 return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFKeyedArchiverUID %p [%p]>{value = %u}"), cf, CFGetAllocator(cf), uid->_value);
110 }
111
112 static CFStringRef __CFKeyedArchiverUIDCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
113 CFKeyedArchiverUIDRef uid = (CFKeyedArchiverUIDRef)cf;
114 return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("@%u@"), uid->_value);
115 }
116
117 static CFTypeID __kCFKeyedArchiverUIDTypeID = _kCFRuntimeNotATypeID;
118
119 static const CFRuntimeClass __CFKeyedArchiverUIDClass = {
120 0,
121 "CFKeyedArchiverUID",
122 NULL, // init
123 NULL, // copy
124 NULL, // finalize
125 NULL, // equal -- pointer equality only
126 NULL, // hash -- pointer hashing only
127 __CFKeyedArchiverUIDCopyFormattingDescription,
128 __CFKeyedArchiverUIDCopyDescription
129 };
130
131 __private_extern__ void __CFKeyedArchiverUIDInitialize(void) {
132 __kCFKeyedArchiverUIDTypeID = _CFRuntimeRegisterClass(&__CFKeyedArchiverUIDClass);
133 }
134
135 CFTypeID _CFKeyedArchiverUIDGetTypeID(void) {
136 return __kCFKeyedArchiverUIDTypeID;
137 }
138
139 CFKeyedArchiverUIDRef _CFKeyedArchiverUIDCreate(CFAllocatorRef allocator, uint32_t value) {
140 CFKeyedArchiverUIDRef uid;
141 uid = (CFKeyedArchiverUIDRef)_CFRuntimeCreateInstance(allocator, __kCFKeyedArchiverUIDTypeID, sizeof(struct __CFKeyedArchiverUID) - sizeof(CFRuntimeBase), NULL);
142 if (NULL == uid) {
143 return NULL;
144 }
145 ((struct __CFKeyedArchiverUID *)uid)->_value = value;
146 return uid;
147 }
148
149
150 uint32_t _CFKeyedArchiverUIDGetValue(CFKeyedArchiverUIDRef uid) {
151 return uid->_value;
152 }
153
154
155 typedef struct {
156 CFTypeRef stream;
157 CFErrorRef error;
158 uint64_t written;
159 int32_t used;
160 bool streamIsData;
161 uint8_t buffer[8192 - 32];
162 } __CFBinaryPlistWriteBuffer;
163
164 static void writeBytes(__CFBinaryPlistWriteBuffer *buf, const UInt8 *bytes, CFIndex length) {
165 if (0 == length) return;
166 if (buf->error) return;
167 if (buf->streamIsData) {
168 CFDataAppendBytes((CFMutableDataRef)buf->stream, bytes, length);
169 buf->written += length;
170 } else {
171 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
172 while (0 < length) {
173 CFIndex ret = CFWriteStreamWrite((CFWriteStreamRef)buf->stream, bytes, length);
174 if (ret == 0) {
175 buf->error = __CFPropertyListCreateError(kCFPropertyListWriteStreamError, CFSTR("Binary property list writing could not be completed because stream is full."));
176 return;
177 }
178 if (ret < 0) {
179 CFErrorRef err = CFWriteStreamCopyError((CFWriteStreamRef)buf->stream);
180 buf->error = err ? err : __CFPropertyListCreateError(kCFPropertyListWriteStreamError, CFSTR("Binary property list writing could not be completed because the stream had an unknown error."));
181 return;
182 }
183 buf->written += ret;
184 length -= ret;
185 bytes += ret;
186 }
187 #else
188 CFAssert(false, __kCFLogAssertion, "Streams are not supported on this platform");
189 #endif
190 }
191 }
192
193 static void bufferWrite(__CFBinaryPlistWriteBuffer *buf, const uint8_t *buffer, CFIndex count) {
194 if (0 == count) return;
195 if ((CFIndex)sizeof(buf->buffer) <= count) {
196 writeBytes(buf, buf->buffer, buf->used);
197 buf->used = 0;
198 writeBytes(buf, buffer, count);
199 return;
200 }
201 CFIndex copyLen = __CFMin(count, (CFIndex)sizeof(buf->buffer) - buf->used);
202 memmove(buf->buffer + buf->used, buffer, copyLen);
203 buf->used += copyLen;
204 if (sizeof(buf->buffer) == buf->used) {
205 writeBytes(buf, buf->buffer, sizeof(buf->buffer));
206 memmove(buf->buffer, buffer + copyLen, count - copyLen);
207 buf->used = count - copyLen;
208 }
209 }
210
211 static void bufferFlush(__CFBinaryPlistWriteBuffer *buf) {
212 writeBytes(buf, buf->buffer, buf->used);
213 buf->used = 0;
214 }
215
216 /*
217 HEADER
218 magic number ("bplist")
219 file format version
220
221 OBJECT TABLE
222 variable-sized objects
223
224 Object Formats (marker byte followed by additional info in some cases)
225 null 0000 0000
226 bool 0000 1000 // false
227 bool 0000 1001 // true
228 fill 0000 1111 // fill byte
229 int 0001 nnnn ... // # of bytes is 2^nnnn, big-endian bytes
230 real 0010 nnnn ... // # of bytes is 2^nnnn, big-endian bytes
231 date 0011 0011 ... // 8 byte float follows, big-endian bytes
232 data 0100 nnnn [int] ... // nnnn is number of bytes unless 1111 then int count follows, followed by bytes
233 string 0101 nnnn [int] ... // ASCII string, nnnn is # of chars, else 1111 then int count, then bytes
234 string 0110 nnnn [int] ... // Unicode string, nnnn is # of chars, else 1111 then int count, then big-endian 2-byte uint16_t
235 0111 xxxx // unused
236 uid 1000 nnnn ... // nnnn+1 is # of bytes
237 1001 xxxx // unused
238 array 1010 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows
239 1011 xxxx // unused
240 set 1100 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows
241 dict 1101 nnnn [int] keyref* objref* // nnnn is count, unless '1111', then int count follows
242 1110 xxxx // unused
243 1111 xxxx // unused
244
245 OFFSET TABLE
246 list of ints, byte size of which is given in trailer
247 -- these are the byte offsets into the file
248 -- number of these is in the trailer
249
250 TRAILER
251 byte size of offset ints in offset table
252 byte size of object refs in arrays and dicts
253 number of offsets in offset table (also is number of objects)
254 element # in offset table which is top level object
255 offset table offset
256
257 */
258
259
260 static CFTypeID stringtype = -1, datatype = -1, numbertype = -1, datetype = -1;
261 static CFTypeID booltype = -1, nulltype = -1, dicttype = -1, arraytype = -1, settype = -1;
262
263 static void initStatics() {
264 if ((CFTypeID)-1 == stringtype) {
265 stringtype = CFStringGetTypeID();
266 }
267 if ((CFTypeID)-1 == datatype) {
268 datatype = CFDataGetTypeID();
269 }
270 if ((CFTypeID)-1 == numbertype) {
271 numbertype = CFNumberGetTypeID();
272 }
273 if ((CFTypeID)-1 == booltype) {
274 booltype = CFBooleanGetTypeID();
275 }
276 if ((CFTypeID)-1 == datetype) {
277 datetype = CFDateGetTypeID();
278 }
279 if ((CFTypeID)-1 == dicttype) {
280 dicttype = CFDictionaryGetTypeID();
281 }
282 if ((CFTypeID)-1 == arraytype) {
283 arraytype = CFArrayGetTypeID();
284 }
285 if ((CFTypeID)-1 == settype) {
286 settype = CFSetGetTypeID();
287 }
288 if ((CFTypeID)-1 == nulltype) {
289 nulltype = CFNullGetTypeID();
290 }
291 }
292
293 static void _appendInt(__CFBinaryPlistWriteBuffer *buf, uint64_t bigint) {
294 uint8_t marker;
295 uint8_t *bytes;
296 CFIndex nbytes;
297 if (bigint <= (uint64_t)0xff) {
298 nbytes = 1;
299 marker = kCFBinaryPlistMarkerInt | 0;
300 } else if (bigint <= (uint64_t)0xffff) {
301 nbytes = 2;
302 marker = kCFBinaryPlistMarkerInt | 1;
303 } else if (bigint <= (uint64_t)0xffffffff) {
304 nbytes = 4;
305 marker = kCFBinaryPlistMarkerInt | 2;
306 } else {
307 nbytes = 8;
308 marker = kCFBinaryPlistMarkerInt | 3;
309 }
310 bigint = CFSwapInt64HostToBig(bigint);
311 bytes = (uint8_t *)&bigint + sizeof(bigint) - nbytes;
312 bufferWrite(buf, &marker, 1);
313 bufferWrite(buf, bytes, nbytes);
314 }
315
316 static void _appendUID(__CFBinaryPlistWriteBuffer *buf, CFKeyedArchiverUIDRef uid) {
317 uint8_t marker;
318 uint8_t *bytes;
319 CFIndex nbytes;
320 uint64_t bigint = _CFKeyedArchiverUIDGetValue(uid);
321 if (bigint <= (uint64_t)0xff) {
322 nbytes = 1;
323 } else if (bigint <= (uint64_t)0xffff) {
324 nbytes = 2;
325 } else if (bigint <= (uint64_t)0xffffffff) {
326 nbytes = 4;
327 } else {
328 nbytes = 8;
329 }
330 marker = kCFBinaryPlistMarkerUID | (uint8_t)(nbytes - 1);
331 bigint = CFSwapInt64HostToBig(bigint);
332 bytes = (uint8_t *)&bigint + sizeof(bigint) - nbytes;
333 bufferWrite(buf, &marker, 1);
334 bufferWrite(buf, bytes, nbytes);
335 }
336
337 static void _flattenPlist(CFPropertyListRef plist, CFMutableArrayRef objlist, CFMutableDictionaryRef objtable, CFMutableSetRef uniquingset) {
338 CFPropertyListRef unique;
339 uint32_t refnum;
340 CFTypeID type = CFGetTypeID(plist);
341 CFIndex idx;
342 CFPropertyListRef *list, buffer[256];
343
344 // Do not unique dictionaries or arrays, because: they
345 // are slow to compare, and have poor hash codes.
346 // Uniquing bools is unnecessary.
347 if (stringtype == type || numbertype == type || datetype == type || datatype == type) {
348 CFIndex before = CFSetGetCount(uniquingset);
349 CFSetAddValue(uniquingset, plist);
350 CFIndex after = CFSetGetCount(uniquingset);
351 if (after == before) { // already in set
352 unique = CFSetGetValue(uniquingset, plist);
353 if (unique != plist) {
354 refnum = (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable, unique);
355 CFDictionaryAddValue(objtable, plist, (const void *)(uintptr_t)refnum);
356 }
357 return;
358 }
359 }
360 refnum = CFArrayGetCount(objlist);
361 CFArrayAppendValue(objlist, plist);
362 CFDictionaryAddValue(objtable, plist, (const void *)(uintptr_t)refnum);
363 if (dicttype == type) {
364 CFIndex count = CFDictionaryGetCount((CFDictionaryRef)plist);
365 list = (count <= 128) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, 2 * count * sizeof(CFTypeRef), __kCFAllocatorGCScannedMemory);
366 CFDictionaryGetKeysAndValues((CFDictionaryRef)plist, list, list + count);
367 for (idx = 0; idx < 2 * count; idx++) {
368 _flattenPlist(list[idx], objlist, objtable, uniquingset);
369 }
370 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, list);
371 } else if (arraytype == type) {
372 CFIndex count = CFArrayGetCount((CFArrayRef)plist);
373 list = (count <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, count * sizeof(CFTypeRef), __kCFAllocatorGCScannedMemory);
374 CFArrayGetValues((CFArrayRef)plist, CFRangeMake(0, count), list);
375 for (idx = 0; idx < count; idx++) {
376 _flattenPlist(list[idx], objlist, objtable, uniquingset);
377 }
378 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, list);
379 }
380 }
381
382 /* Get the number of bytes required to hold the value in 'count'. Will return a power of 2 value big enough to hold 'count'.
383 */
384 CF_INLINE uint8_t _byteCount(uint64_t count) {
385 uint64_t mask = ~(uint64_t)0;
386 uint8_t size = 0;
387
388 // Find something big enough to hold 'count'
389 while (count & mask) {
390 size++;
391 mask = mask << 8;
392 }
393
394 // Ensure that 'count' is a power of 2
395 // For sizes bigger than 8, just use the required count
396 while ((size != 1 && size != 2 && size != 4 && size != 8) && size <= 8) {
397 size++;
398 }
399
400 return size;
401 }
402
403
404 // stream can be a CFWriteStreamRef (on supported platforms) or a CFMutableDataRef
405 /* Write a property list to a stream, in binary format. plist is the property list to write (one of the basic property list types), stream is the destination of the property list, and estimate is a best-guess at the total number of objects in the property list. The estimate parameter is for efficiency in pre-allocating memory for the uniquing step. Pass in a 0 if no estimate is available. The options flag specifies sort options. If the error parameter is non-NULL and an error occurs, it will be used to return a CFError explaining the problem. It is the callers responsibility to release the error. */
406 CFIndex __CFBinaryPlistWrite(CFPropertyListRef plist, CFTypeRef stream, uint64_t estimate, CFOptionFlags options, CFErrorRef *error) {
407 CFMutableDictionaryRef objtable;
408 CFMutableArrayRef objlist;
409 CFMutableSetRef uniquingset;
410 CFBinaryPlistTrailer trailer;
411 uint64_t *offsets, length_so_far;
412 uint64_t refnum;
413 int64_t idx, idx2, cnt;
414 __CFBinaryPlistWriteBuffer *buf;
415
416 initStatics();
417
418 const CFDictionaryKeyCallBacks dictKeyCallbacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, 0, 0, 0};
419 objtable = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &dictKeyCallbacks, NULL);
420
421 const CFArrayCallBacks arrayCallbacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, 0, 0};
422 objlist = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &arrayCallbacks);
423
424
425 const CFSetCallBacks setCallbacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, 0, 0, 0};
426 uniquingset = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &setCallbacks);
427
428 #if DEPLOYMENT_TARGET_MACOSX
429 _CFDictionarySetCapacity(objtable, estimate ? estimate : 650);
430 _CFArraySetCapacity(objlist, estimate ? estimate : 650);
431 _CFSetSetCapacity(uniquingset, estimate ? estimate : 1000);
432 #endif
433
434 _flattenPlist(plist, objlist, objtable, uniquingset);
435
436 CFRelease(uniquingset);
437
438 cnt = CFArrayGetCount(objlist);
439 offsets = (uint64_t *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, (CFIndex)(cnt * sizeof(*offsets)), 0);
440
441 buf = (__CFBinaryPlistWriteBuffer *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, sizeof(__CFBinaryPlistWriteBuffer), 0);
442 buf->stream = stream;
443 buf->error = NULL;
444 buf->streamIsData = (CFGetTypeID(stream) == CFDataGetTypeID());
445 buf->written = 0;
446 buf->used = 0;
447 bufferWrite(buf, (uint8_t *)"bplist00", 8); // header
448
449 memset(&trailer, 0, sizeof(trailer));
450 trailer._numObjects = CFSwapInt64HostToBig(cnt);
451 trailer._topObject = 0; // true for this implementation
452 trailer._objectRefSize = _byteCount(cnt);
453 for (idx = 0; idx < cnt; idx++) {
454 CFPropertyListRef obj = CFArrayGetValueAtIndex(objlist, (CFIndex)idx);
455 CFTypeID type = CFGetTypeID(obj);
456 offsets[idx] = buf->written + buf->used;
457 if (stringtype == type) {
458 CFIndex ret, count = CFStringGetLength((CFStringRef)obj);
459 CFIndex needed;
460 uint8_t *bytes, buffer[1024];
461 bytes = (count <= 1024) ? buffer : (uint8_t *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, count, 0);
462 // presumption, believed to be true, is that ASCII encoding may need
463 // less bytes, but will not need greater, than the # of unichars
464 ret = CFStringGetBytes((CFStringRef)obj, CFRangeMake(0, count), kCFStringEncodingASCII, 0, false, bytes, count, &needed);
465 if (ret == count) {
466 uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerASCIIString | (needed < 15 ? needed : 0xf));
467 bufferWrite(buf, &marker, 1);
468 if (15 <= needed) {
469 _appendInt(buf, (uint64_t)needed);
470 }
471 bufferWrite(buf, bytes, needed);
472 } else {
473 UniChar *chars;
474 uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerUnicode16String | (count < 15 ? count : 0xf));
475 bufferWrite(buf, &marker, 1);
476 if (15 <= count) {
477 _appendInt(buf, (uint64_t)count);
478 }
479 chars = (UniChar *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, count * sizeof(UniChar), 0);
480 CFStringGetCharacters((CFStringRef)obj, CFRangeMake(0, count), chars);
481 for (idx2 = 0; idx2 < count; idx2++) {
482 chars[idx2] = CFSwapInt16HostToBig(chars[idx2]);
483 }
484 bufferWrite(buf, (uint8_t *)chars, count * sizeof(UniChar));
485 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, chars);
486 }
487 if (bytes != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, bytes);
488 } else if (numbertype == type) {
489 uint8_t marker;
490 uint64_t bigint;
491 uint8_t *bytes;
492 CFIndex nbytes;
493 if (CFNumberIsFloatType((CFNumberRef)obj)) {
494 CFSwappedFloat64 swapped64;
495 CFSwappedFloat32 swapped32;
496 if (CFNumberGetByteSize((CFNumberRef)obj) <= (CFIndex)sizeof(float)) {
497 float v;
498 CFNumberGetValue((CFNumberRef)obj, kCFNumberFloat32Type, &v);
499 swapped32 = CFConvertFloat32HostToSwapped(v);
500 bytes = (uint8_t *)&swapped32;
501 nbytes = sizeof(float);
502 marker = kCFBinaryPlistMarkerReal | 2;
503 } else {
504 double v;
505 CFNumberGetValue((CFNumberRef)obj, kCFNumberFloat64Type, &v);
506 swapped64 = CFConvertFloat64HostToSwapped(v);
507 bytes = (uint8_t *)&swapped64;
508 nbytes = sizeof(double);
509 marker = kCFBinaryPlistMarkerReal | 3;
510 }
511 bufferWrite(buf, &marker, 1);
512 bufferWrite(buf, bytes, nbytes);
513 } else {
514 CFNumberType type = _CFNumberGetType2((CFNumberRef)obj);
515 if (kCFNumberSInt128Type == type) {
516 CFSInt128Struct s;
517 CFNumberGetValue((CFNumberRef)obj, kCFNumberSInt128Type, &s);
518 struct {
519 int64_t high;
520 uint64_t low;
521 } storage;
522 storage.high = CFSwapInt64HostToBig(s.high);
523 storage.low = CFSwapInt64HostToBig(s.low);
524 uint8_t *bytes = (uint8_t *)&storage;
525 uint8_t marker = kCFBinaryPlistMarkerInt | 4;
526 CFIndex nbytes = 16;
527 bufferWrite(buf, &marker, 1);
528 bufferWrite(buf, bytes, nbytes);
529 } else {
530 CFNumberGetValue((CFNumberRef)obj, kCFNumberSInt64Type, &bigint);
531 _appendInt(buf, bigint);
532 }
533 }
534 } else if (_CFKeyedArchiverUIDGetTypeID() == type) {
535 _appendUID(buf, (CFKeyedArchiverUIDRef)obj);
536 } else if (booltype == type) {
537 uint8_t marker = CFBooleanGetValue((CFBooleanRef)obj) ? kCFBinaryPlistMarkerTrue : kCFBinaryPlistMarkerFalse;
538 bufferWrite(buf, &marker, 1);
539 } else if (datatype == type) {
540 CFIndex count = CFDataGetLength((CFDataRef)obj);
541 uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerData | (count < 15 ? count : 0xf));
542 bufferWrite(buf, &marker, 1);
543 if (15 <= count) {
544 _appendInt(buf, (uint64_t)count);
545 }
546 bufferWrite(buf, CFDataGetBytePtr((CFDataRef)obj), count);
547 } else if (datetype == type) {
548 CFSwappedFloat64 swapped;
549 uint8_t marker = kCFBinaryPlistMarkerDate;
550 bufferWrite(buf, &marker, 1);
551 swapped = CFConvertFloat64HostToSwapped(CFDateGetAbsoluteTime((CFDateRef)obj));
552 bufferWrite(buf, (uint8_t *)&swapped, sizeof(swapped));
553 } else if (dicttype == type) {
554 CFIndex count = CFDictionaryGetCount((CFDictionaryRef)obj);
555
556 uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerDict | (count < 15 ? count : 0xf));
557 bufferWrite(buf, &marker, 1);
558 if (15 <= count) {
559 _appendInt(buf, (uint64_t)count);
560 }
561
562 CFPropertyListRef *list, buffer[512];
563
564 list = (count <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, 2 * count * sizeof(CFTypeRef), __kCFAllocatorGCScannedMemory);
565 CFDictionaryGetKeysAndValues((CFDictionaryRef)obj, list, list + count);
566 for (idx2 = 0; idx2 < 2 * count; idx2++) {
567 CFPropertyListRef value = list[idx2];
568 uint32_t swapped = 0;
569 uint8_t *source = (uint8_t *)&swapped;
570 refnum = (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable, value);
571 swapped = CFSwapInt32HostToBig((uint32_t)refnum);
572 bufferWrite(buf, source + sizeof(swapped) - trailer._objectRefSize, trailer._objectRefSize);
573 }
574 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, list);
575 } else if (arraytype == type) {
576 CFIndex count = CFArrayGetCount((CFArrayRef)obj);
577 CFPropertyListRef *list, buffer[256];
578 uint8_t marker = (uint8_t)(kCFBinaryPlistMarkerArray | (count < 15 ? count : 0xf));
579 bufferWrite(buf, &marker, 1);
580 if (15 <= count) {
581 _appendInt(buf, (uint64_t)count);
582 }
583 list = (count <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, count * sizeof(CFTypeRef), __kCFAllocatorGCScannedMemory);
584 CFArrayGetValues((CFArrayRef)obj, CFRangeMake(0, count), list);
585 for (idx2 = 0; idx2 < count; idx2++) {
586 CFPropertyListRef value = list[idx2];
587 uint32_t swapped = 0;
588 uint8_t *source = (uint8_t *)&swapped;
589 refnum = (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable, value);
590 swapped = CFSwapInt32HostToBig((uint32_t)refnum);
591 bufferWrite(buf, source + sizeof(swapped) - trailer._objectRefSize, trailer._objectRefSize);
592 }
593 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, list);
594 } else {
595 CFRelease(objtable);
596 CFRelease(objlist);
597 if (error && buf->error) {
598 // caller will release error
599 *error = buf->error;
600 } else if (buf->error) {
601 // caller is not interested in error, release it here
602 CFRelease(buf->error);
603 }
604 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, buf);
605 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, offsets);
606 return 0;
607 }
608 }
609 CFRelease(objtable);
610 CFRelease(objlist);
611
612 length_so_far = buf->written + buf->used;
613 trailer._offsetTableOffset = CFSwapInt64HostToBig(length_so_far);
614 trailer._offsetIntSize = _byteCount(length_so_far);
615
616 for (idx = 0; idx < cnt; idx++) {
617 uint64_t swapped = CFSwapInt64HostToBig(offsets[idx]);
618 uint8_t *source = (uint8_t *)&swapped;
619 bufferWrite(buf, source + sizeof(*offsets) - trailer._offsetIntSize, trailer._offsetIntSize);
620 }
621 length_so_far += cnt * trailer._offsetIntSize;
622 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, offsets);
623
624 bufferWrite(buf, (uint8_t *)&trailer, sizeof(trailer));
625 bufferFlush(buf);
626 length_so_far += sizeof(trailer);
627 if (buf->error) {
628 if (error) {
629 // caller will release error
630 *error = buf->error;
631 } else {
632 CFRelease(buf->error);
633 }
634 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, buf);
635 return 0;
636 }
637 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, buf);
638 return (CFIndex)length_so_far;
639 }
640
641
642 CFIndex __CFBinaryPlistWriteToStream(CFPropertyListRef plist, CFTypeRef stream) {
643 return __CFBinaryPlistWrite(plist, stream, 0, 0, NULL);
644 }
645
646 // to be removed soon
647 CFIndex __CFBinaryPlistWriteToStreamWithEstimate(CFPropertyListRef plist, CFTypeRef stream, uint64_t estimate) {
648 return __CFBinaryPlistWrite(plist, stream, estimate, 0, NULL);
649 }
650
651 // to be removed soon
652 CFIndex __CFBinaryPlistWriteToStreamWithOptions(CFPropertyListRef plist, CFTypeRef stream, uint64_t estimate, CFOptionFlags options) {
653 return __CFBinaryPlistWrite(plist, stream, estimate, options, NULL);
654 }
655
656 #define FAIL_FALSE do { return false; } while (0)
657 #define FAIL_MAXOFFSET do { return UINT64_MAX; } while (0)
658
659 /* Grab a valSize-bytes integer out of the buffer pointed at by data and return it.
660 */
661 CF_INLINE uint64_t _getSizedInt(const uint8_t *data, uint8_t valSize) {
662 #if defined(__i386__) || defined(__x86_64__)
663 if (valSize == 1) {
664 return (uint64_t)*data;
665 } else if (valSize == 2) {
666 uint16_t val = *(uint16_t *)data;
667 return (uint64_t)CFSwapInt16BigToHost(val);
668 } else if (valSize == 4) {
669 uint32_t val = *(uint32_t *)data;
670 return (uint64_t)CFSwapInt32BigToHost(val);
671 } else if (valSize == 8) {
672 uint64_t val = *(uint64_t *)data;
673 return CFSwapInt64BigToHost(val);
674 }
675 #endif
676 // Compatability with existing archives, including anything with a non-power-of-2
677 // size and 16-byte values, and architectures that don't support unaligned access
678 uint64_t res = 0;
679 for (CFIndex idx = 0; idx < valSize; idx++) {
680 res = (res << 8) + data[idx];
681 }
682 return res;
683 }
684
685 bool __CFBinaryPlistGetTopLevelInfo(const uint8_t *databytes, uint64_t datalen, uint8_t *marker, uint64_t *offset, CFBinaryPlistTrailer *trailer) {
686 CFBinaryPlistTrailer trail;
687
688 initStatics();
689
690 if (!databytes || datalen < sizeof(trail) + 8 + 1) FAIL_FALSE;
691 // Tiger and earlier will parse "bplist00"
692 // Leopard will parse "bplist00" or "bplist01"
693 // SnowLeopard will parse "bplist0?" where ? is any one character
694 if (0 != memcmp("bplist0", databytes, 7)) {
695 return false;
696 }
697 memmove(&trail, databytes + datalen - sizeof(trail), sizeof(trail));
698 // In Leopard, the unused bytes in the trailer must be 0 or the parse will fail
699 // This check is not present in Tiger and earlier or after Leopard
700 trail._numObjects = CFSwapInt64BigToHost(trail._numObjects);
701 trail._topObject = CFSwapInt64BigToHost(trail._topObject);
702 trail._offsetTableOffset = CFSwapInt64BigToHost(trail._offsetTableOffset);
703
704 // Don't overflow on the number of objects or offset of the table
705 if (LONG_MAX < trail._numObjects) FAIL_FALSE;
706 if (LONG_MAX < trail._offsetTableOffset) FAIL_FALSE;
707
708 // Must be a minimum of 1 object
709 if (trail._numObjects < 1) FAIL_FALSE;
710
711 // The ref to the top object must be a value in the range of 1 to the total number of objects
712 if (trail._numObjects <= trail._topObject) FAIL_FALSE;
713
714 // The offset table must be after at least 9 bytes of other data ('bplist??' + 1 byte of object table data).
715 if (trail._offsetTableOffset < 9) FAIL_FALSE;
716
717 // The trailer must point to a value before itself in the data.
718 if (datalen - sizeof(trail) <= trail._offsetTableOffset) FAIL_FALSE;
719
720 // Minimum of 1 byte for the size of integers and references in the data
721 if (trail._offsetIntSize < 1) FAIL_FALSE;
722 if (trail._objectRefSize < 1) FAIL_FALSE;
723
724 int32_t err = CF_NO_ERROR;
725
726 // The total size of the offset table (number of objects * size of each int in the table) must not overflow
727 uint64_t offsetIntSize = trail._offsetIntSize;
728 uint64_t offsetTableSize = __check_uint64_mul_unsigned_unsigned(trail._numObjects, offsetIntSize, &err);
729 if (CF_NO_ERROR!= err) FAIL_FALSE;
730
731 // The offset table must have at least 1 entry
732 if (offsetTableSize < 1) FAIL_FALSE;
733
734 // Make sure the size of the offset table and data sections do not overflow
735 uint64_t objectDataSize = trail._offsetTableOffset - 8;
736 uint64_t tmpSum = __check_uint64_add_unsigned_unsigned(8, objectDataSize, &err);
737 tmpSum = __check_uint64_add_unsigned_unsigned(tmpSum, offsetTableSize, &err);
738 tmpSum = __check_uint64_add_unsigned_unsigned(tmpSum, sizeof(trail), &err);
739 if (CF_NO_ERROR != err) FAIL_FALSE;
740
741 // The total size of the data should be equal to the sum of offsetTableOffset + sizeof(trailer)
742 if (datalen != tmpSum) FAIL_FALSE;
743
744 // The object refs must be the right size to point into the offset table. That is, if the count of objects is 260, but only 1 byte is used to store references (max value 255), something is wrong.
745 if (trail._objectRefSize < 8 && (1ULL << (8 * trail._objectRefSize)) <= trail._numObjects) FAIL_FALSE;
746
747 // The integers used for pointers in the offset table must be able to reach as far as the start of the offset table.
748 if (trail._offsetIntSize < 8 && (1ULL << (8 * trail._offsetIntSize)) <= trail._offsetTableOffset) FAIL_FALSE;
749
750
751 const uint8_t *objectsFirstByte;
752 objectsFirstByte = check_ptr_add(databytes, 8, &err);
753 if (CF_NO_ERROR != err) FAIL_FALSE;
754 const uint8_t *offsetsFirstByte = check_ptr_add(databytes, trail._offsetTableOffset, &err);
755 if (CF_NO_ERROR != err) FAIL_FALSE;
756 const uint8_t *offsetsLastByte;
757 offsetsLastByte = check_ptr_add(offsetsFirstByte, offsetTableSize - 1, &err);
758 if (CF_NO_ERROR != err) FAIL_FALSE;
759
760 const uint8_t *bytesptr = databytes + trail._offsetTableOffset;
761 uint64_t maxOffset = trail._offsetTableOffset - 1;
762 for (CFIndex idx = 0; idx < trail._numObjects; idx++) {
763 uint64_t off = _getSizedInt(bytesptr, trail._offsetIntSize);
764 if (maxOffset < off) FAIL_FALSE;
765 bytesptr += trail._offsetIntSize;
766 }
767
768 bytesptr = databytes + trail._offsetTableOffset + trail._topObject * trail._offsetIntSize;
769 uint64_t off = _getSizedInt(bytesptr, trail._offsetIntSize);
770 if (off < 8 || trail._offsetTableOffset <= off) FAIL_FALSE;
771 if (trailer) *trailer = trail;
772 if (offset) *offset = off;
773 if (marker) *marker = *(databytes + off);
774 return true;
775 }
776
777 CF_INLINE Boolean _plistIsPrimitive(CFPropertyListRef pl) {
778 CFTypeID type = CFGetTypeID(pl);
779 if (dicttype == type || arraytype == type || settype == type) FAIL_FALSE;
780 return true;
781 }
782
783 CF_INLINE bool _readInt(const uint8_t *ptr, const uint8_t *end_byte_ptr, uint64_t *bigint, const uint8_t **newptr) {
784 if (end_byte_ptr < ptr) FAIL_FALSE;
785 uint8_t marker = *ptr++;
786 if ((marker & 0xf0) != kCFBinaryPlistMarkerInt) FAIL_FALSE;
787 uint64_t cnt = 1 << (marker & 0x0f);
788 int32_t err = CF_NO_ERROR;
789 const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1;
790 if (CF_NO_ERROR != err) FAIL_FALSE;
791 if (end_byte_ptr < extent) FAIL_FALSE;
792 // integers are not required to be in the most compact possible representation, but only the last 64 bits are significant currently
793 *bigint = _getSizedInt(ptr, cnt);
794 ptr += cnt;
795 if (newptr) *newptr = ptr;
796 return true;
797 }
798
799 // bytesptr points at a ref
800 CF_INLINE uint64_t _getOffsetOfRefAt(const uint8_t *databytes, const uint8_t *bytesptr, const CFBinaryPlistTrailer *trailer) {
801 // *trailer contents are trusted, even for overflows -- was checked when the trailer was parsed;
802 // this pointer arithmetic and the multiplication was also already done once and checked,
803 // and the offsetTable was already validated.
804 const uint8_t *objectsFirstByte = databytes + 8;
805 const uint8_t *offsetsFirstByte = databytes + trailer->_offsetTableOffset;
806 if (bytesptr < objectsFirstByte || offsetsFirstByte - trailer->_objectRefSize < bytesptr) FAIL_MAXOFFSET;
807
808 uint64_t ref = _getSizedInt(bytesptr, trailer->_objectRefSize);
809 if (trailer->_numObjects <= ref) FAIL_MAXOFFSET;
810
811 bytesptr = databytes + trailer->_offsetTableOffset + ref * trailer->_offsetIntSize;
812 uint64_t off = _getSizedInt(bytesptr, trailer->_offsetIntSize);
813 return off;
814 }
815
816 bool __CFBinaryPlistGetOffsetForValueFromArray2(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFIndex idx, uint64_t *offset, CFMutableDictionaryRef objects) {
817 uint64_t objectsRangeStart = 8, objectsRangeEnd = trailer->_offsetTableOffset - 1;
818 if (startOffset < objectsRangeStart || objectsRangeEnd < startOffset) FAIL_FALSE;
819 const uint8_t *ptr = databytes + startOffset;
820 uint8_t marker = *ptr;
821 if ((marker & 0xf0) != kCFBinaryPlistMarkerArray) FAIL_FALSE;
822 int32_t err = CF_NO_ERROR;
823 ptr = check_ptr_add(ptr, 1, &err);
824 if (CF_NO_ERROR != err) FAIL_FALSE;
825 uint64_t cnt = (marker & 0x0f);
826 if (0xf == cnt) {
827 uint64_t bigint;
828 if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE;
829 if (LONG_MAX < bigint) FAIL_FALSE;
830 cnt = bigint;
831 }
832 if (cnt <= idx) FAIL_FALSE;
833 size_t byte_cnt = check_size_t_mul(cnt, trailer->_objectRefSize, &err);
834 if (CF_NO_ERROR != err) FAIL_FALSE;
835 const uint8_t *extent = check_ptr_add(ptr, byte_cnt, &err) - 1;
836 if (CF_NO_ERROR != err) FAIL_FALSE;
837 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
838 uint64_t off = _getOffsetOfRefAt(databytes, ptr + idx * trailer->_objectRefSize, trailer);
839 if (offset) *offset = off;
840 return true;
841 }
842
843 // Compatibility method, to be removed soon
844 CF_EXPORT bool __CFBinaryPlistGetOffsetForValueFromDictionary2(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFTypeRef key, uint64_t *koffset, uint64_t *voffset, CFMutableDictionaryRef objects) {
845 return __CFBinaryPlistGetOffsetForValueFromDictionary3(databytes, datalen, startOffset, trailer, key, koffset, voffset, false, objects);
846 }
847
848 /* Get the offset for a value in a dictionary in a binary property list.
849 @param databytes A pointer to the start of the binary property list data.
850 @param datalen The length of the data.
851 @param startOffset The offset at which the dictionary starts.
852 @param trailer A pointer to a filled out trailer structure (use __CFBinaryPlistGetTopLevelInfo).
853 @param key A string key in the dictionary that should be searched for.
854 @param koffset Will be filled out with the offset to the key in the data bytes.
855 @param voffset Will be filled out with the offset to the value in the data bytes.
856 @param unused Unused parameter.
857 @param objects Used for caching objects. Should be a valid CFMutableDictionaryRef.
858 @return True if the key was found, false otherwise.
859 */
860 bool __CFBinaryPlistGetOffsetForValueFromDictionary3(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFTypeRef key, uint64_t *koffset, uint64_t *voffset, Boolean unused, CFMutableDictionaryRef objects) {
861
862 // Require a key that is a plist primitive
863 if (!key || !_plistIsPrimitive(key)) FAIL_FALSE;
864
865 // Require that startOffset is in the range of the object table
866 uint64_t objectsRangeStart = 8, objectsRangeEnd = trailer->_offsetTableOffset - 1;
867 if (startOffset < objectsRangeStart || objectsRangeEnd < startOffset) FAIL_FALSE;
868
869 // ptr is the start of the dictionary we are reading
870 const uint8_t *ptr = databytes + startOffset;
871
872 // Check that the data pointer actually points to a dictionary
873 uint8_t marker = *ptr;
874 if ((marker & 0xf0) != kCFBinaryPlistMarkerDict) FAIL_FALSE;
875
876 // Get the number of objects in this dictionary
877 int32_t err = CF_NO_ERROR;
878 ptr = check_ptr_add(ptr, 1, &err);
879 if (CF_NO_ERROR != err) FAIL_FALSE;
880 uint64_t cnt = (marker & 0x0f);
881 if (0xf == cnt) {
882 uint64_t bigint = 0;
883 if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE;
884 if (LONG_MAX < bigint) FAIL_FALSE;
885 cnt = bigint;
886 }
887
888 // Total number of objects (keys + values) is cnt * 2
889 cnt = check_size_t_mul(cnt, 2, &err);
890 if (CF_NO_ERROR != err) FAIL_FALSE;
891 size_t byte_cnt = check_size_t_mul(cnt, trailer->_objectRefSize, &err);
892 if (CF_NO_ERROR != err) FAIL_FALSE;
893
894 // Find the end of the dictionary
895 const uint8_t *extent = check_ptr_add(ptr, byte_cnt, &err) - 1;
896 if (CF_NO_ERROR != err) FAIL_FALSE;
897
898 // Check that we didn't overflow the size of the dictionary
899 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
900
901 // For short keys (15 bytes or less) in ASCII form, we can do a quick comparison check
902 // We get the pointer or copy the buffer here, outside of the loop
903 CFIndex stringKeyLen = -1;
904 if (CFGetTypeID(key) == stringtype) {
905 stringKeyLen = CFStringGetLength((CFStringRef)key);
906 }
907
908 // Find the object in the dictionary with this key
909 cnt = cnt / 2;
910 uint64_t totalKeySize = cnt * trailer->_objectRefSize;
911 uint64_t off;
912 Boolean match = false;
913 CFPropertyListRef keyInData = NULL;
914
915 #define KEY_BUFF_SIZE 16
916 char keyBuffer[KEY_BUFF_SIZE];
917 const char *keyBufferPtr = NULL;
918
919 // If we have a string for the key, then we will grab the ASCII encoded version of it, if possible, and do a memcmp on it
920 if (stringKeyLen != -1) {
921 // Since we will only be comparing ASCII strings, we can attempt to get a pointer using MacRoman encoding
922 // (this is cheaper than a copy)
923 if (!(keyBufferPtr = CFStringGetCStringPtr((CFStringRef)key, kCFStringEncodingMacRoman)) && stringKeyLen < KEY_BUFF_SIZE) {
924 CFStringGetCString((CFStringRef)key, keyBuffer, KEY_BUFF_SIZE, kCFStringEncodingMacRoman);
925 // The pointer should now point to our keyBuffer instead of the original string buffer, since we've copied it
926 keyBufferPtr = keyBuffer;
927 }
928 }
929
930 // Perform linear search of the keys
931 for (CFIndex idx = 0; idx < cnt; idx++) {
932 off = _getOffsetOfRefAt(databytes, ptr, trailer);
933 marker = *(databytes + off);
934 // if it is an ASCII string in the data, then we do a memcmp. If the key isn't ASCII, then it won't pass the compare, unless it hits some odd edge case of the ASCII string actually containing the unicode escape sequence.
935 if (keyBufferPtr && (marker & 0xf0) == kCFBinaryPlistMarkerASCIIString) {
936 CFIndex len = marker & 0x0f;
937 // move past the marker
938 const uint8_t *ptr2 = databytes + off;
939 err = CF_NO_ERROR;
940 ptr2 = check_ptr_add(ptr2, 1, &err);
941 if (CF_NO_ERROR != err) FAIL_FALSE;
942
943 // If the key's length is large, and the length we are querying is also large, then we have to read it in. If stringKeyLen is less than 0xf, then len will never be equal to it if it was encoded as large.
944 if (0xf == len && stringKeyLen >= 0xf) {
945 uint64_t bigint = 0;
946 if (!_readInt(ptr2, databytes + objectsRangeEnd, &bigint, &ptr2)) FAIL_FALSE;
947 if (LONG_MAX < bigint) FAIL_FALSE;
948 len = (CFIndex)bigint;
949 }
950
951 if (len == stringKeyLen) {
952 err = CF_NO_ERROR;
953 extent = check_ptr_add(ptr2, len, &err);
954 if (CF_NO_ERROR != err) FAIL_FALSE;
955
956 if (databytes + trailer->_offsetTableOffset <= extent) FAIL_FALSE;
957
958 // Compare the key to this potential match
959 if (memcmp(ptr2, keyBufferPtr, stringKeyLen) == 0) {
960 match = true;
961 }
962 }
963 } else {
964 // temp object not saved in 'objects', because we don't know what allocator to use
965 // (what allocator __CFBinaryPlistCreateObject2() or __CFBinaryPlistCreateObject()
966 // will eventually be called with which results in that object)
967 keyInData = NULL;
968 if (!__CFBinaryPlistCreateObject2(databytes, datalen, off, trailer, kCFAllocatorSystemDefault, kCFPropertyListImmutable, NULL /*objects*/, NULL, 0, &keyInData) || !_plistIsPrimitive(keyInData)) {
969 if (keyInData) CFRelease(keyInData);
970 return false;
971 }
972
973 match = CFEqual(key, keyInData);
974 CFRelease(keyInData);
975 }
976
977 if (match) {
978 if (koffset) *koffset = off;
979 if (voffset) *voffset = _getOffsetOfRefAt(databytes, ptr + totalKeySize, trailer);
980 return true;
981 }
982
983 ptr += trailer->_objectRefSize;
984 }
985
986 return false;
987 }
988
989 extern CFDictionaryRef __CFDictionaryCreateTransfer(CFAllocatorRef allocator, const void * *klist, const void * *vlist, CFIndex numValues);
990 extern CFSetRef __CFSetCreateTransfer(CFAllocatorRef allocator, const void * *klist, CFIndex numValues);
991 extern CFArrayRef __CFArrayCreateTransfer(CFAllocatorRef allocator, const void * *klist, CFIndex numValues);
992
993 CF_EXPORT bool __CFBinaryPlistCreateObject2(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFAllocatorRef allocator, CFOptionFlags mutabilityOption, CFMutableDictionaryRef objects, CFMutableSetRef set, CFIndex curDepth, CFPropertyListRef *plist) {
994
995 if (objects) {
996 *plist = CFDictionaryGetValue(objects, (const void *)(uintptr_t)startOffset);
997 if (*plist) {
998 // have to assume that '*plist' was previously created with same allocator that is now being passed in
999 if (!_CFAllocatorIsGCRefZero(allocator)) CFRetain(*plist);
1000 return true;
1001 }
1002 }
1003
1004 // at any one invocation of this function, set should contain the offsets in the "path" down to this object
1005 if (set && CFSetContainsValue(set, (const void *)(uintptr_t)startOffset)) return false;
1006
1007 // databytes is trusted to be at least datalen bytes long
1008 // *trailer contents are trusted, even for overflows -- was checked when the trailer was parsed
1009 uint64_t objectsRangeStart = 8, objectsRangeEnd = trailer->_offsetTableOffset - 1;
1010 if (startOffset < objectsRangeStart || objectsRangeEnd < startOffset) FAIL_FALSE;
1011
1012 uint64_t off;
1013 CFPropertyListRef *list, buffer[256];
1014
1015 uint8_t marker = *(databytes + startOffset);
1016 switch (marker & 0xf0) {
1017 case kCFBinaryPlistMarkerNull:
1018 switch (marker) {
1019 case kCFBinaryPlistMarkerNull:
1020 *plist = kCFNull;
1021 return true;
1022 case kCFBinaryPlistMarkerFalse:
1023 *plist = !_CFAllocatorIsGCRefZero(allocator) ? CFRetain(kCFBooleanFalse) : kCFBooleanFalse;
1024 return true;
1025 case kCFBinaryPlistMarkerTrue:
1026 *plist = !_CFAllocatorIsGCRefZero(allocator) ? CFRetain(kCFBooleanTrue) : kCFBooleanTrue;
1027 return true;
1028 }
1029 FAIL_FALSE;
1030 case kCFBinaryPlistMarkerInt:
1031 {
1032 const uint8_t *ptr = (databytes + startOffset);
1033 int32_t err = CF_NO_ERROR;
1034 ptr = check_ptr_add(ptr, 1, &err);
1035 if (CF_NO_ERROR != err) FAIL_FALSE;
1036 uint64_t cnt = 1 << (marker & 0x0f);
1037 const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1;
1038 if (CF_NO_ERROR != err) FAIL_FALSE;
1039 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1040 if (16 < cnt) FAIL_FALSE;
1041 // in format version '00', 1, 2, and 4-byte integers have to be interpreted as unsigned,
1042 // whereas 8-byte integers are signed (and 16-byte when available)
1043 // negative 1, 2, 4-byte integers are always emitted as 8 bytes in format '00'
1044 // integers are not required to be in the most compact possible representation, but only the last 64 bits are significant currently
1045 uint64_t bigint = _getSizedInt(ptr, cnt);
1046 ptr += cnt;
1047 if (8 < cnt) {
1048 CFSInt128Struct val;
1049 val.high = 0;
1050 val.low = bigint;
1051 *plist = CFNumberCreate(allocator, kCFNumberSInt128Type, &val);
1052 } else {
1053 *plist = CFNumberCreate(allocator, kCFNumberSInt64Type, &bigint);
1054 }
1055 // these are always immutable
1056 if (objects && *plist) {
1057 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1058 }
1059 return (*plist) ? true : false;
1060 }
1061 case kCFBinaryPlistMarkerReal:
1062 switch (marker & 0x0f) {
1063 case 2: {
1064 const uint8_t *ptr = (databytes + startOffset);
1065 int32_t err = CF_NO_ERROR;
1066 ptr = check_ptr_add(ptr, 1, &err);
1067 if (CF_NO_ERROR != err) FAIL_FALSE;
1068 const uint8_t *extent = check_ptr_add(ptr, 4, &err) - 1;
1069 if (CF_NO_ERROR != err) FAIL_FALSE;
1070 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1071 CFSwappedFloat32 swapped32;
1072 memmove(&swapped32, ptr, 4);
1073 float f = CFConvertFloat32SwappedToHost(swapped32);
1074 *plist = CFNumberCreate(allocator, kCFNumberFloat32Type, &f);
1075 // these are always immutable
1076 if (objects && *plist) {
1077 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1078 }
1079 return (*plist) ? true : false;
1080 }
1081 case 3: {
1082 const uint8_t *ptr = (databytes + startOffset);
1083 int32_t err = CF_NO_ERROR;
1084 ptr = check_ptr_add(ptr, 1, &err);
1085 if (CF_NO_ERROR != err) FAIL_FALSE;
1086 const uint8_t *extent = check_ptr_add(ptr, 8, &err) - 1;
1087 if (CF_NO_ERROR != err) FAIL_FALSE;
1088 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1089 CFSwappedFloat64 swapped64;
1090 memmove(&swapped64, ptr, 8);
1091 double d = CFConvertFloat64SwappedToHost(swapped64);
1092 *plist = CFNumberCreate(allocator, kCFNumberFloat64Type, &d);
1093 // these are always immutable
1094 if (objects && *plist) {
1095 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1096 }
1097 return (*plist) ? true : false;
1098 }
1099 }
1100 FAIL_FALSE;
1101 case kCFBinaryPlistMarkerDate & 0xf0:
1102 switch (marker) {
1103 case kCFBinaryPlistMarkerDate: {
1104 const uint8_t *ptr = (databytes + startOffset);
1105 int32_t err = CF_NO_ERROR;
1106 ptr = check_ptr_add(ptr, 1, &err);
1107 if (CF_NO_ERROR != err) FAIL_FALSE;
1108 const uint8_t *extent = check_ptr_add(ptr, 8, &err) - 1;
1109 if (CF_NO_ERROR != err) FAIL_FALSE;
1110 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1111 CFSwappedFloat64 swapped64;
1112 memmove(&swapped64, ptr, 8);
1113 double d = CFConvertFloat64SwappedToHost(swapped64);
1114 *plist = CFDateCreate(allocator, d);
1115 // these are always immutable
1116 if (objects && *plist) {
1117 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1118 }
1119 return (*plist) ? true : false;
1120 }
1121 }
1122 FAIL_FALSE;
1123 case kCFBinaryPlistMarkerData: {
1124 const uint8_t *ptr = databytes + startOffset;
1125 int32_t err = CF_NO_ERROR;
1126 ptr = check_ptr_add(ptr, 1, &err);
1127 if (CF_NO_ERROR != err) FAIL_FALSE;
1128 CFIndex cnt = marker & 0x0f;
1129 if (0xf == cnt) {
1130 uint64_t bigint = 0;
1131 if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE;
1132 if (LONG_MAX < bigint) FAIL_FALSE;
1133 cnt = (CFIndex)bigint;
1134 }
1135 const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1;
1136 if (CF_NO_ERROR != err) FAIL_FALSE;
1137 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1138 if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) {
1139 *plist = CFDataCreateMutable(allocator, 0);
1140 if (*plist) CFDataAppendBytes((CFMutableDataRef)*plist, ptr, cnt);
1141 } else {
1142 *plist = CFDataCreate(allocator, ptr, cnt);
1143 }
1144 if (objects && *plist && (mutabilityOption != kCFPropertyListMutableContainersAndLeaves)) {
1145 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1146 }
1147 return (*plist) ? true : false;
1148 }
1149 case kCFBinaryPlistMarkerASCIIString: {
1150 const uint8_t *ptr = databytes + startOffset;
1151 int32_t err = CF_NO_ERROR;
1152 ptr = check_ptr_add(ptr, 1, &err);
1153 if (CF_NO_ERROR != err) FAIL_FALSE;
1154 CFIndex cnt = marker & 0x0f;
1155 if (0xf == cnt) {
1156 uint64_t bigint = 0;
1157 if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE;
1158 if (LONG_MAX < bigint) FAIL_FALSE;
1159 cnt = (CFIndex)bigint;
1160 }
1161 const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1;
1162 if (CF_NO_ERROR != err) FAIL_FALSE;
1163 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1164 if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) {
1165 CFStringRef str = CFStringCreateWithBytes(allocator, ptr, cnt, kCFStringEncodingASCII, false);
1166 *plist = str ? CFStringCreateMutableCopy(allocator, 0, str) : NULL;
1167 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(str);
1168 } else {
1169 *plist = CFStringCreateWithBytes(allocator, ptr, cnt, kCFStringEncodingASCII, false);
1170 }
1171 if (objects && *plist && (mutabilityOption != kCFPropertyListMutableContainersAndLeaves)) {
1172 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1173 }
1174 return (*plist) ? true : false;
1175 }
1176 case kCFBinaryPlistMarkerUnicode16String: {
1177 const uint8_t *ptr = databytes + startOffset;
1178 int32_t err = CF_NO_ERROR;
1179 ptr = check_ptr_add(ptr, 1, &err);
1180 if (CF_NO_ERROR != err) FAIL_FALSE;
1181 CFIndex cnt = marker & 0x0f;
1182 if (0xf == cnt) {
1183 uint64_t bigint = 0;
1184 if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE;
1185 if (LONG_MAX < bigint) FAIL_FALSE;
1186 cnt = (CFIndex)bigint;
1187 }
1188 const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1;
1189 extent = check_ptr_add(extent, cnt, &err); // 2 bytes per character
1190 if (CF_NO_ERROR != err) FAIL_FALSE;
1191 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1192 size_t byte_cnt = check_size_t_mul(cnt, sizeof(UniChar), &err);
1193 if (CF_NO_ERROR != err) FAIL_FALSE;
1194 UniChar *chars = (UniChar *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, byte_cnt, 0);
1195 if (!chars) FAIL_FALSE;
1196 memmove(chars, ptr, byte_cnt);
1197 for (CFIndex idx = 0; idx < cnt; idx++) {
1198 chars[idx] = CFSwapInt16BigToHost(chars[idx]);
1199 }
1200 if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) {
1201 CFStringRef str = CFStringCreateWithCharacters(allocator, chars, cnt);
1202 *plist = str ? CFStringCreateMutableCopy(allocator, 0, str) : NULL;
1203 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(str);
1204 } else {
1205 *plist = CFStringCreateWithCharacters(allocator, chars, cnt);
1206 }
1207 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, chars);
1208 if (objects && *plist && (mutabilityOption != kCFPropertyListMutableContainersAndLeaves)) {
1209 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1210 }
1211 return (*plist) ? true : false;
1212 }
1213 case kCFBinaryPlistMarkerUID: {
1214 const uint8_t *ptr = databytes + startOffset;
1215 int32_t err = CF_NO_ERROR;
1216 ptr = check_ptr_add(ptr, 1, &err);
1217 if (CF_NO_ERROR != err) FAIL_FALSE;
1218 CFIndex cnt = (marker & 0x0f) + 1;
1219 const uint8_t *extent = check_ptr_add(ptr, cnt, &err) - 1;
1220 if (CF_NO_ERROR != err) FAIL_FALSE;
1221 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1222 // uids are not required to be in the most compact possible representation, but only the last 64 bits are significant currently
1223 uint64_t bigint = _getSizedInt(ptr, cnt);
1224 ptr += cnt;
1225 if (UINT32_MAX < bigint) FAIL_FALSE;
1226 *plist = _CFKeyedArchiverUIDCreate(allocator, (uint32_t)bigint);
1227 // these are always immutable
1228 if (objects && *plist) {
1229 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1230 }
1231 return (*plist) ? true : false;
1232 }
1233 case kCFBinaryPlistMarkerArray:
1234 case kCFBinaryPlistMarkerSet: {
1235 const uint8_t *ptr = databytes + startOffset;
1236 int32_t err = CF_NO_ERROR;
1237 ptr = check_ptr_add(ptr, 1, &err);
1238 if (CF_NO_ERROR != err) FAIL_FALSE;
1239 CFIndex cnt = marker & 0x0f;
1240 if (0xf == cnt) {
1241 uint64_t bigint = 0;
1242 if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE;
1243 if (LONG_MAX < bigint) FAIL_FALSE;
1244 cnt = (CFIndex)bigint;
1245 }
1246 size_t byte_cnt = check_size_t_mul(cnt, trailer->_objectRefSize, &err);
1247 if (CF_NO_ERROR != err) FAIL_FALSE;
1248 const uint8_t *extent = check_ptr_add(ptr, byte_cnt, &err) - 1;
1249 if (CF_NO_ERROR != err) FAIL_FALSE;
1250 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1251 byte_cnt = check_size_t_mul(cnt, sizeof(CFPropertyListRef), &err);
1252 if (CF_NO_ERROR != err) FAIL_FALSE;
1253 list = (cnt <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, byte_cnt, __kCFAllocatorGCScannedMemory);
1254 if (!list) FAIL_FALSE;
1255 Boolean madeSet = false;
1256 if (!set && 15 < curDepth) {
1257 set = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, NULL);
1258 madeSet = set ? true : false;
1259 }
1260 if (set) CFSetAddValue(set, (const void *)(uintptr_t)startOffset);
1261 for (CFIndex idx = 0; idx < cnt; idx++) {
1262 CFPropertyListRef pl;
1263 off = _getOffsetOfRefAt(databytes, ptr, trailer);
1264 if (!__CFBinaryPlistCreateObject2(databytes, datalen, off, trailer, allocator, mutabilityOption, objects, set, curDepth + 1, &pl)) {
1265 while (idx--) {
1266 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(list[idx]);
1267 }
1268 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, list);
1269 FAIL_FALSE;
1270 }
1271 __CFAssignWithWriteBarrier((void **)list + idx, (void *)pl);
1272 ptr += trailer->_objectRefSize;
1273 }
1274 if (set) CFSetRemoveValue(set, (const void *)(uintptr_t)startOffset);
1275 if (madeSet) {
1276 CFRelease(set);
1277 set = NULL;
1278 }
1279 if ((marker & 0xf0) == kCFBinaryPlistMarkerArray) {
1280 if (mutabilityOption != kCFPropertyListImmutable) {
1281 *plist = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
1282 CFArrayReplaceValues((CFMutableArrayRef)*plist, CFRangeMake(0, 0), list, cnt);
1283 for (CFIndex idx = 0; idx < cnt; idx++) {
1284 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(list[idx]);
1285 }
1286 } else {
1287 if (!kCFUseCollectableAllocator) {
1288 *plist = __CFArrayCreateTransfer(allocator, list, cnt);
1289 } else {
1290 *plist = CFArrayCreate(allocator, list, cnt, &kCFTypeArrayCallBacks);
1291 for (CFIndex idx = 0; idx < cnt; idx++) {
1292 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(list[idx]);
1293 }
1294 }
1295 }
1296 } else {
1297 if (mutabilityOption != kCFPropertyListImmutable) {
1298 *plist = CFSetCreateMutable(allocator, 0, &kCFTypeSetCallBacks);
1299 for (CFIndex idx = 0; idx < cnt; idx++) {
1300 CFSetAddValue((CFMutableSetRef)*plist, list[idx]);
1301 }
1302 for (CFIndex idx = 0; idx < cnt; idx++) {
1303 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(list[idx]);
1304 }
1305 } else {
1306 if (!kCFUseCollectableAllocator) {
1307 *plist = __CFSetCreateTransfer(allocator, list, cnt);
1308 } else {
1309 *plist = CFSetCreate(allocator, list, cnt, &kCFTypeSetCallBacks);
1310 for (CFIndex idx = 0; idx < cnt; idx++) {
1311 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(list[idx]);
1312 }
1313 }
1314 }
1315 }
1316 if (objects && *plist && (mutabilityOption == kCFPropertyListImmutable)) {
1317 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1318 }
1319 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, list);
1320 return (*plist) ? true : false;
1321 }
1322 case kCFBinaryPlistMarkerDict: {
1323 const uint8_t *ptr = databytes + startOffset;
1324 int32_t err = CF_NO_ERROR;
1325 ptr = check_ptr_add(ptr, 1, &err);
1326 if (CF_NO_ERROR != err) FAIL_FALSE;
1327 CFIndex cnt = marker & 0x0f;
1328 if (0xf == cnt) {
1329 uint64_t bigint = 0;
1330 if (!_readInt(ptr, databytes + objectsRangeEnd, &bigint, &ptr)) FAIL_FALSE;
1331 if (LONG_MAX < bigint) FAIL_FALSE;
1332 cnt = (CFIndex)bigint;
1333 }
1334 cnt = check_size_t_mul(cnt, 2, &err);
1335 if (CF_NO_ERROR != err) FAIL_FALSE;
1336 size_t byte_cnt = check_size_t_mul(cnt, trailer->_objectRefSize, &err);
1337 if (CF_NO_ERROR != err) FAIL_FALSE;
1338 const uint8_t *extent = check_ptr_add(ptr, byte_cnt, &err) - 1;
1339 if (CF_NO_ERROR != err) FAIL_FALSE;
1340 if (databytes + objectsRangeEnd < extent) FAIL_FALSE;
1341 byte_cnt = check_size_t_mul(cnt, sizeof(CFPropertyListRef), &err);
1342 if (CF_NO_ERROR != err) FAIL_FALSE;
1343 list = (cnt <= 256) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, byte_cnt, __kCFAllocatorGCScannedMemory);
1344 if (!list) FAIL_FALSE;
1345 Boolean madeSet = false;
1346 if (!set && 15 < curDepth) {
1347 set = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, NULL);
1348 madeSet = set ? true : false;
1349 }
1350 if (set) CFSetAddValue(set, (const void *)(uintptr_t)startOffset);
1351 for (CFIndex idx = 0; idx < cnt; idx++) {
1352 CFPropertyListRef pl = NULL;
1353 off = _getOffsetOfRefAt(databytes, ptr, trailer);
1354 if (!__CFBinaryPlistCreateObject2(databytes, datalen, off, trailer, allocator, mutabilityOption, objects, set, curDepth + 1, &pl) || (idx < cnt / 2 && !_plistIsPrimitive(pl))) {
1355 if (pl && !_CFAllocatorIsGCRefZero(allocator)) CFRelease(pl);
1356 while (idx--) {
1357 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(list[idx]);
1358 }
1359 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, list);
1360 FAIL_FALSE;
1361 }
1362 __CFAssignWithWriteBarrier((void **)list + idx, (void *)pl);
1363 ptr += trailer->_objectRefSize;
1364 }
1365 if (set) CFSetRemoveValue(set, (const void *)(uintptr_t)startOffset);
1366 if (madeSet) {
1367 CFRelease(set);
1368 set = NULL;
1369 }
1370 if (mutabilityOption != kCFPropertyListImmutable) {
1371 *plist = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1372 for (CFIndex idx = 0; idx < cnt / 2; idx++) {
1373 CFDictionaryAddValue((CFMutableDictionaryRef)*plist, list[idx], list[idx + cnt / 2]);
1374 }
1375 for (CFIndex idx = 0; idx < cnt; idx++) {
1376 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(list[idx]);
1377 }
1378 } else {
1379 if (!kCFUseCollectableAllocator) {
1380 *plist = __CFDictionaryCreateTransfer(allocator, list, list + cnt / 2, cnt / 2);
1381 } else {
1382 *plist = CFDictionaryCreate(allocator, list, list + cnt / 2, cnt / 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1383 for (CFIndex idx = 0; idx < cnt; idx++) {
1384 if (!_CFAllocatorIsGCRefZero(allocator)) CFRelease(list[idx]);
1385 }
1386 }
1387 }
1388 if (objects && *plist && (mutabilityOption == kCFPropertyListImmutable)) {
1389 CFDictionarySetValue(objects, (const void *)(uintptr_t)startOffset, *plist);
1390 }
1391 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, list);
1392 return (*plist) ? true : false;
1393 }
1394 }
1395 FAIL_FALSE;
1396 }
1397
1398 bool __CFBinaryPlistCreateObject(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFAllocatorRef allocator, CFOptionFlags mutabilityOption, CFMutableDictionaryRef objects, CFPropertyListRef *plist) {
1399 // for compatibility with Foundation's use, need to leave this here
1400 return __CFBinaryPlistCreateObject2(databytes, datalen, startOffset, trailer, allocator, mutabilityOption, objects, NULL, 0, plist);
1401 }
1402
1403 __private_extern__ bool __CFTryParseBinaryPlist(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags option, CFPropertyListRef *plist, CFStringRef *errorString) {
1404 uint8_t marker;
1405 CFBinaryPlistTrailer trailer;
1406 uint64_t offset;
1407 const uint8_t *databytes = CFDataGetBytePtr(data);
1408 uint64_t datalen = CFDataGetLength(data);
1409
1410 if (8 <= datalen && __CFBinaryPlistGetTopLevelInfo(databytes, datalen, &marker, &offset, &trailer)) {
1411 // FALSE: We know for binary plist parsing that the result objects will be retained
1412 // by their containing collections as the parsing proceeds, so we do not need
1413 // to use retaining callbacks for the objects map in this case. WHY: the file might
1414 // be malformed and contain hash-equal keys for the same dictionary (for example)
1415 // and the later key will cause the previous one to be released when we set the second
1416 // in the dictionary.
1417 CFMutableDictionaryRef objects = CFDictionaryCreateMutable(kCFAllocatorSystemDefaultGCRefZero, 0, NULL, &kCFTypeDictionaryValueCallBacks);
1418 _CFDictionarySetCapacity(objects, trailer._numObjects);
1419 CFPropertyListRef pl = NULL;
1420 if (__CFBinaryPlistCreateObject2(databytes, datalen, offset, &trailer, allocator, option, objects, NULL, 0, &pl)) {
1421 if (plist) *plist = pl;
1422 } else {
1423 if (plist) *plist = NULL;
1424 if (errorString) *errorString = (CFStringRef)CFRetain(CFSTR("binary data is corrupt"));
1425 }
1426 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFRelease(objects);
1427 return true;
1428 }
1429 return false;
1430 }