2 * Copyright (c) 2012 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 Copyright (c) 2000-2012, Apple Inc. All rights reserved.
26 Responsibility: Tony Parker
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 <CoreFoundation/CFUUID.h>
45 #include "CFInternal.h"
46 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
47 #include <CoreFoundation/CFStream.h>
56 kCFNumberSInt128Type
= 17
61 CF_OVERFLOW_ERROR
= (1 << 0),
64 CF_INLINE
uint32_t __check_uint32_add_unsigned_unsigned(uint32_t x
, uint32_t y
, int32_t* err
) {
65 if((UINT_MAX
- y
) < x
)
66 *err
= *err
| CF_OVERFLOW_ERROR
;
70 CF_INLINE
uint64_t __check_uint64_add_unsigned_unsigned(uint64_t x
, uint64_t y
, int32_t* err
) {
71 if((ULLONG_MAX
- y
) < x
)
72 *err
= *err
| CF_OVERFLOW_ERROR
;
76 CF_INLINE
uint32_t __check_uint32_mul_unsigned_unsigned(uint32_t x
, uint32_t y
, int32_t* err
) {
77 uint64_t tmp
= (uint64_t) x
* (uint64_t) y
;
78 /* If any of the upper 32 bits touched, overflow */
79 if(tmp
& 0xffffffff00000000ULL
)
80 *err
= *err
| CF_OVERFLOW_ERROR
;
81 return (uint32_t) tmp
;
84 CF_INLINE
uint64_t __check_uint64_mul_unsigned_unsigned(uint64_t x
, uint64_t y
, int32_t* err
) {
87 *err
= *err
| CF_OVERFLOW_ERROR
;
92 #define check_ptr_add(p, a, err) (const uint8_t *)__check_uint64_add_unsigned_unsigned((uintptr_t)p, (uintptr_t)a, err)
93 #define check_size_t_mul(b, a, err) (size_t)__check_uint64_mul_unsigned_unsigned((size_t)b, (size_t)a, err)
95 #define check_ptr_add(p, a, err) (const uint8_t *)__check_uint32_add_unsigned_unsigned((uintptr_t)p, (uintptr_t)a, err)
96 #define check_size_t_mul(b, a, err) (size_t)__check_uint32_mul_unsigned_unsigned((size_t)b, (size_t)a, err)
100 #pragma mark Keyed Archiver UID
102 struct __CFKeyedArchiverUID
{
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
);
112 static CFStringRef
__CFKeyedArchiverUIDCopyFormattingDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
113 CFKeyedArchiverUIDRef uid
= (CFKeyedArchiverUIDRef
)cf
;
114 return CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("@%u@"), uid
->_value
);
117 static CFTypeID __kCFKeyedArchiverUIDTypeID
= _kCFRuntimeNotATypeID
;
119 static const CFRuntimeClass __CFKeyedArchiverUIDClass
= {
121 "CFKeyedArchiverUID",
125 NULL
, // equal -- pointer equality only
126 NULL
, // hash -- pointer hashing only
127 __CFKeyedArchiverUIDCopyFormattingDescription
,
128 __CFKeyedArchiverUIDCopyDescription
131 __private_extern__
void __CFKeyedArchiverUIDInitialize(void) {
132 __kCFKeyedArchiverUIDTypeID
= _CFRuntimeRegisterClass(&__CFKeyedArchiverUIDClass
);
135 CFTypeID
_CFKeyedArchiverUIDGetTypeID(void) {
136 return __kCFKeyedArchiverUIDTypeID
;
139 CFKeyedArchiverUIDRef
_CFKeyedArchiverUIDCreate(CFAllocatorRef allocator
, uint32_t value
) {
140 CFKeyedArchiverUIDRef uid
;
141 uid
= (CFKeyedArchiverUIDRef
)_CFRuntimeCreateInstance(allocator
, __kCFKeyedArchiverUIDTypeID
, sizeof(struct __CFKeyedArchiverUID
) - sizeof(CFRuntimeBase
), NULL
);
145 ((struct __CFKeyedArchiverUID
*)uid
)->_value
= value
;
150 uint32_t _CFKeyedArchiverUIDGetValue(CFKeyedArchiverUIDRef uid
) {
157 __private_extern__ CFErrorRef
__CFPropertyListCreateError(CFIndex code
, CFStringRef debugString
, ...);
165 uint8_t buffer
[8192 - 32];
166 } __CFBinaryPlistWriteBuffer
;
168 static void writeBytes(__CFBinaryPlistWriteBuffer
*buf
, const UInt8
*bytes
, CFIndex length
) {
169 if (0 == length
) return;
170 if (buf
->error
) return;
171 if (buf
->streamIsData
) {
172 CFDataAppendBytes((CFMutableDataRef
)buf
->stream
, bytes
, length
);
173 buf
->written
+= length
;
175 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
177 CFIndex ret
= CFWriteStreamWrite((CFWriteStreamRef
)buf
->stream
, bytes
, length
);
179 buf
->error
= __CFPropertyListCreateError(kCFPropertyListWriteStreamError
, CFSTR("Binary property list writing could not be completed because stream is full."));
183 CFErrorRef err
= CFWriteStreamCopyError((CFWriteStreamRef
)buf
->stream
);
184 buf
->error
= err
? err
: __CFPropertyListCreateError(kCFPropertyListWriteStreamError
, CFSTR("Binary property list writing could not be completed because the stream had an unknown error."));
192 CFAssert(false, __kCFLogAssertion
, "Streams are not supported on this platform");
197 static void bufferWrite(__CFBinaryPlistWriteBuffer
*buf
, const uint8_t *buffer
, CFIndex count
) {
198 if (0 == count
) return;
199 if ((CFIndex
)sizeof(buf
->buffer
) <= count
) {
200 writeBytes(buf
, buf
->buffer
, buf
->used
);
202 writeBytes(buf
, buffer
, count
);
205 CFIndex copyLen
= __CFMin(count
, (CFIndex
)sizeof(buf
->buffer
) - buf
->used
);
206 memmove(buf
->buffer
+ buf
->used
, buffer
, copyLen
);
207 buf
->used
+= copyLen
;
208 if (sizeof(buf
->buffer
) == buf
->used
) {
209 writeBytes(buf
, buf
->buffer
, sizeof(buf
->buffer
));
210 memmove(buf
->buffer
, buffer
+ copyLen
, count
- copyLen
);
211 buf
->used
= count
- copyLen
;
215 static void bufferFlush(__CFBinaryPlistWriteBuffer
*buf
) {
216 writeBytes(buf
, buf
->buffer
, buf
->used
);
222 magic number ("bplist")
224 byte length of plist incl. header, an encoded int number object (as below) [v.2+ only]
225 32-bit CRC (ISO/IEC 8802-3:1989) of plist bytes w/o CRC, encoded always as
226 "0x12 0x__ 0x__ 0x__ 0x__", big-endian, may be 0 to indicate no CRC [v.2+ only]
229 variable-sized objects
231 Object Formats (marker byte followed by additional info in some cases)
232 null 0000 0000 // null object [v1+ only]
233 bool 0000 1000 // false
234 bool 0000 1001 // true
235 url 0000 1100 string // URL with no base URL, recursive encoding of URL string [v1+ only]
236 url 0000 1101 base string // URL with base URL, recursive encoding of base URL, then recursive encoding of URL string [v1+ only]
237 uuid 0000 1110 // 16-byte UUID [v1+ only]
238 fill 0000 1111 // fill byte
239 int 0001 0nnn ... // # of bytes is 2^nnn, big-endian bytes
240 real 0010 0nnn ... // # of bytes is 2^nnn, big-endian bytes
241 date 0011 0011 ... // 8 byte float follows, big-endian bytes
242 data 0100 nnnn [int] ... // nnnn is number of bytes unless 1111 then int count follows, followed by bytes
243 string 0101 nnnn [int] ... // ASCII string, nnnn is # of chars, else 1111 then int count, then bytes
244 string 0110 nnnn [int] ... // Unicode string, nnnn is # of chars, else 1111 then int count, then big-endian 2-byte uint16_t
246 uid 1000 nnnn ... // nnnn+1 is # of bytes
248 array 1010 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows
249 ordset 1011 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows [v1+ only]
250 set 1100 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows [v1+ only]
251 dict 1101 nnnn [int] keyref* objref* // nnnn is count, unless '1111', then int count follows
256 list of ints, byte size of which is given in trailer
257 -- these are the byte offsets into the file
258 -- number of these is in the trailer
261 byte size of offset ints in offset table
262 byte size of object refs in arrays and dicts
263 number of offsets in offset table (also is number of objects)
264 element # in offset table which is top level object
267 Version 1.5 binary plists do not use object references (uid),
268 but instead inline the object serialization itself at that point.
269 It also doesn't use an offset table or a trailer. It does have
270 an extended header, and the top-level object follows the header.
275 static CFTypeID stringtype
= -1, datatype
= -1, numbertype
= -1, datetype
= -1;
276 static CFTypeID booltype
= -1, nulltype
= -1, dicttype
= -1, arraytype
= -1;
277 static CFTypeID uuidtype
= -1, urltype
= -1, osettype
= -1, settype
= -1;
279 static void initStatics() {
280 if ((CFTypeID
)-1 == stringtype
) {
281 stringtype
= CFStringGetTypeID();
283 if ((CFTypeID
)-1 == datatype
) {
284 datatype
= CFDataGetTypeID();
286 if ((CFTypeID
)-1 == numbertype
) {
287 numbertype
= CFNumberGetTypeID();
289 if ((CFTypeID
)-1 == booltype
) {
290 booltype
= CFBooleanGetTypeID();
292 if ((CFTypeID
)-1 == datetype
) {
293 datetype
= CFDateGetTypeID();
295 if ((CFTypeID
)-1 == dicttype
) {
296 dicttype
= CFDictionaryGetTypeID();
298 if ((CFTypeID
)-1 == arraytype
) {
299 arraytype
= CFArrayGetTypeID();
301 if ((CFTypeID
)-1 == settype
) {
302 settype
= CFSetGetTypeID();
304 if ((CFTypeID
)-1 == nulltype
) {
305 nulltype
= CFNullGetTypeID();
307 if ((CFTypeID
)-1 == uuidtype
) {
308 uuidtype
= CFUUIDGetTypeID();
310 if ((CFTypeID
)-1 == urltype
) {
311 urltype
= CFURLGetTypeID();
313 if ((CFTypeID
)-1 == osettype
) {
318 static void _appendInt(__CFBinaryPlistWriteBuffer
*buf
, uint64_t bigint
) {
322 if (bigint
<= (uint64_t)0xff) {
324 marker
= kCFBinaryPlistMarkerInt
| 0;
325 } else if (bigint
<= (uint64_t)0xffff) {
327 marker
= kCFBinaryPlistMarkerInt
| 1;
328 } else if (bigint
<= (uint64_t)0xffffffff) {
330 marker
= kCFBinaryPlistMarkerInt
| 2;
333 marker
= kCFBinaryPlistMarkerInt
| 3;
335 bigint
= CFSwapInt64HostToBig(bigint
);
336 bytes
= (uint8_t *)&bigint
+ sizeof(bigint
) - nbytes
;
337 bufferWrite(buf
, &marker
, 1);
338 bufferWrite(buf
, bytes
, nbytes
);
341 static void _appendUID(__CFBinaryPlistWriteBuffer
*buf
, CFKeyedArchiverUIDRef uid
) {
345 uint64_t bigint
= _CFKeyedArchiverUIDGetValue(uid
);
346 if (bigint
<= (uint64_t)0xff) {
348 } else if (bigint
<= (uint64_t)0xffff) {
350 } else if (bigint
<= (uint64_t)0xffffffff) {
355 marker
= kCFBinaryPlistMarkerUID
| (uint8_t)(nbytes
- 1);
356 bigint
= CFSwapInt64HostToBig(bigint
);
357 bytes
= (uint8_t *)&bigint
+ sizeof(bigint
) - nbytes
;
358 bufferWrite(buf
, &marker
, 1);
359 bufferWrite(buf
, bytes
, nbytes
);
362 static void _appendString(__CFBinaryPlistWriteBuffer
*buf
, CFStringRef str
) {
363 CFIndex ret
, count
= CFStringGetLength(str
);
364 CFIndex needed
, idx2
;
365 uint8_t *bytes
, buffer
[1024];
366 bytes
= (count
<= 1024) ? buffer
: (uint8_t *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero
, count
, 0);
367 // presumption, believed to be true, is that ASCII encoding may need
368 // less bytes, but will not need greater, than the # of unichars
369 ret
= CFStringGetBytes(str
, CFRangeMake(0, count
), kCFStringEncodingASCII
, 0, false, bytes
, count
, &needed
);
371 uint8_t marker
= (uint8_t)(kCFBinaryPlistMarkerASCIIString
| (needed
< 15 ? needed
: 0xf));
372 bufferWrite(buf
, &marker
, 1);
374 _appendInt(buf
, (uint64_t)needed
);
376 bufferWrite(buf
, bytes
, needed
);
379 uint8_t marker
= (uint8_t)(kCFBinaryPlistMarkerUnicode16String
| (count
< 15 ? count
: 0xf));
380 bufferWrite(buf
, &marker
, 1);
382 _appendInt(buf
, (uint64_t)count
);
384 chars
= (UniChar
*)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero
, count
* sizeof(UniChar
), 0);
385 CFStringGetCharacters(str
, CFRangeMake(0, count
), chars
);
386 for (idx2
= 0; idx2
< count
; idx2
++) {
387 chars
[idx2
] = CFSwapInt16HostToBig(chars
[idx2
]);
389 bufferWrite(buf
, (uint8_t *)chars
, count
* sizeof(UniChar
));
390 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero
, chars
);
392 if (bytes
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero
, bytes
);
395 CF_EXPORT CFNumberType
_CFNumberGetType2(CFNumberRef number
);
397 static void _appendNumber(__CFBinaryPlistWriteBuffer
*buf
, CFNumberRef num
) {
402 if (CFNumberIsFloatType(num
)) {
403 CFSwappedFloat64 swapped64
;
404 CFSwappedFloat32 swapped32
;
405 if (CFNumberGetByteSize(num
) <= (CFIndex
)sizeof(float)) {
407 CFNumberGetValue(num
, kCFNumberFloat32Type
, &v
);
408 swapped32
= CFConvertFloat32HostToSwapped(v
);
409 bytes
= (uint8_t *)&swapped32
;
410 nbytes
= sizeof(float);
411 marker
= kCFBinaryPlistMarkerReal
| 2;
414 CFNumberGetValue(num
, kCFNumberFloat64Type
, &v
);
415 swapped64
= CFConvertFloat64HostToSwapped(v
);
416 bytes
= (uint8_t *)&swapped64
;
417 nbytes
= sizeof(double);
418 marker
= kCFBinaryPlistMarkerReal
| 3;
420 bufferWrite(buf
, &marker
, 1);
421 bufferWrite(buf
, bytes
, nbytes
);
423 CFNumberType type
= _CFNumberGetType2(num
);
424 if (kCFNumberSInt128Type
== type
) {
426 CFNumberGetValue(num
, kCFNumberSInt128Type
, &s
);
431 storage
.high
= CFSwapInt64HostToBig(s
.high
);
432 storage
.low
= CFSwapInt64HostToBig(s
.low
);
433 uint8_t *bytes
= (uint8_t *)&storage
;
434 uint8_t marker
= kCFBinaryPlistMarkerInt
| 4;
436 bufferWrite(buf
, &marker
, 1);
437 bufferWrite(buf
, bytes
, nbytes
);
439 CFNumberGetValue(num
, kCFNumberSInt64Type
, &bigint
);
440 _appendInt(buf
, bigint
);
445 // 0 == objRefSize means version 1.5, else version 0.0 or 0.1
446 static Boolean
_appendObject(__CFBinaryPlistWriteBuffer
*buf
, CFTypeRef obj
, CFDictionaryRef objtable
, uint32_t objRefSize
) {
449 CFTypeID type
= CFGetTypeID(obj
);
450 if (stringtype
== type
) {
451 _appendString(buf
, (CFStringRef
)obj
);
452 } else if (numbertype
== type
) {
453 _appendNumber(buf
, (CFNumberRef
)obj
);
454 } else if (booltype
== type
) {
455 uint8_t marker
= CFBooleanGetValue((CFBooleanRef
)obj
) ? kCFBinaryPlistMarkerTrue
: kCFBinaryPlistMarkerFalse
;
456 bufferWrite(buf
, &marker
, 1);
457 } else if (0 == objRefSize
&& nulltype
== type
) {
458 uint8_t marker
= 0x00;
459 bufferWrite(buf
, &marker
, 1);
460 } else if (datatype
== type
) {
461 CFIndex count
= CFDataGetLength((CFDataRef
)obj
);
462 uint8_t marker
= (uint8_t)(kCFBinaryPlistMarkerData
| (count
< 15 ? count
: 0xf));
463 bufferWrite(buf
, &marker
, 1);
465 _appendInt(buf
, (uint64_t)count
);
467 bufferWrite(buf
, CFDataGetBytePtr((CFDataRef
)obj
), count
);
468 } else if (datetype
== type
) {
469 CFSwappedFloat64 swapped
;
470 uint8_t marker
= kCFBinaryPlistMarkerDate
;
471 bufferWrite(buf
, &marker
, 1);
472 swapped
= CFConvertFloat64HostToSwapped(CFDateGetAbsoluteTime((CFDateRef
)obj
));
473 bufferWrite(buf
, (uint8_t *)&swapped
, sizeof(swapped
));
474 } else if (dicttype
== type
) {
475 CFIndex count
= CFDictionaryGetCount((CFDictionaryRef
)obj
);
476 uint8_t marker
= (uint8_t)(kCFBinaryPlistMarkerDict
| (count
< 15 ? count
: 0xf));
477 bufferWrite(buf
, &marker
, 1);
479 _appendInt(buf
, (uint64_t)count
);
481 CFPropertyListRef
*list
, buffer
[512];
482 list
= (count
<= 256) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero
, 2 * count
* sizeof(CFTypeRef
), __kCFAllocatorGCScannedMemory
);
483 CFDictionaryGetKeysAndValues((CFDictionaryRef
)obj
, list
, list
+ count
);
484 for (idx2
= 0; idx2
< 2 * count
; idx2
++) {
485 CFPropertyListRef value
= list
[idx2
];
487 uint32_t swapped
= 0;
488 uint8_t *source
= (uint8_t *)&swapped
;
489 refnum
= (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable
, value
);
490 swapped
= CFSwapInt32HostToBig((uint32_t)refnum
);
491 bufferWrite(buf
, source
+ sizeof(swapped
) - objRefSize
, objRefSize
);
493 Boolean ret
= _appendObject(buf
, value
, objtable
, objRefSize
);
495 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero
, list
);
500 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero
, list
);
501 } else if (arraytype
== type
) {
502 CFIndex count
= CFArrayGetCount((CFArrayRef
)obj
);
503 CFPropertyListRef
*list
, buffer
[256];
504 uint8_t marker
= (uint8_t)(kCFBinaryPlistMarkerArray
| (count
< 15 ? count
: 0xf));
505 bufferWrite(buf
, &marker
, 1);
507 _appendInt(buf
, (uint64_t)count
);
509 list
= (count
<= 256) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero
, count
* sizeof(CFTypeRef
), __kCFAllocatorGCScannedMemory
);
510 CFArrayGetValues((CFArrayRef
)obj
, CFRangeMake(0, count
), list
);
511 for (idx2
= 0; idx2
< count
; idx2
++) {
512 CFPropertyListRef value
= list
[idx2
];
514 uint32_t swapped
= 0;
515 uint8_t *source
= (uint8_t *)&swapped
;
516 refnum
= (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable
, value
);
517 swapped
= CFSwapInt32HostToBig((uint32_t)refnum
);
518 bufferWrite(buf
, source
+ sizeof(swapped
) - objRefSize
, objRefSize
);
520 Boolean ret
= _appendObject(buf
, value
, objtable
, objRefSize
);
522 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero
, list
);
527 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero
, list
);
528 } else if (0 == objRefSize
&& settype
== type
) {
529 CFIndex count
= CFSetGetCount((CFSetRef
)obj
);
530 CFPropertyListRef
*list
, buffer
[256];
531 uint8_t marker
= (uint8_t)(0xc0 | (count
< 15 ? count
: 0xf));
532 bufferWrite(buf
, &marker
, 1);
534 _appendInt(buf
, (uint64_t)count
);
536 list
= (count
<= 256) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero
, count
* sizeof(CFTypeRef
), __kCFAllocatorGCScannedMemory
);
537 CFSetGetValues((CFSetRef
)obj
, list
);
538 for (idx2
= 0; idx2
< count
; idx2
++) {
539 CFPropertyListRef value
= list
[idx2
];
541 uint32_t swapped
= 0;
542 uint8_t *source
= (uint8_t *)&swapped
;
543 refnum
= (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable
, value
);
544 swapped
= CFSwapInt32HostToBig((uint32_t)refnum
);
545 bufferWrite(buf
, source
+ sizeof(swapped
) - objRefSize
, objRefSize
);
547 Boolean ret
= _appendObject(buf
, value
, objtable
, objRefSize
);
549 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero
, list
);
554 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero
, list
);
555 } else if (0 == objRefSize
&& osettype
== type
) {
556 // Not actually implemented
557 } else if (0 == objRefSize
&& uuidtype
== type
) {
558 uint8_t marker
= (uint8_t)(0x0e);
559 bufferWrite(buf
, &marker
, 1);
561 CFUUIDBytes uu
= CFUUIDGetUUIDBytes((CFUUIDRef
)obj
);
562 bufferWrite(buf
, (const uint8_t *)&uu
, 16);
564 } else if (0 == objRefSize
&& urltype
== type
) {
565 CFStringRef string
= CFURLGetString((CFURLRef
)obj
);
569 CFURLRef base
= CFURLGetBaseURL((CFURLRef
)obj
);
570 uint8_t marker
= (uint8_t)(0x00 | (base
? 0xd : 0xc));
571 bufferWrite(buf
, &marker
, 1);
573 _appendObject(buf
, base
, objtable
, objRefSize
);
575 _appendObject(buf
, string
, objtable
, objRefSize
);
576 } else if (0 != objRefSize
&& _CFKeyedArchiverUIDGetTypeID() == type
) {
577 _appendUID(buf
, (CFKeyedArchiverUIDRef
)obj
);
584 static void _flattenPlist(CFPropertyListRef plist
, CFMutableArrayRef objlist
, CFMutableDictionaryRef objtable
, CFMutableSetRef uniquingset
) {
585 CFPropertyListRef unique
;
587 CFTypeID type
= CFGetTypeID(plist
);
589 CFPropertyListRef
*list
, buffer
[256];
591 // Do not unique dictionaries or arrays, because: they
592 // are slow to compare, and have poor hash codes.
593 // Uniquing bools is unnecessary.
594 if (stringtype
== type
|| numbertype
== type
|| datetype
== type
|| datatype
== type
) {
595 CFIndex before
= CFSetGetCount(uniquingset
);
596 CFSetAddValue(uniquingset
, plist
);
597 CFIndex after
= CFSetGetCount(uniquingset
);
598 if (after
== before
) { // already in set
599 unique
= CFSetGetValue(uniquingset
, plist
);
600 if (unique
!= plist
) {
601 refnum
= (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable
, unique
);
602 CFDictionaryAddValue(objtable
, plist
, (const void *)(uintptr_t)refnum
);
607 refnum
= CFArrayGetCount(objlist
);
608 CFArrayAppendValue(objlist
, plist
);
609 CFDictionaryAddValue(objtable
, plist
, (const void *)(uintptr_t)refnum
);
610 if (dicttype
== type
) {
611 CFIndex count
= CFDictionaryGetCount((CFDictionaryRef
)plist
);
612 list
= (count
<= 128) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero
, 2 * count
* sizeof(CFTypeRef
), __kCFAllocatorGCScannedMemory
);
613 CFDictionaryGetKeysAndValues((CFDictionaryRef
)plist
, list
, list
+ count
);
614 for (idx
= 0; idx
< 2 * count
; idx
++) {
615 _flattenPlist(list
[idx
], objlist
, objtable
, uniquingset
);
617 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero
, list
);
618 } else if (arraytype
== type
) {
619 CFIndex count
= CFArrayGetCount((CFArrayRef
)plist
);
620 list
= (count
<= 256) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero
, count
* sizeof(CFTypeRef
), __kCFAllocatorGCScannedMemory
);
621 CFArrayGetValues((CFArrayRef
)plist
, CFRangeMake(0, count
), list
);
622 for (idx
= 0; idx
< count
; idx
++) {
623 _flattenPlist(list
[idx
], objlist
, objtable
, uniquingset
);
625 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero
, list
);
629 /* Get the number of bytes required to hold the value in 'count'. Will return a power of 2 value big enough to hold 'count'.
631 CF_INLINE
uint8_t _byteCount(uint64_t count
) {
632 uint64_t mask
= ~(uint64_t)0;
635 // Find something big enough to hold 'count'
636 while (count
& mask
) {
641 // Ensure that 'count' is a power of 2
642 // For sizes bigger than 8, just use the required count
643 while ((size
!= 1 && size
!= 2 && size
!= 4 && size
!= 8) && size
<= 8) {
650 // stream can be a CFWriteStreamRef (on supported platforms) or a CFMutableDataRef
651 /* 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. */
652 CFIndex
__CFBinaryPlistWrite(CFPropertyListRef plist
, CFTypeRef stream
, uint64_t estimate
, CFOptionFlags options
, CFErrorRef
*error
) {
653 CFMutableDictionaryRef objtable
= NULL
;
654 CFMutableArrayRef objlist
= NULL
;
655 CFMutableSetRef uniquingset
= NULL
;
656 CFBinaryPlistTrailer trailer
;
657 uint64_t *offsets
, length_so_far
;
659 __CFBinaryPlistWriteBuffer
*buf
;
663 const CFDictionaryKeyCallBacks dictKeyCallbacks
= {0, __CFTypeCollectionRetain
, __CFTypeCollectionRelease
, 0, 0, 0};
664 objtable
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &dictKeyCallbacks
, NULL
);
666 const CFArrayCallBacks arrayCallbacks
= {0, __CFTypeCollectionRetain
, __CFTypeCollectionRelease
, 0, 0};
667 objlist
= CFArrayCreateMutable(kCFAllocatorSystemDefault
, 0, &arrayCallbacks
);
670 const CFSetCallBacks setCallbacks
= {0, __CFTypeCollectionRetain
, __CFTypeCollectionRelease
, 0, 0, 0};
671 uniquingset
= CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, &setCallbacks
);
673 #if DEPLOYMENT_TARGET_MACOSX
674 _CFDictionarySetCapacity(objtable
, estimate
? estimate
: 650);
675 _CFArraySetCapacity(objlist
, estimate
? estimate
: 650);
676 _CFSetSetCapacity(uniquingset
, estimate
? estimate
: 1000);
679 _flattenPlist(plist
, objlist
, objtable
, uniquingset
);
681 CFRelease(uniquingset
);
683 cnt
= CFArrayGetCount(objlist
);
684 offsets
= (uint64_t *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero
, (CFIndex
)(cnt
* sizeof(*offsets
)), 0);
686 buf
= (__CFBinaryPlistWriteBuffer
*)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero
, sizeof(__CFBinaryPlistWriteBuffer
), 0);
687 buf
->stream
= stream
;
689 buf
->streamIsData
= (CFGetTypeID(stream
) == CFDataGetTypeID());
692 bufferWrite(buf
, (uint8_t *)"bplist00", 8); // header
694 memset(&trailer
, 0, sizeof(trailer
));
695 trailer
._numObjects
= CFSwapInt64HostToBig(cnt
);
696 trailer
._topObject
= 0; // true for this implementation
697 trailer
._objectRefSize
= _byteCount(cnt
);
698 for (idx
= 0; idx
< cnt
; idx
++) {
699 offsets
[idx
] = buf
->written
+ buf
->used
;
700 CFPropertyListRef obj
= CFArrayGetValueAtIndex(objlist
, (CFIndex
)idx
);
701 Boolean success
= _appendObject(buf
, obj
, objtable
, trailer
._objectRefSize
);
705 if (error
&& buf
->error
) {
706 // caller will release error
708 } else if (buf
->error
) {
709 // caller is not interested in error, release it here
710 CFRelease(buf
->error
);
712 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero
, buf
);
713 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero
, offsets
);
720 length_so_far
= buf
->written
+ buf
->used
;
721 trailer
._offsetTableOffset
= CFSwapInt64HostToBig(length_so_far
);
722 trailer
._offsetIntSize
= _byteCount(length_so_far
);
724 for (idx
= 0; idx
< cnt
; idx
++) {
725 uint64_t swapped
= CFSwapInt64HostToBig(offsets
[idx
]);
726 uint8_t *source
= (uint8_t *)&swapped
;
727 bufferWrite(buf
, source
+ sizeof(*offsets
) - trailer
._offsetIntSize
, trailer
._offsetIntSize
);
729 length_so_far
+= cnt
* trailer
._offsetIntSize
;
730 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero
, offsets
);
732 bufferWrite(buf
, (uint8_t *)&trailer
, sizeof(trailer
));
734 length_so_far
+= sizeof(trailer
);
737 // caller will release error
740 CFRelease(buf
->error
);
742 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero
, buf
);
745 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero
, buf
);
746 return (CFIndex
)length_so_far
;
750 CFIndex
__CFBinaryPlistWriteToStream(CFPropertyListRef plist
, CFTypeRef stream
) {
751 return __CFBinaryPlistWrite(plist
, stream
, 0, 0, NULL
);
754 // to be removed soon
755 CFIndex
__CFBinaryPlistWriteToStreamWithEstimate(CFPropertyListRef plist
, CFTypeRef stream
, uint64_t estimate
) {
756 return __CFBinaryPlistWrite(plist
, stream
, estimate
, 0, NULL
);
759 // to be removed soon
760 CFIndex
__CFBinaryPlistWriteToStreamWithOptions(CFPropertyListRef plist
, CFTypeRef stream
, uint64_t estimate
, CFOptionFlags options
) {
761 return __CFBinaryPlistWrite(plist
, stream
, estimate
, options
, NULL
);
764 // Write a version 1.5 plist to the data; extra objects + inlined objects (no references, no uniquing)
765 __private_extern__ Boolean
__CFBinaryPlistWrite15(CFPropertyListRef plist
, CFMutableDataRef data
, CFErrorRef
*error
) {
768 __CFBinaryPlistWriteBuffer
*buf
= (__CFBinaryPlistWriteBuffer
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, sizeof(__CFBinaryPlistWriteBuffer
), 0);
769 memset(buf
, 0, sizeof(__CFBinaryPlistWriteBuffer
));
772 buf
->streamIsData
= 1;
776 bufferWrite(buf
, (uint8_t *)"bplist15", 8); // header
777 bufferWrite(buf
, (uint8_t *)"\023\000\000\000\000\000\000\000\000", 9); // header (byte length)
778 bufferWrite(buf
, (uint8_t *)"\022\000\000\000\000", 5); // header (crc)
780 Boolean success
= _appendObject(buf
, plist
, NULL
, 0);
782 if (error
&& buf
->error
) {
783 // caller will release error
785 } else if (buf
->error
) {
786 // caller is not interested in error, release it here
787 CFRelease(buf
->error
);
789 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, buf
);
794 uint64_t swapped_len
= CFSwapInt64HostToBig(buf
->written
);
795 CFDataReplaceBytes(data
, CFRangeMake(9, 8), (uint8_t *)&swapped_len
, (CFIndex
)sizeof(swapped_len
));
797 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, buf
);
805 #define FAIL_FALSE do { return false; } while (0)
806 #define FAIL_MAXOFFSET do { return UINT64_MAX; } while (0)
808 __private_extern__
bool __CFBinaryPlistCreateObjectFiltered(const uint8_t *databytes
, uint64_t datalen
, uint64_t startOffset
, const CFBinaryPlistTrailer
*trailer
, CFAllocatorRef allocator
, CFOptionFlags mutabilityOption
, CFMutableDictionaryRef objects
, CFMutableSetRef set
, CFIndex curDepth
, CFSetRef keyPaths
, CFPropertyListRef
*plist
);
810 /* Grab a valSize-bytes integer out of the buffer pointed at by data and return it.
812 CF_INLINE
uint64_t _getSizedInt(const uint8_t *data
, uint8_t valSize
) {
813 #if defined(__i386__) || defined(__x86_64__)
815 return (uint64_t)*data
;
816 } else if (valSize
== 2) {
817 uint16_t val
= *(uint16_t *)data
;
818 return (uint64_t)CFSwapInt16BigToHost(val
);
819 } else if (valSize
== 4) {
820 uint32_t val
= *(uint32_t *)data
;
821 return (uint64_t)CFSwapInt32BigToHost(val
);
822 } else if (valSize
== 8) {
823 uint64_t val
= *(uint64_t *)data
;
824 return CFSwapInt64BigToHost(val
);
827 // Compatability with existing archives, including anything with a non-power-of-2
828 // size and 16-byte values, and architectures that don't support unaligned access
830 for (CFIndex idx
= 0; idx
< valSize
; idx
++) {
831 res
= (res
<< 8) + data
[idx
];
836 bool __CFBinaryPlistGetTopLevelInfo(const uint8_t *databytes
, uint64_t datalen
, uint8_t *marker
, uint64_t *offset
, CFBinaryPlistTrailer
*trailer
) {
837 CFBinaryPlistTrailer trail
;
841 if (!databytes
|| datalen
< sizeof(trail
) + 8 + 1) FAIL_FALSE
;
842 // Tiger and earlier will parse "bplist00"
843 // Leopard will parse "bplist00" or "bplist01"
844 // SnowLeopard will parse "bplist0?" where ? is any one character
845 if (0 != memcmp("bplist0", databytes
, 7)) {
848 memmove(&trail
, databytes
+ datalen
- sizeof(trail
), sizeof(trail
));
849 // In Leopard, the unused bytes in the trailer must be 0 or the parse will fail
850 // This check is not present in Tiger and earlier or after Leopard
851 trail
._numObjects
= CFSwapInt64BigToHost(trail
._numObjects
);
852 trail
._topObject
= CFSwapInt64BigToHost(trail
._topObject
);
853 trail
._offsetTableOffset
= CFSwapInt64BigToHost(trail
._offsetTableOffset
);
855 // Don't overflow on the number of objects or offset of the table
856 if (LONG_MAX
< trail
._numObjects
) FAIL_FALSE
;
857 if (LONG_MAX
< trail
._offsetTableOffset
) FAIL_FALSE
;
859 // Must be a minimum of 1 object
860 if (trail
._numObjects
< 1) FAIL_FALSE
;
862 // The ref to the top object must be a value in the range of 1 to the total number of objects
863 if (trail
._numObjects
<= trail
._topObject
) FAIL_FALSE
;
865 // The offset table must be after at least 9 bytes of other data ('bplist??' + 1 byte of object table data).
866 if (trail
._offsetTableOffset
< 9) FAIL_FALSE
;
868 // The trailer must point to a value before itself in the data.
869 if (datalen
- sizeof(trail
) <= trail
._offsetTableOffset
) FAIL_FALSE
;
871 // Minimum of 1 byte for the size of integers and references in the data
872 if (trail
._offsetIntSize
< 1) FAIL_FALSE
;
873 if (trail
._objectRefSize
< 1) FAIL_FALSE
;
875 int32_t err
= CF_NO_ERROR
;
877 // The total size of the offset table (number of objects * size of each int in the table) must not overflow
878 uint64_t offsetIntSize
= trail
._offsetIntSize
;
879 uint64_t offsetTableSize
= __check_uint64_mul_unsigned_unsigned(trail
._numObjects
, offsetIntSize
, &err
);
880 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
882 // The offset table must have at least 1 entry
883 if (offsetTableSize
< 1) FAIL_FALSE
;
885 // Make sure the size of the offset table and data sections do not overflow
886 uint64_t objectDataSize
= trail
._offsetTableOffset
- 8;
887 uint64_t tmpSum
= __check_uint64_add_unsigned_unsigned(8, objectDataSize
, &err
);
888 tmpSum
= __check_uint64_add_unsigned_unsigned(tmpSum
, offsetTableSize
, &err
);
889 tmpSum
= __check_uint64_add_unsigned_unsigned(tmpSum
, sizeof(trail
), &err
);
890 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
892 // The total size of the data should be equal to the sum of offsetTableOffset + sizeof(trailer)
893 if (datalen
!= tmpSum
) FAIL_FALSE
;
895 // 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.
896 if (trail
._objectRefSize
< 8 && (1ULL << (8 * trail
._objectRefSize
)) <= trail
._numObjects
) FAIL_FALSE
;
898 // The integers used for pointers in the offset table must be able to reach as far as the start of the offset table.
899 if (trail
._offsetIntSize
< 8 && (1ULL << (8 * trail
._offsetIntSize
)) <= trail
._offsetTableOffset
) FAIL_FALSE
;
902 const uint8_t *objectsFirstByte
;
903 objectsFirstByte
= check_ptr_add(databytes
, 8, &err
);
904 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
905 const uint8_t *offsetsFirstByte
= check_ptr_add(databytes
, trail
._offsetTableOffset
, &err
);
906 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
907 const uint8_t *offsetsLastByte
;
908 offsetsLastByte
= check_ptr_add(offsetsFirstByte
, offsetTableSize
- 1, &err
);
909 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
911 const uint8_t *bytesptr
= databytes
+ trail
._offsetTableOffset
;
912 uint64_t maxOffset
= trail
._offsetTableOffset
- 1;
913 for (CFIndex idx
= 0; idx
< trail
._numObjects
; idx
++) {
914 uint64_t off
= _getSizedInt(bytesptr
, trail
._offsetIntSize
);
915 if (maxOffset
< off
) FAIL_FALSE
;
916 bytesptr
+= trail
._offsetIntSize
;
919 bytesptr
= databytes
+ trail
._offsetTableOffset
+ trail
._topObject
* trail
._offsetIntSize
;
920 uint64_t off
= _getSizedInt(bytesptr
, trail
._offsetIntSize
);
921 if (off
< 8 || trail
._offsetTableOffset
<= off
) FAIL_FALSE
;
922 if (trailer
) *trailer
= trail
;
923 if (offset
) *offset
= off
;
924 if (marker
) *marker
= *(databytes
+ off
);
928 CF_INLINE Boolean
_plistIsPrimitive(CFPropertyListRef pl
) {
929 CFTypeID type
= CFGetTypeID(pl
);
930 if (dicttype
== type
|| arraytype
== type
|| settype
== type
|| osettype
== type
) FAIL_FALSE
;
934 CF_INLINE
bool _readInt(const uint8_t *ptr
, const uint8_t *end_byte_ptr
, uint64_t *bigint
, const uint8_t **newptr
) {
935 if (end_byte_ptr
< ptr
) FAIL_FALSE
;
936 uint8_t marker
= *ptr
++;
937 if ((marker
& 0xf0) != kCFBinaryPlistMarkerInt
) FAIL_FALSE
;
938 uint64_t cnt
= 1 << (marker
& 0x0f);
939 int32_t err
= CF_NO_ERROR
;
940 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
941 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
942 if (end_byte_ptr
< extent
) FAIL_FALSE
;
943 // integers are not required to be in the most compact possible representation, but only the last 64 bits are significant currently
944 *bigint
= _getSizedInt(ptr
, cnt
);
946 if (newptr
) *newptr
= ptr
;
950 // bytesptr points at a ref
951 CF_INLINE
uint64_t _getOffsetOfRefAt(const uint8_t *databytes
, const uint8_t *bytesptr
, const CFBinaryPlistTrailer
*trailer
) {
952 // *trailer contents are trusted, even for overflows -- was checked when the trailer was parsed;
953 // this pointer arithmetic and the multiplication was also already done once and checked,
954 // and the offsetTable was already validated.
955 const uint8_t *objectsFirstByte
= databytes
+ 8;
956 const uint8_t *offsetsFirstByte
= databytes
+ trailer
->_offsetTableOffset
;
957 if (bytesptr
< objectsFirstByte
|| offsetsFirstByte
- trailer
->_objectRefSize
< bytesptr
) FAIL_MAXOFFSET
;
959 uint64_t ref
= _getSizedInt(bytesptr
, trailer
->_objectRefSize
);
960 if (trailer
->_numObjects
<= ref
) FAIL_MAXOFFSET
;
962 bytesptr
= databytes
+ trailer
->_offsetTableOffset
+ ref
* trailer
->_offsetIntSize
;
963 uint64_t off
= _getSizedInt(bytesptr
, trailer
->_offsetIntSize
);
967 bool __CFBinaryPlistGetOffsetForValueFromArray2(const uint8_t *databytes
, uint64_t datalen
, uint64_t startOffset
, const CFBinaryPlistTrailer
*trailer
, CFIndex idx
, uint64_t *offset
, CFMutableDictionaryRef objects
) {
968 uint64_t objectsRangeStart
= 8, objectsRangeEnd
= trailer
->_offsetTableOffset
- 1;
969 if (startOffset
< objectsRangeStart
|| objectsRangeEnd
< startOffset
) FAIL_FALSE
;
970 const uint8_t *ptr
= databytes
+ startOffset
;
971 uint8_t marker
= *ptr
;
972 if ((marker
& 0xf0) != kCFBinaryPlistMarkerArray
) FAIL_FALSE
;
973 int32_t err
= CF_NO_ERROR
;
974 ptr
= check_ptr_add(ptr
, 1, &err
);
975 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
976 uint64_t cnt
= (marker
& 0x0f);
979 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
980 if (LONG_MAX
< bigint
) FAIL_FALSE
;
983 if (cnt
<= idx
) FAIL_FALSE
;
984 size_t byte_cnt
= check_size_t_mul(cnt
, trailer
->_objectRefSize
, &err
);
985 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
986 const uint8_t *extent
= check_ptr_add(ptr
, byte_cnt
, &err
) - 1;
987 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
988 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
989 uint64_t off
= _getOffsetOfRefAt(databytes
, ptr
+ idx
* trailer
->_objectRefSize
, trailer
);
990 if (offset
) *offset
= off
;
994 /* Get the offset for a value in a dictionary in a binary property list.
995 @param databytes A pointer to the start of the binary property list data.
996 @param datalen The length of the data.
997 @param startOffset The offset at which the dictionary starts.
998 @param trailer A pointer to a filled out trailer structure (use __CFBinaryPlistGetTopLevelInfo).
999 @param key A string key in the dictionary that should be searched for.
1000 @param koffset Will be filled out with the offset to the key in the data bytes.
1001 @param voffset Will be filled out with the offset to the value in the data bytes.
1002 @param unused Unused parameter.
1003 @param objects Used for caching objects. Should be a valid CFMutableDictionaryRef.
1004 @return True if the key was found, false otherwise.
1006 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
) {
1008 // Require a key that is a plist primitive
1009 if (!key
|| !_plistIsPrimitive(key
)) FAIL_FALSE
;
1011 // Require that startOffset is in the range of the object table
1012 uint64_t objectsRangeStart
= 8, objectsRangeEnd
= trailer
->_offsetTableOffset
- 1;
1013 if (startOffset
< objectsRangeStart
|| objectsRangeEnd
< startOffset
) FAIL_FALSE
;
1015 // ptr is the start of the dictionary we are reading
1016 const uint8_t *ptr
= databytes
+ startOffset
;
1018 // Check that the data pointer actually points to a dictionary
1019 uint8_t marker
= *ptr
;
1020 if ((marker
& 0xf0) != kCFBinaryPlistMarkerDict
) FAIL_FALSE
;
1022 // Get the number of objects in this dictionary
1023 int32_t err
= CF_NO_ERROR
;
1024 ptr
= check_ptr_add(ptr
, 1, &err
);
1025 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1026 uint64_t cnt
= (marker
& 0x0f);
1028 uint64_t bigint
= 0;
1029 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
1030 if (LONG_MAX
< bigint
) FAIL_FALSE
;
1034 // Total number of objects (keys + values) is cnt * 2
1035 cnt
= check_size_t_mul(cnt
, 2, &err
);
1036 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1037 size_t byte_cnt
= check_size_t_mul(cnt
, trailer
->_objectRefSize
, &err
);
1038 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1040 // Find the end of the dictionary
1041 const uint8_t *extent
= check_ptr_add(ptr
, byte_cnt
, &err
) - 1;
1042 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1044 // Check that we didn't overflow the size of the dictionary
1045 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1047 // For short keys (15 bytes or less) in ASCII form, we can do a quick comparison check
1048 // We get the pointer or copy the buffer here, outside of the loop
1049 CFIndex stringKeyLen
= -1;
1050 if (CFGetTypeID(key
) == stringtype
) {
1051 stringKeyLen
= CFStringGetLength((CFStringRef
)key
);
1054 // Find the object in the dictionary with this key
1056 uint64_t totalKeySize
= cnt
* trailer
->_objectRefSize
;
1058 Boolean match
= false;
1059 CFPropertyListRef keyInData
= NULL
;
1061 #define KEY_BUFF_SIZE 16
1062 char keyBuffer
[KEY_BUFF_SIZE
];
1063 const char *keyBufferPtr
= NULL
;
1065 // 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
1066 if (stringKeyLen
!= -1) {
1067 // Since we will only be comparing ASCII strings, we can attempt to get a pointer using MacRoman encoding
1068 // (this is cheaper than a copy)
1069 if (!(keyBufferPtr
= CFStringGetCStringPtr((CFStringRef
)key
, kCFStringEncodingMacRoman
)) && stringKeyLen
< KEY_BUFF_SIZE
) {
1070 CFStringGetCString((CFStringRef
)key
, keyBuffer
, KEY_BUFF_SIZE
, kCFStringEncodingMacRoman
);
1071 // The pointer should now point to our keyBuffer instead of the original string buffer, since we've copied it
1072 keyBufferPtr
= keyBuffer
;
1076 // Perform linear search of the keys
1077 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
1078 off
= _getOffsetOfRefAt(databytes
, ptr
, trailer
);
1079 marker
= *(databytes
+ off
);
1080 // 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.
1081 if (keyBufferPtr
&& (marker
& 0xf0) == kCFBinaryPlistMarkerASCIIString
) {
1082 CFIndex len
= marker
& 0x0f;
1083 // move past the marker
1084 const uint8_t *ptr2
= databytes
+ off
;
1086 ptr2
= check_ptr_add(ptr2
, 1, &err
);
1087 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1089 // 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.
1090 if (0xf == len
&& stringKeyLen
>= 0xf) {
1091 uint64_t bigint
= 0;
1092 if (!_readInt(ptr2
, databytes
+ objectsRangeEnd
, &bigint
, &ptr2
)) FAIL_FALSE
;
1093 if (LONG_MAX
< bigint
) FAIL_FALSE
;
1094 len
= (CFIndex
)bigint
;
1097 if (len
== stringKeyLen
) {
1099 extent
= check_ptr_add(ptr2
, len
, &err
);
1100 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1102 if (databytes
+ trailer
->_offsetTableOffset
<= extent
) FAIL_FALSE
;
1104 // Compare the key to this potential match
1105 if (memcmp(ptr2
, keyBufferPtr
, stringKeyLen
) == 0) {
1110 // temp object not saved in 'objects', because we don't know what allocator to use
1111 // (what allocator __CFBinaryPlistCreateObjectFiltered() or __CFBinaryPlistCreateObject()
1112 // will eventually be called with which results in that object)
1114 if (!__CFBinaryPlistCreateObjectFiltered(databytes
, datalen
, off
, trailer
, kCFAllocatorSystemDefault
, kCFPropertyListImmutable
, NULL
/*objects*/, NULL
, 0, NULL
, &keyInData
) || !_plistIsPrimitive(keyInData
)) {
1115 if (keyInData
) CFRelease(keyInData
);
1119 match
= CFEqual(key
, keyInData
);
1120 CFRelease(keyInData
);
1124 if (koffset
) *koffset
= off
;
1125 if (voffset
) *voffset
= _getOffsetOfRefAt(databytes
, ptr
+ totalKeySize
, trailer
);
1129 ptr
+= trailer
->_objectRefSize
;
1135 extern CFDictionaryRef
__CFDictionaryCreateTransfer(CFAllocatorRef allocator
, const void * *klist
, const void * *vlist
, CFIndex numValues
);
1136 extern CFSetRef
__CFSetCreateTransfer(CFAllocatorRef allocator
, const void * *klist
, CFIndex numValues
);
1137 extern CFArrayRef
__CFArrayCreateTransfer(CFAllocatorRef allocator
, const void * *klist
, CFIndex numValues
);
1138 __private_extern__
void __CFPropertyListCreateSplitKeypaths(CFAllocatorRef allocator
, CFSetRef currentKeys
, CFSetRef
*theseKeys
, CFSetRef
*nextKeys
);
1140 __private_extern__
bool __CFBinaryPlistCreateObjectFiltered(const uint8_t *databytes
, uint64_t datalen
, uint64_t startOffset
, const CFBinaryPlistTrailer
*trailer
, CFAllocatorRef allocator
, CFOptionFlags mutabilityOption
, CFMutableDictionaryRef objects
, CFMutableSetRef set
, CFIndex curDepth
, CFSetRef keyPaths
, CFPropertyListRef
*plist
) {
1143 *plist
= CFDictionaryGetValue(objects
, (const void *)(uintptr_t)startOffset
);
1145 // have to assume that '*plist' was previously created with same allocator that is now being passed in
1146 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRetain(*plist
);
1151 // at any one invocation of this function, set should contain the offsets in the "path" down to this object
1152 if (set
&& CFSetContainsValue(set
, (const void *)(uintptr_t)startOffset
)) return false;
1154 // databytes is trusted to be at least datalen bytes long
1155 // *trailer contents are trusted, even for overflows -- was checked when the trailer was parsed
1156 uint64_t objectsRangeStart
= 8, objectsRangeEnd
= trailer
->_offsetTableOffset
- 1;
1157 if (startOffset
< objectsRangeStart
|| objectsRangeEnd
< startOffset
) FAIL_FALSE
;
1160 CFPropertyListRef
*list
, buffer
[256];
1162 uint8_t marker
= *(databytes
+ startOffset
);
1163 switch (marker
& 0xf0) {
1164 case kCFBinaryPlistMarkerNull
:
1166 case kCFBinaryPlistMarkerNull
:
1169 case kCFBinaryPlistMarkerFalse
:
1170 *plist
= !_CFAllocatorIsGCRefZero(allocator
) ? CFRetain(kCFBooleanFalse
) : kCFBooleanFalse
;
1172 case kCFBinaryPlistMarkerTrue
:
1173 *plist
= !_CFAllocatorIsGCRefZero(allocator
) ? CFRetain(kCFBooleanTrue
) : kCFBooleanTrue
;
1177 case kCFBinaryPlistMarkerInt
:
1179 const uint8_t *ptr
= (databytes
+ startOffset
);
1180 int32_t err
= CF_NO_ERROR
;
1181 ptr
= check_ptr_add(ptr
, 1, &err
);
1182 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1183 uint64_t cnt
= 1 << (marker
& 0x0f);
1184 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
1185 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1186 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1187 if (16 < cnt
) FAIL_FALSE
;
1188 // in format version '00', 1, 2, and 4-byte integers have to be interpreted as unsigned,
1189 // whereas 8-byte integers are signed (and 16-byte when available)
1190 // negative 1, 2, 4-byte integers are always emitted as 8 bytes in format '00'
1191 // integers are not required to be in the most compact possible representation, but only the last 64 bits are significant currently
1192 uint64_t bigint
= _getSizedInt(ptr
, cnt
);
1195 CFSInt128Struct val
;
1198 *plist
= CFNumberCreate(allocator
, kCFNumberSInt128Type
, &val
);
1200 *plist
= CFNumberCreate(allocator
, kCFNumberSInt64Type
, &bigint
);
1202 // these are always immutable
1203 if (objects
&& *plist
) {
1204 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1206 return (*plist
) ? true : false;
1208 case kCFBinaryPlistMarkerReal
:
1209 switch (marker
& 0x0f) {
1211 const uint8_t *ptr
= (databytes
+ startOffset
);
1212 int32_t err
= CF_NO_ERROR
;
1213 ptr
= check_ptr_add(ptr
, 1, &err
);
1214 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1215 const uint8_t *extent
= check_ptr_add(ptr
, 4, &err
) - 1;
1216 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1217 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1218 CFSwappedFloat32 swapped32
;
1219 memmove(&swapped32
, ptr
, 4);
1220 float f
= CFConvertFloat32SwappedToHost(swapped32
);
1221 *plist
= CFNumberCreate(allocator
, kCFNumberFloat32Type
, &f
);
1222 // these are always immutable
1223 if (objects
&& *plist
) {
1224 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1226 return (*plist
) ? true : false;
1229 const uint8_t *ptr
= (databytes
+ startOffset
);
1230 int32_t err
= CF_NO_ERROR
;
1231 ptr
= check_ptr_add(ptr
, 1, &err
);
1232 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1233 const uint8_t *extent
= check_ptr_add(ptr
, 8, &err
) - 1;
1234 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1235 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1236 CFSwappedFloat64 swapped64
;
1237 memmove(&swapped64
, ptr
, 8);
1238 double d
= CFConvertFloat64SwappedToHost(swapped64
);
1239 *plist
= CFNumberCreate(allocator
, kCFNumberFloat64Type
, &d
);
1240 // these are always immutable
1241 if (objects
&& *plist
) {
1242 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1244 return (*plist
) ? true : false;
1248 case kCFBinaryPlistMarkerDate
& 0xf0:
1250 case kCFBinaryPlistMarkerDate
: {
1251 const uint8_t *ptr
= (databytes
+ startOffset
);
1252 int32_t err
= CF_NO_ERROR
;
1253 ptr
= check_ptr_add(ptr
, 1, &err
);
1254 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1255 const uint8_t *extent
= check_ptr_add(ptr
, 8, &err
) - 1;
1256 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1257 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1258 CFSwappedFloat64 swapped64
;
1259 memmove(&swapped64
, ptr
, 8);
1260 double d
= CFConvertFloat64SwappedToHost(swapped64
);
1261 *plist
= CFDateCreate(allocator
, d
);
1262 // these are always immutable
1263 if (objects
&& *plist
) {
1264 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1266 return (*plist
) ? true : false;
1270 case kCFBinaryPlistMarkerData
: {
1271 const uint8_t *ptr
= databytes
+ startOffset
;
1272 int32_t err
= CF_NO_ERROR
;
1273 ptr
= check_ptr_add(ptr
, 1, &err
);
1274 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1275 CFIndex cnt
= marker
& 0x0f;
1277 uint64_t bigint
= 0;
1278 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
1279 if (LONG_MAX
< bigint
) FAIL_FALSE
;
1280 cnt
= (CFIndex
)bigint
;
1282 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
1283 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1284 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1285 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
1286 *plist
= CFDataCreateMutable(allocator
, 0);
1287 if (*plist
) CFDataAppendBytes((CFMutableDataRef
)*plist
, ptr
, cnt
);
1289 *plist
= CFDataCreate(allocator
, ptr
, cnt
);
1291 if (objects
&& *plist
&& (mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
)) {
1292 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1294 return (*plist
) ? true : false;
1296 case kCFBinaryPlistMarkerASCIIString
: {
1297 const uint8_t *ptr
= databytes
+ startOffset
;
1298 int32_t err
= CF_NO_ERROR
;
1299 ptr
= check_ptr_add(ptr
, 1, &err
);
1300 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1301 CFIndex cnt
= marker
& 0x0f;
1303 uint64_t bigint
= 0;
1304 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
1305 if (LONG_MAX
< bigint
) FAIL_FALSE
;
1306 cnt
= (CFIndex
)bigint
;
1308 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
1309 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1310 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1311 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
1312 CFStringRef str
= CFStringCreateWithBytes(allocator
, ptr
, cnt
, kCFStringEncodingASCII
, false);
1313 *plist
= str
? CFStringCreateMutableCopy(allocator
, 0, str
) : NULL
;
1314 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(str
);
1316 *plist
= CFStringCreateWithBytes(allocator
, ptr
, cnt
, kCFStringEncodingASCII
, false);
1318 if (objects
&& *plist
&& (mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
)) {
1319 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1321 return (*plist
) ? true : false;
1323 case kCFBinaryPlistMarkerUnicode16String
: {
1324 const uint8_t *ptr
= databytes
+ startOffset
;
1325 int32_t err
= CF_NO_ERROR
;
1326 ptr
= check_ptr_add(ptr
, 1, &err
);
1327 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1328 CFIndex cnt
= marker
& 0x0f;
1330 uint64_t bigint
= 0;
1331 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
1332 if (LONG_MAX
< bigint
) FAIL_FALSE
;
1333 cnt
= (CFIndex
)bigint
;
1335 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
1336 extent
= check_ptr_add(extent
, cnt
, &err
); // 2 bytes per character
1337 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1338 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1339 size_t byte_cnt
= check_size_t_mul(cnt
, sizeof(UniChar
), &err
);
1340 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1341 UniChar
*chars
= (UniChar
*)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero
, byte_cnt
, 0);
1342 if (!chars
) FAIL_FALSE
;
1343 memmove(chars
, ptr
, byte_cnt
);
1344 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
1345 chars
[idx
] = CFSwapInt16BigToHost(chars
[idx
]);
1347 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
1348 CFStringRef str
= CFStringCreateWithCharacters(allocator
, chars
, cnt
);
1349 *plist
= str
? CFStringCreateMutableCopy(allocator
, 0, str
) : NULL
;
1350 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(str
);
1352 *plist
= CFStringCreateWithCharacters(allocator
, chars
, cnt
);
1354 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero
, chars
);
1355 if (objects
&& *plist
&& (mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
)) {
1356 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1358 return (*plist
) ? true : false;
1360 case kCFBinaryPlistMarkerUID
: {
1361 const uint8_t *ptr
= databytes
+ startOffset
;
1362 int32_t err
= CF_NO_ERROR
;
1363 ptr
= check_ptr_add(ptr
, 1, &err
);
1364 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1365 CFIndex cnt
= (marker
& 0x0f) + 1;
1366 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
1367 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1368 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1369 // uids are not required to be in the most compact possible representation, but only the last 64 bits are significant currently
1370 uint64_t bigint
= _getSizedInt(ptr
, cnt
);
1372 if (UINT32_MAX
< bigint
) FAIL_FALSE
;
1373 *plist
= _CFKeyedArchiverUIDCreate(allocator
, (uint32_t)bigint
);
1374 // these are always immutable
1375 if (objects
&& *plist
) {
1376 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1378 return (*plist
) ? true : false;
1380 case kCFBinaryPlistMarkerArray
:
1381 case kCFBinaryPlistMarkerSet
: {
1382 const uint8_t *ptr
= databytes
+ startOffset
;
1383 int32_t err
= CF_NO_ERROR
;
1384 ptr
= check_ptr_add(ptr
, 1, &err
);
1385 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1386 CFIndex arrayCount
= marker
& 0x0f;
1387 if (0xf == arrayCount
) {
1388 uint64_t bigint
= 0;
1389 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
1390 if (LONG_MAX
< bigint
) FAIL_FALSE
;
1391 arrayCount
= (CFIndex
)bigint
;
1393 size_t byte_cnt
= check_size_t_mul(arrayCount
, trailer
->_objectRefSize
, &err
);
1394 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1395 const uint8_t *extent
= check_ptr_add(ptr
, byte_cnt
, &err
) - 1;
1396 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1397 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1398 byte_cnt
= check_size_t_mul(arrayCount
, sizeof(CFPropertyListRef
), &err
);
1399 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1400 list
= (arrayCount
<= 256) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero
, byte_cnt
, __kCFAllocatorGCScannedMemory
);
1401 if (!list
) FAIL_FALSE
;
1402 Boolean madeSet
= false;
1403 if (!set
&& 15 < curDepth
) {
1404 set
= CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
);
1405 madeSet
= set
? true : false;
1408 if (set
) CFSetAddValue(set
, (const void *)(uintptr_t)startOffset
);
1409 if ((marker
& 0xf0) == kCFBinaryPlistMarkerArray
&& keyPaths
) {
1410 // Only get a subset of this array
1411 CFSetRef theseKeys
, nextKeys
;
1412 __CFPropertyListCreateSplitKeypaths(kCFAllocatorSystemDefault
, keyPaths
, &theseKeys
, &nextKeys
);
1414 Boolean success
= true;
1415 CFMutableArrayRef array
= CFArrayCreateMutable(allocator
, CFSetGetCount(theseKeys
), &kCFTypeArrayCallBacks
);
1417 CFTypeRef
*keys
= (CFTypeRef
*)malloc(CFSetGetCount(theseKeys
) * sizeof(CFTypeRef
));
1418 CFSetGetValues(theseKeys
, keys
);
1419 for (CFIndex i
= 0; i
< CFSetGetCount(theseKeys
); i
++) {
1420 CFStringRef key
= (CFStringRef
)keys
[i
];
1421 SInt32 intValue
= CFStringGetIntValue(key
);
1422 if ((intValue
== 0 && CFStringCompare(CFSTR("0"), key
, 0) != kCFCompareEqualTo
) || intValue
== INT_MAX
|| intValue
== INT_MIN
|| intValue
< 0) {
1423 // skip, doesn't appear to be a proper integer
1425 uint64_t valueOffset
;
1426 Boolean found
= __CFBinaryPlistGetOffsetForValueFromArray2(databytes
, datalen
, startOffset
, trailer
, (CFIndex
)intValue
, &valueOffset
, objects
);
1428 CFPropertyListRef result
;
1429 success
= __CFBinaryPlistCreateObjectFiltered(databytes
, datalen
, valueOffset
, trailer
, allocator
, mutabilityOption
, objects
, set
, curDepth
+ 1, nextKeys
, &result
);
1431 CFArrayAppendValue(array
, result
);
1441 CFRelease(theseKeys
);
1443 if (nextKeys
) CFRelease(nextKeys
);
1446 if (!(mutabilityOption
== kCFPropertyListMutableContainers
|| mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
)) {
1448 *plist
= CFArrayCreateCopy(allocator
, array
);
1449 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(array
);
1454 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(array
);
1457 for (CFIndex idx
= 0; idx
< arrayCount
; idx
++) {
1458 CFPropertyListRef pl
;
1459 off
= _getOffsetOfRefAt(databytes
, ptr
, trailer
);
1460 if (!__CFBinaryPlistCreateObjectFiltered(databytes
, datalen
, off
, trailer
, allocator
, mutabilityOption
, objects
, set
, curDepth
+ 1, NULL
, &pl
)) {
1462 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(list
[idx
]);
1464 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero
, list
);
1467 __CFAssignWithWriteBarrier((void **)list
+ idx
, (void *)pl
);
1468 ptr
+= trailer
->_objectRefSize
;
1470 if ((marker
& 0xf0) == kCFBinaryPlistMarkerArray
) {
1471 if (mutabilityOption
!= kCFPropertyListImmutable
) {
1472 *plist
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
1473 CFArrayReplaceValues((CFMutableArrayRef
)*plist
, CFRangeMake(0, 0), list
, arrayCount
);
1474 for (CFIndex idx
= 0; idx
< arrayCount
; idx
++) {
1475 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(list
[idx
]);
1478 if (!kCFUseCollectableAllocator
) {
1479 *plist
= __CFArrayCreateTransfer(allocator
, list
, arrayCount
);
1481 *plist
= CFArrayCreate(allocator
, list
, arrayCount
, &kCFTypeArrayCallBacks
);
1482 for (CFIndex idx
= 0; idx
< arrayCount
; idx
++) {
1483 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(list
[idx
]);
1488 if (mutabilityOption
!= kCFPropertyListImmutable
) {
1489 *plist
= CFSetCreateMutable(allocator
, 0, &kCFTypeSetCallBacks
);
1490 for (CFIndex idx
= 0; idx
< arrayCount
; idx
++) {
1491 CFSetAddValue((CFMutableSetRef
)*plist
, list
[idx
]);
1493 for (CFIndex idx
= 0; idx
< arrayCount
; idx
++) {
1494 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(list
[idx
]);
1497 if (!kCFUseCollectableAllocator
) {
1498 *plist
= __CFSetCreateTransfer(allocator
, list
, arrayCount
);
1500 *plist
= CFSetCreate(allocator
, list
, arrayCount
, &kCFTypeSetCallBacks
);
1501 for (CFIndex idx
= 0; idx
< arrayCount
; idx
++) {
1502 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(list
[idx
]);
1508 if (set
) CFSetRemoveValue(set
, (const void *)(uintptr_t)startOffset
);
1513 if (objects
&& *plist
&& (mutabilityOption
== kCFPropertyListImmutable
)) {
1514 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1516 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero
, list
);
1517 return (*plist
) ? true : false;
1519 case kCFBinaryPlistMarkerDict
: {
1520 const uint8_t *ptr
= databytes
+ startOffset
;
1521 int32_t err
= CF_NO_ERROR
;
1522 ptr
= check_ptr_add(ptr
, 1, &err
);
1523 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1524 CFIndex dictionaryCount
= marker
& 0x0f;
1525 if (0xf == dictionaryCount
) {
1526 uint64_t bigint
= 0;
1527 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
1528 if (LONG_MAX
< bigint
) FAIL_FALSE
;
1529 dictionaryCount
= (CFIndex
)bigint
;
1531 dictionaryCount
= check_size_t_mul(dictionaryCount
, 2, &err
);
1532 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1533 size_t byte_cnt
= check_size_t_mul(dictionaryCount
, trailer
->_objectRefSize
, &err
);
1534 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1535 const uint8_t *extent
= check_ptr_add(ptr
, byte_cnt
, &err
) - 1;
1536 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1537 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1538 byte_cnt
= check_size_t_mul(dictionaryCount
, sizeof(CFPropertyListRef
), &err
);
1539 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1540 list
= (dictionaryCount
<= 256) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero
, byte_cnt
, __kCFAllocatorGCScannedMemory
);
1541 if (!list
) FAIL_FALSE
;
1542 Boolean madeSet
= false;
1543 if (!set
&& 15 < curDepth
) {
1544 set
= CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
);
1545 madeSet
= set
? true : false;
1548 if (set
) CFSetAddValue(set
, (const void *)(uintptr_t)startOffset
);
1550 // Only get a subset of this dictionary
1551 CFSetRef theseKeys
, nextKeys
;
1552 __CFPropertyListCreateSplitKeypaths(kCFAllocatorSystemDefault
, keyPaths
, &theseKeys
, &nextKeys
);
1554 Boolean success
= true;
1555 CFMutableDictionaryRef dict
= CFDictionaryCreateMutable(allocator
, CFSetGetCount(theseKeys
), &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1557 CFTypeRef
*keys
= (CFTypeRef
*)malloc(CFSetGetCount(theseKeys
) * sizeof(CFTypeRef
));
1558 CFSetGetValues(theseKeys
, keys
);
1559 for (CFIndex i
= 0; i
< CFSetGetCount(theseKeys
); i
++) {
1560 CFStringRef key
= (CFStringRef
)keys
[i
];
1561 uint64_t keyOffset
, valueOffset
;
1562 Boolean found
= __CFBinaryPlistGetOffsetForValueFromDictionary3(databytes
, datalen
, startOffset
, trailer
, key
, &keyOffset
, &valueOffset
, false, objects
);
1564 CFPropertyListRef result
;
1565 success
= __CFBinaryPlistCreateObjectFiltered(databytes
, datalen
, valueOffset
, trailer
, allocator
, mutabilityOption
, objects
, set
, curDepth
+ 1, nextKeys
, &result
);
1567 CFDictionarySetValue(dict
, key
, result
);
1576 CFRelease(theseKeys
);
1578 if (nextKeys
) CFRelease(nextKeys
);
1581 if (!(mutabilityOption
== kCFPropertyListMutableContainers
|| mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
)) {
1583 *plist
= CFDictionaryCreateCopy(allocator
, dict
);
1584 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(dict
);
1589 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(dict
);
1592 for (CFIndex idx
= 0; idx
< dictionaryCount
; idx
++) {
1593 CFPropertyListRef pl
= NULL
;
1594 off
= _getOffsetOfRefAt(databytes
, ptr
, trailer
);
1595 if (!__CFBinaryPlistCreateObjectFiltered(databytes
, datalen
, off
, trailer
, allocator
, mutabilityOption
, objects
, set
, curDepth
+ 1, NULL
, &pl
) || (idx
< dictionaryCount
/ 2 && !_plistIsPrimitive(pl
))) {
1596 if (pl
&& !_CFAllocatorIsGCRefZero(allocator
)) CFRelease(pl
);
1598 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(list
[idx
]);
1600 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero
, list
);
1603 __CFAssignWithWriteBarrier((void **)list
+ idx
, (void *)pl
);
1604 ptr
+= trailer
->_objectRefSize
;
1606 if (mutabilityOption
!= kCFPropertyListImmutable
) {
1607 *plist
= CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1608 for (CFIndex idx
= 0; idx
< dictionaryCount
/ 2; idx
++) {
1609 CFDictionaryAddValue((CFMutableDictionaryRef
)*plist
, list
[idx
], list
[idx
+ dictionaryCount
/ 2]);
1611 for (CFIndex idx
= 0; idx
< dictionaryCount
; idx
++) {
1612 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(list
[idx
]);
1615 if (!kCFUseCollectableAllocator
) {
1616 *plist
= __CFDictionaryCreateTransfer(allocator
, list
, list
+ dictionaryCount
/ 2, dictionaryCount
/ 2);
1618 *plist
= CFDictionaryCreate(allocator
, list
, list
+ dictionaryCount
/ 2, dictionaryCount
/ 2, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1619 for (CFIndex idx
= 0; idx
< dictionaryCount
; idx
++) {
1620 if (!_CFAllocatorIsGCRefZero(allocator
)) CFRelease(list
[idx
]);
1625 if (set
) CFSetRemoveValue(set
, (const void *)(uintptr_t)startOffset
);
1630 if (objects
&& *plist
&& (mutabilityOption
== kCFPropertyListImmutable
)) {
1631 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1633 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero
, list
);
1634 return (*plist
) ? true : false;
1640 bool __CFBinaryPlistCreateObject(const uint8_t *databytes
, uint64_t datalen
, uint64_t startOffset
, const CFBinaryPlistTrailer
*trailer
, CFAllocatorRef allocator
, CFOptionFlags mutabilityOption
, CFMutableDictionaryRef objects
, CFPropertyListRef
*plist
) {
1641 // for compatibility with Foundation's use, need to leave this here
1642 return __CFBinaryPlistCreateObjectFiltered(databytes
, datalen
, startOffset
, trailer
, allocator
, mutabilityOption
, objects
, NULL
, 0, NULL
, plist
);
1645 __private_extern__
bool __CFTryParseBinaryPlist(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags option
, CFPropertyListRef
*plist
, CFStringRef
*errorString
) {
1647 CFBinaryPlistTrailer trailer
;
1649 const uint8_t *databytes
= CFDataGetBytePtr(data
);
1650 uint64_t datalen
= CFDataGetLength(data
);
1652 if (8 <= datalen
&& __CFBinaryPlistGetTopLevelInfo(databytes
, datalen
, &marker
, &offset
, &trailer
)) {
1653 // FALSE: We know for binary plist parsing that the result objects will be retained
1654 // by their containing collections as the parsing proceeds, so we do not need
1655 // to use retaining callbacks for the objects map in this case. WHY: the file might
1656 // be malformed and contain hash-equal keys for the same dictionary (for example)
1657 // and the later key will cause the previous one to be released when we set the second
1658 // in the dictionary.
1659 CFMutableDictionaryRef objects
= CFDictionaryCreateMutable(kCFAllocatorSystemDefaultGCRefZero
, 0, NULL
, &kCFTypeDictionaryValueCallBacks
);
1660 _CFDictionarySetCapacity(objects
, trailer
._numObjects
);
1661 CFPropertyListRef pl
= NULL
;
1663 if (__CFBinaryPlistCreateObjectFiltered(databytes
, datalen
, offset
, &trailer
, allocator
, option
, objects
, NULL
, 0, NULL
, &pl
)) {
1664 if (plist
) *plist
= pl
;
1666 if (plist
) *plist
= NULL
;
1667 if (errorString
) *errorString
= (CFStringRef
)CFRetain(CFSTR("binary data is corrupt"));
1670 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero
)) CFRelease(objects
);
1677 static CFPropertyListRef
__CFBinaryPlistCreateObject15(const uint8_t *databytes
, uint64_t datalen
, const uint8_t **ptr
) {
1678 const uint8_t *objectsRangeStart
= databytes
+ 22, *objectsRangeEnd
= databytes
+ datalen
- 1;
1679 if (*ptr
< objectsRangeStart
|| objectsRangeEnd
< *ptr
) return NULL
;
1680 uint8_t marker
= **ptr
;
1681 int32_t err
= CF_NO_ERROR
;
1682 *ptr
= check_ptr_add(*ptr
, 1, &err
);
1683 if (CF_NO_ERROR
!= err
) return NULL
;
1684 switch (marker
& 0xf0) {
1687 case kCFBinaryPlistMarkerNull
:
1689 case kCFBinaryPlistMarkerFalse
:
1690 return kCFBooleanFalse
;
1691 case kCFBinaryPlistMarkerTrue
:
1692 return kCFBooleanTrue
;
1694 CFStringRef string
= (CFStringRef
)__CFBinaryPlistCreateObject15(databytes
, datalen
, ptr
);
1698 CFURLRef result
= CFURLCreateWithString(kCFAllocatorSystemDefault
, string
, NULL
);
1703 CFURLRef base
= (CFURLRef
)__CFBinaryPlistCreateObject15(databytes
, datalen
, ptr
);
1704 if (!base
) return NULL
;
1705 CFStringRef string
= (CFStringRef
)__CFBinaryPlistCreateObject15(databytes
, datalen
, ptr
);
1710 CFURLRef result
= CFURLCreateWithString(kCFAllocatorSystemDefault
, string
, base
);
1716 int32_t err
= CF_NO_ERROR
;
1717 const uint8_t *extent
= check_ptr_add(*ptr
, 16, &err
) - 1;
1718 if (CF_NO_ERROR
!= err
) return NULL
;
1719 if (objectsRangeEnd
< extent
) return NULL
;
1721 memmove(&uuid
, *ptr
, 16);
1723 return CFUUIDCreateFromUUIDBytes(kCFAllocatorSystemDefault
, uuid
);
1729 case kCFBinaryPlistMarkerInt
: {
1730 int32_t err
= CF_NO_ERROR
;
1731 uint64_t cnt
= 1 << (marker
& 0x0f);
1732 const uint8_t *extent
= check_ptr_add(*ptr
, cnt
, &err
) - 1;
1733 if (CF_NO_ERROR
!= err
) return NULL
;
1734 if (objectsRangeEnd
< extent
) return NULL
;
1735 if (16 < cnt
) return NULL
;
1736 // in format version '15', 1, 2, and 4-byte integers have to be interpreted as unsigned,
1737 // whereas 8-byte integers are signed (and 16-byte when available)
1738 // negative 1, 2, 4-byte integers are always emitted as 8 bytes in format '15'
1739 // integers are not required to be in the most compact possible representation, but only the last 64 bits are significant currently
1740 uint64_t bigint
= _getSizedInt(*ptr
, cnt
);
1742 CFPropertyListRef plist
= NULL
;
1744 CFSInt128Struct val
;
1747 plist
= CFNumberCreate(kCFAllocatorSystemDefault
, kCFNumberSInt128Type
, &val
);
1749 plist
= CFNumberCreate(kCFAllocatorSystemDefault
, kCFNumberSInt64Type
, &bigint
);
1753 case kCFBinaryPlistMarkerReal
:
1754 switch (marker
& 0x0f) {
1756 int32_t err
= CF_NO_ERROR
;
1757 const uint8_t *extent
= check_ptr_add(*ptr
, 4, &err
) - 1;
1758 if (CF_NO_ERROR
!= err
) return NULL
;
1759 if (objectsRangeEnd
< extent
) return NULL
;
1760 CFSwappedFloat32 swapped32
;
1761 memmove(&swapped32
, *ptr
, 4);
1763 float f
= CFConvertFloat32SwappedToHost(swapped32
);
1764 CFPropertyListRef plist
= CFNumberCreate(kCFAllocatorSystemDefault
, kCFNumberFloat32Type
, &f
);
1768 int32_t err
= CF_NO_ERROR
;
1769 const uint8_t *extent
= check_ptr_add(*ptr
, 8, &err
) - 1;
1770 if (CF_NO_ERROR
!= err
) return NULL
;
1771 if (objectsRangeEnd
< extent
) return NULL
;
1772 CFSwappedFloat64 swapped64
;
1773 memmove(&swapped64
, *ptr
, 8);
1775 double d
= CFConvertFloat64SwappedToHost(swapped64
);
1776 CFPropertyListRef plist
= CFNumberCreate(kCFAllocatorSystemDefault
, kCFNumberFloat64Type
, &d
);
1781 case kCFBinaryPlistMarkerDate
& 0xf0:
1783 case kCFBinaryPlistMarkerDate
: {
1784 int32_t err
= CF_NO_ERROR
;
1785 const uint8_t *extent
= check_ptr_add(*ptr
, 8, &err
) - 1;
1786 if (CF_NO_ERROR
!= err
) return NULL
;
1787 if (objectsRangeEnd
< extent
) return NULL
;
1788 CFSwappedFloat64 swapped64
;
1789 memmove(&swapped64
, *ptr
, 8);
1791 double d
= CFConvertFloat64SwappedToHost(swapped64
);
1792 CFPropertyListRef plist
= CFDateCreate(kCFAllocatorSystemDefault
, d
);
1797 case kCFBinaryPlistMarkerData
: {
1798 int32_t err
= CF_NO_ERROR
;
1799 CFIndex cnt
= marker
& 0x0f;
1801 uint64_t bigint
= 0;
1802 if (!_readInt(*ptr
, objectsRangeEnd
, &bigint
, ptr
)) return NULL
;
1803 if (LONG_MAX
< bigint
) return NULL
;
1804 cnt
= (CFIndex
)bigint
;
1806 const uint8_t *extent
= check_ptr_add(*ptr
, cnt
, &err
) - 1;
1807 if (CF_NO_ERROR
!= err
) return NULL
;
1808 if (objectsRangeEnd
< extent
) return NULL
;
1809 CFPropertyListRef plist
= CFDataCreate(kCFAllocatorSystemDefault
, *ptr
, cnt
);
1813 case kCFBinaryPlistMarkerASCIIString
: {
1814 int32_t err
= CF_NO_ERROR
;
1815 CFIndex cnt
= marker
& 0x0f;
1817 uint64_t bigint
= 0;
1818 if (!_readInt(*ptr
, objectsRangeEnd
, &bigint
, ptr
)) return NULL
;
1819 if (LONG_MAX
< bigint
) return NULL
;
1820 cnt
= (CFIndex
)bigint
;
1822 const uint8_t *extent
= check_ptr_add(*ptr
, cnt
, &err
) - 1;
1823 if (CF_NO_ERROR
!= err
) return NULL
;
1824 if (objectsRangeEnd
< extent
) return NULL
;
1825 CFPropertyListRef plist
= CFStringCreateWithBytes(kCFAllocatorSystemDefault
, *ptr
, cnt
, kCFStringEncodingASCII
, false);
1829 case kCFBinaryPlistMarkerUnicode16String
: {
1830 int32_t err
= CF_NO_ERROR
;
1831 CFIndex cnt
= marker
& 0x0f;
1833 uint64_t bigint
= 0;
1834 if (!_readInt(*ptr
, objectsRangeEnd
, &bigint
, ptr
)) return NULL
;
1835 if (LONG_MAX
< bigint
) return NULL
;
1836 cnt
= (CFIndex
)bigint
;
1838 const uint8_t *extent
= check_ptr_add(*ptr
, cnt
, &err
) - 1;
1839 extent
= check_ptr_add(extent
, cnt
, &err
); // 2 bytes per character
1840 if (CF_NO_ERROR
!= err
) return NULL
;
1841 if (objectsRangeEnd
< extent
) return NULL
;
1842 size_t byte_cnt
= check_size_t_mul(cnt
, sizeof(UniChar
), &err
);
1843 if (CF_NO_ERROR
!= err
) return NULL
;
1844 UniChar
*chars
= (UniChar
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, byte_cnt
, 0);
1845 if (!chars
) return NULL
;
1846 memmove(chars
, *ptr
, byte_cnt
);
1847 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
1848 chars
[idx
] = CFSwapInt16BigToHost(chars
[idx
]);
1850 CFPropertyListRef plist
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, chars
, cnt
);
1851 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, chars
);
1855 case kCFBinaryPlistMarkerArray
:
1856 case 0xb0: // ordset
1857 case kCFBinaryPlistMarkerSet
:
1858 case kCFBinaryPlistMarkerDict
: {
1859 int32_t err
= CF_NO_ERROR
;
1860 CFIndex cnt
= marker
& 0x0f;
1862 uint64_t bigint
= 0;
1863 if (!_readInt(*ptr
, objectsRangeEnd
, &bigint
, ptr
)) return NULL
;
1864 if (LONG_MAX
< bigint
) return NULL
;
1865 cnt
= (CFIndex
)bigint
;
1867 if ((marker
& 0xf0) == kCFBinaryPlistMarkerDict
) {
1868 cnt
= check_size_t_mul(cnt
, 2, &err
);
1869 if (CF_NO_ERROR
!= err
) return NULL
;
1871 size_t byte_cnt
= check_size_t_mul(cnt
, sizeof(CFPropertyListRef
), &err
); // check now for a later overflow
1872 if (CF_NO_ERROR
!= err
) return NULL
;
1873 CFPropertyListRef
*list
, buffer
[256];
1874 list
= (cnt
<= 256) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, byte_cnt
, 0);
1875 if (!list
) return NULL
;
1877 CFMutableArrayRef tmparray
= CFArrayCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeArrayCallBacks
);
1878 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
1879 CFPropertyListRef pl
= __CFBinaryPlistCreateObject15(databytes
, datalen
, ptr
);
1881 CFRelease(tmparray
);
1884 if ((marker
& 0xf0) == kCFBinaryPlistMarkerDict
) {
1885 if (idx
< cnt
/ 2 && !_plistIsPrimitive(pl
)) {
1887 CFRelease(tmparray
);
1891 CFArrayAppendValue(tmparray
, pl
);
1892 // CFRelease(pl); // don't release pl here, we're going to transfer the retain to the ultimate collection owner
1894 CFArrayGetValues(tmparray
, CFRangeMake(0, cnt
), list
);
1895 CFPropertyListRef plist
= NULL
;
1896 if ((marker
& 0xf0) == kCFBinaryPlistMarkerArray
) {
1897 plist
= __CFArrayCreateTransfer(kCFAllocatorSystemDefault
, list
, cnt
);
1898 } else if ((marker
& 0xf0) == 0xb0) {
1899 plist
= NULL
; // Not actually implemented
1900 // leaks contents of tmparray, but this path shouldn't be exercised anyway
1901 } else if ((marker
& 0xf0) == kCFBinaryPlistMarkerSet
) {
1902 plist
= __CFSetCreateTransfer(kCFAllocatorSystemDefault
, list
, cnt
);
1904 plist
= __CFDictionaryCreateTransfer(kCFAllocatorSystemDefault
, list
, list
+ cnt
/ 2, cnt
/ 2);
1906 CFRelease(tmparray
);
1907 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
1916 // *error is currently never set
1917 __private_extern__ CFPropertyListRef
__CFBinaryPlistCreate15(CFDataRef data
, CFErrorRef
*error
) {
1919 const uint8_t *databytes
= CFDataGetBytePtr(data
);
1920 uint64_t datalen
= CFDataGetLength(data
);
1921 if (23 <= datalen
) { // header is 22 bytes, plus 1 byte minimum for smallest object
1922 const uint8_t *ptr
= databytes
;
1923 if (0 != memcmp((uint8_t *)"bplist15", ptr
, 8)) return NULL
;
1925 if (*ptr
!= (kCFBinaryPlistMarkerInt
| 3)) return NULL
;
1927 uint64_t swapped_len
;
1928 memmove(&swapped_len
, ptr
, 8);
1929 uint64_t bytelen
= CFSwapInt64BigToHost(swapped_len
);
1930 if (bytelen
!= datalen
) return NULL
;
1932 if (*ptr
!= (kCFBinaryPlistMarkerInt
| 2)) return NULL
;
1933 ptr
+= 5; // skip crc
1934 CFPropertyListRef pl
= __CFBinaryPlistCreateObject15(databytes
, datalen
, &ptr
);
1935 // ptr should equal (databytes+datalen) if there is no junk at the end of top-level object