2 * Copyright (c) 2008 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 Copyright 2000-2002, Apple, Inc. All rights reserved.
25 Responsibility: Christopher Kane
29 #include <CoreFoundation/CFString.h>
30 #include <CoreFoundation/CFNumber.h>
31 #include <CoreFoundation/CFDate.h>
32 #include <CoreFoundation/CFData.h>
33 #include <CoreFoundation/CFArray.h>
34 #include <CoreFoundation/CFDictionary.h>
35 #include <CoreFoundation/CFSet.h>
36 #include <CoreFoundation/CFPropertyList.h>
37 #include <CoreFoundation/CFByteOrder.h>
38 #include <CoreFoundation/CFRuntime.h>
42 #include "CFInternal.h"
50 kCFNumberSInt128Type
= 17
53 extern CFNumberType
_CFNumberGetType2(CFNumberRef number
);
57 CF_OVERFLOW_ERROR
= (1 << 0),
60 CF_INLINE
uint32_t __check_uint32_add_unsigned_unsigned(uint32_t x
, uint32_t y
, int32_t* err
) {
61 if((UINT_MAX
- y
) < x
)
62 *err
= *err
| CF_OVERFLOW_ERROR
;
66 CF_INLINE
uint64_t __check_uint64_add_unsigned_unsigned(uint64_t x
, uint64_t y
, int32_t* err
) {
67 if((ULLONG_MAX
- y
) < x
)
68 *err
= *err
| CF_OVERFLOW_ERROR
;
72 CF_INLINE
uint32_t __check_uint32_mul_unsigned_unsigned(uint32_t x
, uint32_t y
, int32_t* err
) {
73 uint64_t tmp
= (uint64_t) x
* (uint64_t) y
;
74 /* If any of the upper 32 bits touched, overflow */
75 if(tmp
& 0xffffffff00000000ULL
)
76 *err
= *err
| CF_OVERFLOW_ERROR
;
77 return (uint32_t) tmp
;
80 CF_INLINE
uint64_t __check_uint64_mul_unsigned_unsigned(uint64_t x
, uint64_t y
, int32_t* err
) {
83 *err
= *err
| CF_OVERFLOW_ERROR
;
88 #define check_ptr_add(p, a, err) (const uint8_t *)__check_uint64_add_unsigned_unsigned((uintptr_t)p, (uintptr_t)a, err)
89 #define check_size_t_mul(b, a, err) (size_t)__check_uint64_mul_unsigned_unsigned((size_t)b, (size_t)a, err)
91 #define check_ptr_add(p, a, err) (const uint8_t *)__check_uint32_add_unsigned_unsigned((uintptr_t)p, (uintptr_t)a, err)
92 #define check_size_t_mul(b, a, err) (size_t)__check_uint32_mul_unsigned_unsigned((size_t)b, (size_t)a, err)
96 CF_INLINE CFTypeID
__CFGenericTypeID_genericobj_inline(const void *cf
) {
97 CFTypeID typeID
= (*(uint32_t *)(((CFRuntimeBase
*)cf
)->_cfinfo
) >> 8) & 0xFFFF;
98 return CF_IS_OBJC(typeID
, cf
) ? CFGetTypeID(cf
) : typeID
;
101 struct __CFKeyedArchiverUID
{
106 static CFStringRef
__CFKeyedArchiverUIDCopyDescription(CFTypeRef cf
) {
107 CFKeyedArchiverUIDRef uid
= (CFKeyedArchiverUIDRef
)cf
;
108 return CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("<CFKeyedArchiverUID %p [%p]>{value = %u}"), cf
, CFGetAllocator(cf
), uid
->_value
);
111 static CFStringRef
__CFKeyedArchiverUIDCopyFormattingDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
112 CFKeyedArchiverUIDRef uid
= (CFKeyedArchiverUIDRef
)cf
;
113 return CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("@%u@"), uid
->_value
);
116 static CFTypeID __kCFKeyedArchiverUIDTypeID
= _kCFRuntimeNotATypeID
;
118 static const CFRuntimeClass __CFKeyedArchiverUIDClass
= {
120 "CFKeyedArchiverUID",
124 NULL
, // equal -- pointer equality only
125 NULL
, // hash -- pointer hashing only
126 __CFKeyedArchiverUIDCopyFormattingDescription
,
127 __CFKeyedArchiverUIDCopyDescription
130 __private_extern__
void __CFKeyedArchiverUIDInitialize(void) {
131 __kCFKeyedArchiverUIDTypeID
= _CFRuntimeRegisterClass(&__CFKeyedArchiverUIDClass
);
134 CFTypeID
_CFKeyedArchiverUIDGetTypeID(void) {
135 return __kCFKeyedArchiverUIDTypeID
;
138 CFKeyedArchiverUIDRef
_CFKeyedArchiverUIDCreate(CFAllocatorRef allocator
, uint32_t value
) {
139 CFKeyedArchiverUIDRef uid
;
140 uid
= (CFKeyedArchiverUIDRef
)_CFRuntimeCreateInstance(allocator
, __kCFKeyedArchiverUIDTypeID
, sizeof(struct __CFKeyedArchiverUID
) - sizeof(CFRuntimeBase
), NULL
);
144 ((struct __CFKeyedArchiverUID
*)uid
)->_value
= value
;
149 uint32_t _CFKeyedArchiverUIDGetValue(CFKeyedArchiverUIDRef uid
) {
160 uint8_t buffer
[8192 - 32];
161 } __CFBinaryPlistWriteBuffer
;
163 static void writeBytes(__CFBinaryPlistWriteBuffer
*buf
, const UInt8
*bytes
, CFIndex length
) {
164 if (0 == length
) return;
165 if (buf
->error
) return;
166 if (buf
->streamIsData
) {
167 CFDataAppendBytes((CFMutableDataRef
)buf
->stream
, bytes
, length
);
168 buf
->written
+= length
;
170 CFAssert(false, __kCFLogAssertion
, "Streams are not supported on this platform");
174 static void bufferWrite(__CFBinaryPlistWriteBuffer
*buf
, const uint8_t *buffer
, CFIndex count
) {
175 if (0 == count
) return;
176 if ((CFIndex
)sizeof(buf
->buffer
) <= count
) {
177 writeBytes(buf
, buf
->buffer
, buf
->used
);
179 writeBytes(buf
, buffer
, count
);
182 CFIndex copyLen
= __CFMin(count
, (CFIndex
)sizeof(buf
->buffer
) - buf
->used
);
183 memmove(buf
->buffer
+ buf
->used
, buffer
, copyLen
);
184 buf
->used
+= copyLen
;
185 if (sizeof(buf
->buffer
) == buf
->used
) {
186 writeBytes(buf
, buf
->buffer
, sizeof(buf
->buffer
));
187 memmove(buf
->buffer
, buffer
+ copyLen
, count
- copyLen
);
188 buf
->used
= count
- copyLen
;
192 static void bufferFlush(__CFBinaryPlistWriteBuffer
*buf
) {
193 writeBytes(buf
, buf
->buffer
, buf
->used
);
199 magic number ("bplist")
203 variable-sized objects
205 Object Formats (marker byte followed by additional info in some cases)
207 bool 0000 1000 // false
208 bool 0000 1001 // true
209 fill 0000 1111 // fill byte
210 int 0001 nnnn ... // # of bytes is 2^nnnn, big-endian bytes
211 real 0010 nnnn ... // # of bytes is 2^nnnn, big-endian bytes
212 date 0011 0011 ... // 8 byte float follows, big-endian bytes
213 data 0100 nnnn [int] ... // nnnn is number of bytes unless 1111 then int count follows, followed by bytes
214 string 0101 nnnn [int] ... // ASCII string, nnnn is # of chars, else 1111 then int count, then bytes
215 string 0110 nnnn [int] ... // Unicode string, nnnn is # of chars, else 1111 then int count, then big-endian 2-byte uint16_t
217 uid 1000 nnnn ... // nnnn+1 is # of bytes
219 array 1010 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows
221 set 1100 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows
222 dict 1101 nnnn [int] keyref* objref* // nnnn is count, unless '1111', then int count follows
227 list of ints, byte size of which is given in trailer
228 -- these are the byte offsets into the file
229 -- number of these is in the trailer
232 byte size of offset ints in offset table
233 byte size of object refs in arrays and dicts
234 number of offsets in offset table (also is number of objects)
235 element # in offset table which is top level object
241 static CFTypeID stringtype
= -1, datatype
= -1, numbertype
= -1, datetype
= -1;
242 static CFTypeID booltype
= -1, nulltype
= -1, dicttype
= -1, arraytype
= -1, settype
= -1;
244 static void _appendInt(__CFBinaryPlistWriteBuffer
*buf
, uint64_t bigint
) {
248 if (bigint
<= (uint64_t)0xff) {
250 marker
= kCFBinaryPlistMarkerInt
| 0;
251 } else if (bigint
<= (uint64_t)0xffff) {
253 marker
= kCFBinaryPlistMarkerInt
| 1;
254 } else if (bigint
<= (uint64_t)0xffffffff) {
256 marker
= kCFBinaryPlistMarkerInt
| 2;
259 marker
= kCFBinaryPlistMarkerInt
| 3;
261 bigint
= CFSwapInt64HostToBig(bigint
);
262 bytes
= (uint8_t *)&bigint
+ sizeof(bigint
) - nbytes
;
263 bufferWrite(buf
, &marker
, 1);
264 bufferWrite(buf
, bytes
, nbytes
);
267 static void _appendUID(__CFBinaryPlistWriteBuffer
*buf
, CFKeyedArchiverUIDRef uid
) {
271 uint64_t bigint
= _CFKeyedArchiverUIDGetValue(uid
);
272 if (bigint
<= (uint64_t)0xff) {
274 } else if (bigint
<= (uint64_t)0xffff) {
276 } else if (bigint
<= (uint64_t)0xffffffff) {
281 marker
= kCFBinaryPlistMarkerUID
| (uint8_t)(nbytes
- 1);
282 bigint
= CFSwapInt64HostToBig(bigint
);
283 bytes
= (uint8_t *)&bigint
+ sizeof(bigint
) - nbytes
;
284 bufferWrite(buf
, &marker
, 1);
285 bufferWrite(buf
, bytes
, nbytes
);
288 static Boolean
__plistNumberEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
289 // As long as this equals function is more restrictive than the
290 // existing one, for any given type, the hash function need not
291 // also be provided for the uniquing set.
292 if (CFNumberIsFloatType((CFNumberRef
)cf1
) != CFNumberIsFloatType((CFNumberRef
)cf2
)) return false;
293 return CFEqual(cf1
, cf2
);
296 static CFHashCode
__plistDataHash(CFTypeRef cf
) {
297 CFDataRef data
= (CFDataRef
)cf
;
298 return CFHashBytes((UInt8
*)CFDataGetBytePtr(data
), __CFMin(CFDataGetLength(data
), 1280));
301 static void _flattenPlist(CFPropertyListRef plist
, CFMutableArrayRef objlist
, CFMutableDictionaryRef objtable
, CFMutableSetRef uniquingsets
[]) {
302 CFPropertyListRef unique
;
304 CFTypeID type
= __CFGenericTypeID_genericobj_inline(plist
);
306 CFPropertyListRef
*list
, buffer
[256];
308 // Do not unique dictionaries or arrays, because: they
309 // are slow to compare, and have poor hash codes.
310 // Uniquing bools is unnecessary.
312 if (stringtype
== type
) {
314 } else if (numbertype
== type
) {
316 } else if (datetype
== type
) {
318 } else if (datatype
== type
) {
321 if (1 && -1 != which
) {
322 CFMutableSetRef uniquingset
= uniquingsets
[which
];
323 CFIndex before
= CFSetGetCount(uniquingset
);
324 CFSetAddValue(uniquingset
, plist
);
325 CFIndex after
= CFSetGetCount(uniquingset
);
326 if (after
== before
) { // already in set
327 unique
= CFSetGetValue(uniquingset
, plist
);
328 if (unique
!= plist
) {
329 refnum
= (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable
, unique
);
330 CFDictionaryAddValue(objtable
, plist
, (const void *)(uintptr_t)refnum
);
335 refnum
= CFArrayGetCount(objlist
);
336 CFArrayAppendValue(objlist
, plist
);
337 CFDictionaryAddValue(objtable
, plist
, (const void *)(uintptr_t)refnum
);
338 if (dicttype
== type
) {
339 CFIndex count
= CFDictionaryGetCount((CFDictionaryRef
)plist
);
340 list
= (count
<= 128) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, 2 * count
* sizeof(CFTypeRef
), 0);
341 CFDictionaryGetKeysAndValues((CFDictionaryRef
)plist
, list
, list
+ count
);
342 for (idx
= 0; idx
< 2 * count
; idx
++) {
343 _flattenPlist(list
[idx
], objlist
, objtable
, uniquingsets
);
345 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
346 } else if (arraytype
== type
) {
347 CFIndex count
= CFArrayGetCount((CFArrayRef
)plist
);
348 list
= (count
<= 256) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, count
* sizeof(CFTypeRef
), 0);
349 CFArrayGetValues((CFArrayRef
)plist
, CFRangeMake(0, count
), list
);
350 for (idx
= 0; idx
< count
; idx
++) {
351 _flattenPlist(list
[idx
], objlist
, objtable
, uniquingsets
);
353 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
357 // stream must be a CFMutableDataRef
358 CFIndex
__CFBinaryPlistWriteToStream(CFPropertyListRef plist
, CFTypeRef stream
) {
359 CFMutableDictionaryRef objtable
;
360 CFMutableArrayRef objlist
;
361 CFBinaryPlistTrailer trailer
;
362 uint64_t *offsets
, length_so_far
;
363 uint64_t mask
, refnum
;
364 int64_t idx
, idx2
, cnt
;
365 __CFBinaryPlistWriteBuffer
*buf
;
367 if ((CFTypeID
)-1 == stringtype
) {
368 stringtype
= CFStringGetTypeID();
370 if ((CFTypeID
)-1 == datatype
) {
371 datatype
= CFDataGetTypeID();
373 if ((CFTypeID
)-1 == numbertype
) {
374 numbertype
= CFNumberGetTypeID();
376 if ((CFTypeID
)-1 == booltype
) {
377 booltype
= CFBooleanGetTypeID();
379 if ((CFTypeID
)-1 == datetype
) {
380 datetype
= CFDateGetTypeID();
382 if ((CFTypeID
)-1 == dicttype
) {
383 dicttype
= CFDictionaryGetTypeID();
385 if ((CFTypeID
)-1 == arraytype
) {
386 arraytype
= CFArrayGetTypeID();
388 if ((CFTypeID
)-1 == settype
) {
389 settype
= CFSetGetTypeID();
391 if ((CFTypeID
)-1 == nulltype
) {
392 nulltype
= CFNullGetTypeID();
395 objtable
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
, NULL
);
396 _CFDictionarySetCapacity(objtable
, 640);
397 objlist
= CFArrayCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
);
398 _CFArraySetCapacity(objlist
, 640);
399 CFSetCallBacks cb
= kCFTypeSetCallBacks
;
402 CFMutableSetRef uniquingsets
[4];
403 uniquingsets
[0] = CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, &cb
);
404 _CFSetSetCapacity(uniquingsets
[0], 1000);
405 cb
.equal
= __plistNumberEqual
;
406 uniquingsets
[1] = CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, &cb
);
407 _CFSetSetCapacity(uniquingsets
[1], 500);
408 cb
.equal
= kCFTypeSetCallBacks
.equal
;
409 uniquingsets
[2] = CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, &cb
);
410 _CFSetSetCapacity(uniquingsets
[2], 500);
411 cb
.hash
= __plistDataHash
;
412 uniquingsets
[3] = CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, &cb
);
413 _CFSetSetCapacity(uniquingsets
[3], 500);
415 _flattenPlist(plist
, objlist
, objtable
, uniquingsets
);
417 CFRelease(uniquingsets
[0]);
418 CFRelease(uniquingsets
[1]);
419 CFRelease(uniquingsets
[2]);
420 CFRelease(uniquingsets
[3]);
422 cnt
= CFArrayGetCount(objlist
);
423 offsets
= (uint64_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault
, (CFIndex
)(cnt
* sizeof(*offsets
)), 0);
425 buf
= (__CFBinaryPlistWriteBuffer
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, sizeof(__CFBinaryPlistWriteBuffer
), 0);
426 buf
->stream
= stream
;
428 buf
->streamIsData
= (CFGetTypeID(stream
) == CFDataGetTypeID());
431 bufferWrite(buf
, (uint8_t *)"bplist00", 8); // header
433 memset(&trailer
, 0, sizeof(trailer
));
434 trailer
._numObjects
= CFSwapInt64HostToBig(cnt
);
435 trailer
._topObject
= 0; // true for this implementation
438 trailer
._objectRefSize
++;
442 for (idx
= 0; idx
< cnt
; idx
++) {
443 CFPropertyListRef obj
= CFArrayGetValueAtIndex(objlist
, (CFIndex
)idx
);
444 CFTypeID type
= __CFGenericTypeID_genericobj_inline(obj
);
445 offsets
[idx
] = buf
->written
+ buf
->used
;
446 if (stringtype
== type
) {
447 CFIndex ret
, count
= CFStringGetLength((CFStringRef
)obj
);
449 uint8_t *bytes
, buffer
[1024];
450 bytes
= (count
<= 1024) ? buffer
: (uint8_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault
, count
, 0);
451 // presumption, believed to be true, is that ASCII encoding may need
452 // less bytes, but will not need greater, than the # of unichars
453 ret
= CFStringGetBytes((CFStringRef
)obj
, CFRangeMake(0, count
), kCFStringEncodingASCII
, 0, false, bytes
, count
, &needed
);
455 uint8_t marker
= (uint8_t)(kCFBinaryPlistMarkerASCIIString
| (needed
< 15 ? needed
: 0xf));
456 bufferWrite(buf
, &marker
, 1);
458 _appendInt(buf
, (uint64_t)needed
);
460 bufferWrite(buf
, bytes
, needed
);
463 uint8_t marker
= (uint8_t)(kCFBinaryPlistMarkerUnicode16String
| (count
< 15 ? count
: 0xf));
464 bufferWrite(buf
, &marker
, 1);
466 _appendInt(buf
, (uint64_t)count
);
468 chars
= (UniChar
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, count
* sizeof(UniChar
), 0);
469 CFStringGetCharacters((CFStringRef
)obj
, CFRangeMake(0, count
), chars
);
470 for (idx2
= 0; idx2
< count
; idx2
++) {
471 chars
[idx2
] = CFSwapInt16HostToBig(chars
[idx2
]);
473 bufferWrite(buf
, (uint8_t *)chars
, count
* sizeof(UniChar
));
474 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, chars
);
476 if (bytes
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, bytes
);
477 } else if (numbertype
== type
) {
482 if (CFNumberIsFloatType((CFNumberRef
)obj
)) {
483 CFSwappedFloat64 swapped64
;
484 CFSwappedFloat32 swapped32
;
485 if (CFNumberGetByteSize((CFNumberRef
)obj
) <= (CFIndex
)sizeof(float)) {
487 CFNumberGetValue((CFNumberRef
)obj
, kCFNumberFloat32Type
, &v
);
488 swapped32
= CFConvertFloat32HostToSwapped(v
);
489 bytes
= (uint8_t *)&swapped32
;
490 nbytes
= sizeof(float);
491 marker
= kCFBinaryPlistMarkerReal
| 2;
494 CFNumberGetValue((CFNumberRef
)obj
, kCFNumberFloat64Type
, &v
);
495 swapped64
= CFConvertFloat64HostToSwapped(v
);
496 bytes
= (uint8_t *)&swapped64
;
497 nbytes
= sizeof(double);
498 marker
= kCFBinaryPlistMarkerReal
| 3;
500 bufferWrite(buf
, &marker
, 1);
501 bufferWrite(buf
, bytes
, nbytes
);
503 CFNumberType type
= _CFNumberGetType2((CFNumberRef
)obj
);
504 if (kCFNumberSInt128Type
== type
) {
506 CFNumberGetValue((CFNumberRef
)obj
, kCFNumberSInt128Type
, &s
);
511 storage
.high
= CFSwapInt64HostToBig(s
.high
);
512 storage
.low
= CFSwapInt64HostToBig(s
.low
);
513 uint8_t *bytes
= (uint8_t *)&storage
;
514 uint8_t marker
= kCFBinaryPlistMarkerInt
| 4;
516 bufferWrite(buf
, &marker
, 1);
517 bufferWrite(buf
, bytes
, nbytes
);
519 CFNumberGetValue((CFNumberRef
)obj
, kCFNumberSInt64Type
, &bigint
);
520 _appendInt(buf
, bigint
);
523 } else if (_CFKeyedArchiverUIDGetTypeID() == type
) {
524 _appendUID(buf
, (CFKeyedArchiverUIDRef
)obj
);
525 } else if (booltype
== type
) {
526 uint8_t marker
= CFBooleanGetValue((CFBooleanRef
)obj
) ? kCFBinaryPlistMarkerTrue
: kCFBinaryPlistMarkerFalse
;
527 bufferWrite(buf
, &marker
, 1);
528 } else if (datatype
== type
) {
529 CFIndex count
= CFDataGetLength((CFDataRef
)obj
);
530 uint8_t marker
= (uint8_t)(kCFBinaryPlistMarkerData
| (count
< 15 ? count
: 0xf));
531 bufferWrite(buf
, &marker
, 1);
533 _appendInt(buf
, (uint64_t)count
);
535 bufferWrite(buf
, CFDataGetBytePtr((CFDataRef
)obj
), count
);
536 } else if (datetype
== type
) {
537 CFSwappedFloat64 swapped
;
538 uint8_t marker
= kCFBinaryPlistMarkerDate
;
539 bufferWrite(buf
, &marker
, 1);
540 swapped
= CFConvertFloat64HostToSwapped(CFDateGetAbsoluteTime((CFDateRef
)obj
));
541 bufferWrite(buf
, (uint8_t *)&swapped
, sizeof(swapped
));
542 } else if (dicttype
== type
) {
543 CFIndex count
= CFDictionaryGetCount((CFDictionaryRef
)obj
);
544 CFPropertyListRef
*list
, buffer
[512];
545 uint8_t marker
= (uint8_t)(kCFBinaryPlistMarkerDict
| (count
< 15 ? count
: 0xf));
546 bufferWrite(buf
, &marker
, 1);
548 _appendInt(buf
, (uint64_t)count
);
550 list
= (count
<= 256) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, 2 * count
* sizeof(CFTypeRef
), 0);
551 CFDictionaryGetKeysAndValues((CFDictionaryRef
)obj
, list
, list
+ count
);
552 for (idx2
= 0; idx2
< 2 * count
; idx2
++) {
553 CFPropertyListRef value
= list
[idx2
];
554 uint32_t swapped
= 0;
555 uint8_t *source
= (uint8_t *)&swapped
;
556 refnum
= (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable
, value
);
557 swapped
= CFSwapInt32HostToBig((uint32_t)refnum
);
558 bufferWrite(buf
, source
+ sizeof(swapped
) - trailer
._objectRefSize
, trailer
._objectRefSize
);
560 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
561 } else if (arraytype
== type
) {
562 CFIndex count
= CFArrayGetCount((CFArrayRef
)obj
);
563 CFPropertyListRef
*list
, buffer
[256];
564 uint8_t marker
= (uint8_t)(kCFBinaryPlistMarkerArray
| (count
< 15 ? count
: 0xf));
565 bufferWrite(buf
, &marker
, 1);
567 _appendInt(buf
, (uint64_t)count
);
569 list
= (count
<= 256) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, count
* sizeof(CFTypeRef
), 0);
570 CFArrayGetValues((CFArrayRef
)obj
, CFRangeMake(0, count
), list
);
571 for (idx2
= 0; idx2
< count
; idx2
++) {
572 CFPropertyListRef value
= list
[idx2
];
573 uint32_t swapped
= 0;
574 uint8_t *source
= (uint8_t *)&swapped
;
575 refnum
= (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable
, value
);
576 swapped
= CFSwapInt32HostToBig((uint32_t)refnum
);
577 bufferWrite(buf
, source
+ sizeof(swapped
) - trailer
._objectRefSize
, trailer
._objectRefSize
);
579 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
583 if (buf
->error
) CFRelease(buf
->error
);
584 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, buf
);
585 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, offsets
);
592 length_so_far
= buf
->written
+ buf
->used
;
593 trailer
._offsetTableOffset
= CFSwapInt64HostToBig(length_so_far
);
594 trailer
._offsetIntSize
= 0;
596 while (length_so_far
& mask
) {
597 trailer
._offsetIntSize
++;
601 for (idx
= 0; idx
< cnt
; idx
++) {
602 uint64_t swapped
= CFSwapInt64HostToBig(offsets
[idx
]);
603 uint8_t *source
= (uint8_t *)&swapped
;
604 bufferWrite(buf
, source
+ sizeof(*offsets
) - trailer
._offsetIntSize
, trailer
._offsetIntSize
);
606 length_so_far
+= cnt
* trailer
._offsetIntSize
;
608 bufferWrite(buf
, (uint8_t *)&trailer
, sizeof(trailer
));
610 length_so_far
+= sizeof(trailer
);
612 CFRelease(buf
->error
);
615 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, buf
);
616 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, offsets
);
617 return (CFIndex
)length_so_far
;
621 #define FAIL_FALSE do { return false; } while (0)
622 #define FAIL_MAXOFFSET do { return UINT64_MAX; } while (0)
624 bool __CFBinaryPlistGetTopLevelInfo(const uint8_t *databytes
, uint64_t datalen
, uint8_t *marker
, uint64_t *offset
, CFBinaryPlistTrailer
*trailer
) {
625 CFBinaryPlistTrailer trail
;
627 if ((CFTypeID
)-1 == stringtype
) {
628 stringtype
= CFStringGetTypeID();
630 if ((CFTypeID
)-1 == datatype
) {
631 datatype
= CFDataGetTypeID();
633 if ((CFTypeID
)-1 == numbertype
) {
634 numbertype
= CFNumberGetTypeID();
636 if ((CFTypeID
)-1 == booltype
) {
637 booltype
= CFBooleanGetTypeID();
639 if ((CFTypeID
)-1 == datetype
) {
640 datetype
= CFDateGetTypeID();
642 if ((CFTypeID
)-1 == dicttype
) {
643 dicttype
= CFDictionaryGetTypeID();
645 if ((CFTypeID
)-1 == arraytype
) {
646 arraytype
= CFArrayGetTypeID();
648 if ((CFTypeID
)-1 == settype
) {
649 settype
= CFSetGetTypeID();
651 if ((CFTypeID
)-1 == nulltype
) {
652 nulltype
= CFNullGetTypeID();
655 if (!databytes
|| datalen
< sizeof(trail
) + 8 + 1) FAIL_FALSE
;
656 if (0 != memcmp("bplist00", databytes
, 8) && 0 != memcmp("bplist01", databytes
, 8)) return false;
657 memmove(&trail
, databytes
+ datalen
- sizeof(trail
), sizeof(trail
));
658 if (trail
._unused
[0] != 0 || trail
._unused
[1] != 0 || trail
._unused
[2] != 0 || trail
._unused
[3] != 0 || trail
._unused
[4] != 0 || trail
._unused
[5] != 0) FAIL_FALSE
;
659 trail
._numObjects
= CFSwapInt64BigToHost(trail
._numObjects
);
660 trail
._topObject
= CFSwapInt64BigToHost(trail
._topObject
);
661 trail
._offsetTableOffset
= CFSwapInt64BigToHost(trail
._offsetTableOffset
);
662 if (LONG_MAX
< trail
._numObjects
) FAIL_FALSE
;
663 if (LONG_MAX
< trail
._offsetTableOffset
) FAIL_FALSE
;
664 if (trail
._numObjects
< 1) FAIL_FALSE
;
665 if (trail
._numObjects
<= trail
._topObject
) FAIL_FALSE
;
666 if (trail
._offsetTableOffset
< 9) FAIL_FALSE
;
667 if (datalen
- sizeof(trail
) <= trail
._offsetTableOffset
) FAIL_FALSE
;
668 if (trail
._offsetIntSize
< 1) FAIL_FALSE
;
669 if (trail
._objectRefSize
< 1) FAIL_FALSE
;
670 int32_t err
= CF_NO_ERROR
;
671 uint64_t offsetIntSize
= trail
._offsetIntSize
;
672 uint64_t offsetTableSize
= __check_uint64_mul_unsigned_unsigned(trail
._numObjects
, offsetIntSize
, &err
);
673 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
674 if (offsetTableSize
< 1) FAIL_FALSE
;
675 uint64_t objectDataSize
= trail
._offsetTableOffset
- 8;
676 uint64_t tmpSum
= __check_uint64_add_unsigned_unsigned(8, objectDataSize
, &err
);
677 tmpSum
= __check_uint64_add_unsigned_unsigned(tmpSum
, offsetTableSize
, &err
);
678 tmpSum
= __check_uint64_add_unsigned_unsigned(tmpSum
, sizeof(trail
), &err
);
679 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
680 if (datalen
!= tmpSum
) FAIL_FALSE
;
681 if (trail
._objectRefSize
< 8 && (1ULL << (8 * trail
._objectRefSize
)) <= trail
._numObjects
) FAIL_FALSE
;
682 if (trail
._offsetIntSize
< 8 && (1ULL << (8 * trail
._offsetIntSize
)) <= trail
._offsetTableOffset
) FAIL_FALSE
;
683 const uint8_t *objectsFirstByte
;
684 objectsFirstByte
= check_ptr_add(databytes
, 8, &err
);
685 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
686 const uint8_t *offsetsFirstByte
= check_ptr_add(databytes
, trail
._offsetTableOffset
, &err
);
687 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
688 const uint8_t *offsetsLastByte
;
689 offsetsLastByte
= check_ptr_add(offsetsFirstByte
, offsetTableSize
- 1, &err
);
690 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
692 const uint8_t *bytesptr
= databytes
+ trail
._offsetTableOffset
;
693 uint64_t maxOffset
= trail
._offsetTableOffset
- 1;
694 for (CFIndex idx
= 0; idx
< trail
._numObjects
; idx
++) {
696 for (CFIndex idx2
= 0; idx2
< trail
._offsetIntSize
; idx2
++) {
697 off
= (off
<< 8) + bytesptr
[idx2
];
699 if (maxOffset
< off
) FAIL_FALSE
;
700 bytesptr
+= trail
._offsetIntSize
;
703 bytesptr
= databytes
+ trail
._offsetTableOffset
+ trail
._topObject
* trail
._offsetIntSize
;
705 for (CFIndex idx
= 0; idx
< trail
._offsetIntSize
; idx
++) {
706 off
= (off
<< 8) + bytesptr
[idx
];
708 if (off
< 8 || trail
._offsetTableOffset
<= off
) FAIL_FALSE
;
709 if (trailer
) *trailer
= trail
;
710 if (offset
) *offset
= off
;
711 if (marker
) *marker
= *(databytes
+ off
);
715 CF_INLINE Boolean
_plistIsPrimitive(CFPropertyListRef pl
) {
716 CFTypeID type
= __CFGenericTypeID_genericobj_inline(pl
);
717 if (dicttype
== type
|| arraytype
== type
|| settype
== type
) FAIL_FALSE
;
721 CF_INLINE
bool _readInt(const uint8_t *ptr
, const uint8_t *end_byte_ptr
, uint64_t *bigint
, const uint8_t **newptr
) {
722 if (end_byte_ptr
< ptr
) FAIL_FALSE
;
723 uint8_t marker
= *ptr
++;
724 if ((marker
& 0xf0) != kCFBinaryPlistMarkerInt
) FAIL_FALSE
;
725 uint64_t cnt
= 1 << (marker
& 0x0f);
726 int32_t err
= CF_NO_ERROR
;
727 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
728 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
729 if (end_byte_ptr
< extent
) FAIL_FALSE
;
730 // integers are not required to be in the most compact possible representation, but only the last 64 bits are significant currently
732 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
733 *bigint
= (*bigint
<< 8) + *ptr
++;
735 if (newptr
) *newptr
= ptr
;
739 // bytesptr points at a ref
740 CF_INLINE
uint64_t _getOffsetOfRefAt(const uint8_t *databytes
, const uint8_t *bytesptr
, const CFBinaryPlistTrailer
*trailer
) {
741 // *trailer contents are trusted, even for overflows -- was checked when the trailer was parsed;
742 // this pointer arithmetic and the multiplication was also already done once and checked,
743 // and the offsetTable was already validated.
744 const uint8_t *objectsFirstByte
= databytes
+ 8;
745 const uint8_t *offsetsFirstByte
= databytes
+ trailer
->_offsetTableOffset
;
746 if (bytesptr
< objectsFirstByte
|| offsetsFirstByte
- trailer
->_objectRefSize
< bytesptr
) FAIL_MAXOFFSET
;
749 for (CFIndex idx
= 0; idx
< trailer
->_objectRefSize
; idx
++) {
750 ref
= (ref
<< 8) + bytesptr
[idx
];
752 if (trailer
->_numObjects
<= ref
) FAIL_MAXOFFSET
;
754 bytesptr
= databytes
+ trailer
->_offsetTableOffset
+ ref
* trailer
->_offsetIntSize
;
756 for (CFIndex idx
= 0; idx
< trailer
->_offsetIntSize
; idx
++) {
757 off
= (off
<< 8) + bytesptr
[idx
];
762 static 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
);
764 bool __CFBinaryPlistGetOffsetForValueFromArray2(const uint8_t *databytes
, uint64_t datalen
, uint64_t startOffset
, const CFBinaryPlistTrailer
*trailer
, CFIndex idx
, uint64_t *offset
, CFMutableDictionaryRef objects
) {
765 uint64_t objectsRangeStart
= 8, objectsRangeEnd
= trailer
->_offsetTableOffset
- 1;
766 if (startOffset
< objectsRangeStart
|| objectsRangeEnd
< startOffset
) FAIL_FALSE
;
767 const uint8_t *ptr
= databytes
+ startOffset
;
768 uint8_t marker
= *ptr
;
769 if ((marker
& 0xf0) != kCFBinaryPlistMarkerArray
) FAIL_FALSE
;
770 int32_t err
= CF_NO_ERROR
;
771 ptr
= check_ptr_add(ptr
, 1, &err
);
772 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
773 uint64_t cnt
= (marker
& 0x0f);
776 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
777 if (LONG_MAX
< bigint
) FAIL_FALSE
;
780 if (cnt
<= idx
) FAIL_FALSE
;
781 size_t byte_cnt
= check_size_t_mul(cnt
, trailer
->_objectRefSize
, &err
);
782 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
783 const uint8_t *extent
= check_ptr_add(ptr
, byte_cnt
, &err
) - 1;
784 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
785 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
786 uint64_t off
= _getOffsetOfRefAt(databytes
, ptr
+ idx
* trailer
->_objectRefSize
, trailer
);
787 if (offset
) *offset
= off
;
791 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
) {
792 if (!key
|| !_plistIsPrimitive(key
)) FAIL_FALSE
;
793 uint64_t objectsRangeStart
= 8, objectsRangeEnd
= trailer
->_offsetTableOffset
- 1;
794 if (startOffset
< objectsRangeStart
|| objectsRangeEnd
< startOffset
) FAIL_FALSE
;
795 const uint8_t *ptr
= databytes
+ startOffset
;
796 uint8_t marker
= *ptr
;
797 if ((marker
& 0xf0) != kCFBinaryPlistMarkerDict
) FAIL_FALSE
;
798 int32_t err
= CF_NO_ERROR
;
799 ptr
= check_ptr_add(ptr
, 1, &err
);
800 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
801 uint64_t cnt
= (marker
& 0x0f);
804 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
805 if (LONG_MAX
< bigint
) FAIL_FALSE
;
808 cnt
= check_size_t_mul(cnt
, 2, &err
);
809 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
810 size_t byte_cnt
= check_size_t_mul(cnt
, trailer
->_objectRefSize
, &err
);
811 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
812 const uint8_t *extent
= check_ptr_add(ptr
, byte_cnt
, &err
) - 1;
813 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
814 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
815 CFIndex stringKeyLen
= -1;
817 if (__CFGenericTypeID_genericobj_inline(key
) == stringtype
) {
818 stringKeyLen
= CFStringGetLength((CFStringRef
)key
);
819 if (stringKeyLen
< 0xf) {
820 CFStringGetCharacters((CFStringRef
)key
, CFRangeMake(0, stringKeyLen
), ubuffer
);
824 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
825 uint64_t off
= _getOffsetOfRefAt(databytes
, ptr
, trailer
);
826 uint8_t marker
= *(databytes
+ off
);
827 CFIndex len
= marker
& 0x0f;
828 // if it is a short ascii string in the data, and the key is a string
829 if ((marker
& 0xf0) == kCFBinaryPlistMarkerASCIIString
&& len
< 0xf && stringKeyLen
!= -1) {
830 if (len
!= stringKeyLen
) goto miss
;
832 const uint8_t *ptr2
= databytes
+ off
;
833 extent
= check_ptr_add(ptr2
, len
, &err
);
834 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
835 if (databytes
+ trailer
->_offsetTableOffset
<= extent
) FAIL_FALSE
;
836 for (CFIndex idx2
= 0; idx2
< stringKeyLen
; idx2
++) {
837 if ((UniChar
)ptr2
[idx2
+ 1] != ubuffer
[idx2
]) goto miss
;
839 if (koffset
) *koffset
= off
;
841 off
= _getOffsetOfRefAt(databytes
, ptr
+ cnt
* trailer
->_objectRefSize
, trailer
);
847 CFPropertyListRef pl
= NULL
;
848 if (!__CFBinaryPlistCreateObject2(databytes
, datalen
, off
, trailer
, kCFAllocatorSystemDefault
, kCFPropertyListImmutable
, objects
, NULL
, 0, &pl
) || !_plistIsPrimitive(pl
)) {
849 if (pl
) CFRelease(pl
);
852 if (CFEqual(key
, pl
)) {
854 if (koffset
) *koffset
= off
;
856 off
= _getOffsetOfRefAt(databytes
, ptr
+ cnt
* trailer
->_objectRefSize
, trailer
);
863 ptr
+= trailer
->_objectRefSize
;
868 extern CFArrayRef
_CFArrayCreate_ex(CFAllocatorRef allocator
, Boolean isMutable
, const void **values
, CFIndex numValues
);
869 extern CFSetRef
_CFSetCreate_ex(CFAllocatorRef allocator
, Boolean isMutable
, const void **values
, CFIndex numValues
);
870 extern CFDictionaryRef
_CFDictionaryCreate_ex(CFAllocatorRef allocator
, Boolean isMutable
, const void **keys
, const void **values
, CFIndex numValues
);
872 static 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
) {
875 *plist
= CFDictionaryGetValue(objects
, (const void *)(uintptr_t)startOffset
);
882 // at any one invocation of this function, set should contain the offsets in the "path" down to this object
883 if (set
&& CFSetContainsValue(set
, (const void *)(uintptr_t)startOffset
)) return false;
885 // databytes is trusted to be at least datalen bytes long
886 // *trailer contents are trusted, even for overflows -- was checked when the trailer was parsed
887 uint64_t objectsRangeStart
= 8, objectsRangeEnd
= trailer
->_offsetTableOffset
- 1;
888 if (startOffset
< objectsRangeStart
|| objectsRangeEnd
< startOffset
) FAIL_FALSE
;
891 CFPropertyListRef
*list
, buffer
[256];
892 CFAllocatorRef listAllocator
;
894 uint8_t marker
= *(databytes
+ startOffset
);
895 switch (marker
& 0xf0) {
896 case kCFBinaryPlistMarkerNull
:
898 case kCFBinaryPlistMarkerNull
:
901 case kCFBinaryPlistMarkerFalse
:
902 *plist
= CFRetain(kCFBooleanFalse
);
904 case kCFBinaryPlistMarkerTrue
:
905 *plist
= CFRetain(kCFBooleanTrue
);
909 case kCFBinaryPlistMarkerInt
:
911 const uint8_t *ptr
= (databytes
+ startOffset
);
912 int32_t err
= CF_NO_ERROR
;
913 ptr
= check_ptr_add(ptr
, 1, &err
);
914 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
915 uint64_t cnt
= 1 << (marker
& 0x0f);
916 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
917 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
918 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
919 if (16 < cnt
) FAIL_FALSE
;
920 // in format version '00', 1, 2, and 4-byte integers have to be interpreted as unsigned,
921 // whereas 8-byte integers are signed (and 16-byte when available)
922 // negative 1, 2, 4-byte integers are always emitted as 8 bytes in format '00'
924 // integers are not required to be in the most compact possible representation, but only the last 64 bits are significant currently
925 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
926 bigint
= (bigint
<< 8) + *ptr
++;
932 *plist
= CFNumberCreate(allocator
, kCFNumberSInt128Type
, &val
);
934 *plist
= CFNumberCreate(allocator
, kCFNumberSInt64Type
, &bigint
);
936 // these are always immutable
937 if (objects
&& *plist
) {
938 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
940 return (*plist
) ? true : false;
942 case kCFBinaryPlistMarkerReal
:
943 switch (marker
& 0x0f) {
945 const uint8_t *ptr
= (databytes
+ startOffset
);
946 int32_t err
= CF_NO_ERROR
;
947 ptr
= check_ptr_add(ptr
, 1, &err
);
948 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
949 const uint8_t *extent
= check_ptr_add(ptr
, 4, &err
) - 1;
950 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
951 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
952 CFSwappedFloat32 swapped32
;
953 memmove(&swapped32
, ptr
, 4);
954 float f
= CFConvertFloat32SwappedToHost(swapped32
);
955 *plist
= CFNumberCreate(allocator
, kCFNumberFloat32Type
, &f
);
956 // these are always immutable
957 if (objects
&& *plist
) {
958 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
960 return (*plist
) ? true : false;
963 const uint8_t *ptr
= (databytes
+ startOffset
);
964 int32_t err
= CF_NO_ERROR
;
965 ptr
= check_ptr_add(ptr
, 1, &err
);
966 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
967 const uint8_t *extent
= check_ptr_add(ptr
, 8, &err
) - 1;
968 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
969 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
970 CFSwappedFloat64 swapped64
;
971 memmove(&swapped64
, ptr
, 8);
972 double d
= CFConvertFloat64SwappedToHost(swapped64
);
973 *plist
= CFNumberCreate(allocator
, kCFNumberFloat64Type
, &d
);
974 // these are always immutable
975 if (objects
&& *plist
) {
976 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
978 return (*plist
) ? true : false;
982 case kCFBinaryPlistMarkerDate
& 0xf0:
984 case kCFBinaryPlistMarkerDate
: {
985 const uint8_t *ptr
= (databytes
+ startOffset
);
986 int32_t err
= CF_NO_ERROR
;
987 ptr
= check_ptr_add(ptr
, 1, &err
);
988 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
989 const uint8_t *extent
= check_ptr_add(ptr
, 8, &err
) - 1;
990 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
991 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
992 CFSwappedFloat64 swapped64
;
993 memmove(&swapped64
, ptr
, 8);
994 double d
= CFConvertFloat64SwappedToHost(swapped64
);
995 *plist
= CFDateCreate(allocator
, d
);
996 // these are always immutable
997 if (objects
&& *plist
) {
998 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1000 return (*plist
) ? true : false;
1004 case kCFBinaryPlistMarkerData
: {
1005 const uint8_t *ptr
= databytes
+ startOffset
;
1006 int32_t err
= CF_NO_ERROR
;
1007 ptr
= check_ptr_add(ptr
, 1, &err
);
1008 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1009 CFIndex cnt
= marker
& 0x0f;
1011 uint64_t bigint
= 0;
1012 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
1013 if (LONG_MAX
< bigint
) FAIL_FALSE
;
1014 cnt
= (CFIndex
)bigint
;
1016 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
1017 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1018 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1019 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
1020 *plist
= CFDataCreateMutable(allocator
, 0);
1021 if (*plist
) CFDataAppendBytes((CFMutableDataRef
)*plist
, ptr
, cnt
);
1023 *plist
= CFDataCreate(allocator
, ptr
, cnt
);
1025 if (objects
&& *plist
&& (mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
)) {
1026 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1028 return (*plist
) ? true : false;
1030 case kCFBinaryPlistMarkerASCIIString
: {
1031 const uint8_t *ptr
= databytes
+ startOffset
;
1032 int32_t err
= CF_NO_ERROR
;
1033 ptr
= check_ptr_add(ptr
, 1, &err
);
1034 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1035 CFIndex cnt
= marker
& 0x0f;
1037 uint64_t bigint
= 0;
1038 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
1039 if (LONG_MAX
< bigint
) FAIL_FALSE
;
1040 cnt
= (CFIndex
)bigint
;
1042 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
1043 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1044 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1045 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
1046 CFStringRef str
= CFStringCreateWithBytes(allocator
, ptr
, cnt
, kCFStringEncodingASCII
, false);
1047 *plist
= str
? CFStringCreateMutableCopy(allocator
, 0, str
) : NULL
;
1048 if (str
) CFRelease(str
);
1050 *plist
= CFStringCreateWithBytes(allocator
, ptr
, cnt
, kCFStringEncodingASCII
, false);
1052 if (objects
&& *plist
&& (mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
)) {
1053 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1055 return (*plist
) ? true : false;
1057 case kCFBinaryPlistMarkerUnicode16String
: {
1058 const uint8_t *ptr
= databytes
+ startOffset
;
1059 int32_t err
= CF_NO_ERROR
;
1060 ptr
= check_ptr_add(ptr
, 1, &err
);
1061 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1062 CFIndex cnt
= marker
& 0x0f;
1064 uint64_t bigint
= 0;
1065 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
1066 if (LONG_MAX
< bigint
) FAIL_FALSE
;
1067 cnt
= (CFIndex
)bigint
;
1069 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
1070 extent
= check_ptr_add(extent
, cnt
, &err
); // 2 bytes per character
1071 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1072 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1073 size_t byte_cnt
= check_size_t_mul(cnt
, sizeof(UniChar
), &err
);
1074 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1075 UniChar
*chars
= (UniChar
*)CFAllocatorAllocate(allocator
, byte_cnt
, 0);
1076 if (!chars
) FAIL_FALSE
;
1077 memmove(chars
, ptr
, byte_cnt
);
1078 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
1079 chars
[idx
] = CFSwapInt16BigToHost(chars
[idx
]);
1081 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
1082 CFStringRef str
= CFStringCreateWithCharactersNoCopy(allocator
, chars
, cnt
, allocator
);
1083 *plist
= str
? CFStringCreateMutableCopy(allocator
, 0, str
) : NULL
;
1084 if (str
) CFRelease(str
);
1086 *plist
= CFStringCreateWithCharactersNoCopy(allocator
, chars
, cnt
, allocator
);
1088 if (objects
&& *plist
&& (mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
)) {
1089 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1091 return (*plist
) ? true : false;
1093 case kCFBinaryPlistMarkerUID
: {
1094 const uint8_t *ptr
= databytes
+ startOffset
;
1095 int32_t err
= CF_NO_ERROR
;
1096 ptr
= check_ptr_add(ptr
, 1, &err
);
1097 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1098 CFIndex cnt
= (marker
& 0x0f) + 1;
1099 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
1100 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1101 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1102 // uids are not required to be in the most compact possible representation, but only the last 64 bits are significant currently
1103 uint64_t bigint
= 0;
1104 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
1105 bigint
= (bigint
<< 8) + *ptr
++;
1107 if (UINT32_MAX
< bigint
) FAIL_FALSE
;
1108 *plist
= _CFKeyedArchiverUIDCreate(allocator
, (uint32_t)bigint
);
1109 // these are always immutable
1110 if (objects
&& *plist
) {
1111 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1113 return (*plist
) ? true : false;
1115 case kCFBinaryPlistMarkerArray
:
1116 case kCFBinaryPlistMarkerSet
: {
1117 const uint8_t *ptr
= databytes
+ startOffset
;
1118 int32_t err
= CF_NO_ERROR
;
1119 ptr
= check_ptr_add(ptr
, 1, &err
);
1120 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1121 CFIndex cnt
= marker
& 0x0f;
1123 uint64_t bigint
= 0;
1124 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
1125 if (LONG_MAX
< bigint
) FAIL_FALSE
;
1126 cnt
= (CFIndex
)bigint
;
1128 size_t byte_cnt
= check_size_t_mul(cnt
, trailer
->_objectRefSize
, &err
);
1129 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1130 const uint8_t *extent
= check_ptr_add(ptr
, byte_cnt
, &err
) - 1;
1131 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1132 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1133 byte_cnt
= check_size_t_mul(cnt
, sizeof(CFPropertyListRef
), &err
);
1134 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1135 list
= (cnt
<= 256) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, byte_cnt
, __kCFAllocatorGCScannedMemory
);
1136 listAllocator
= (list
== buffer
? kCFAllocatorNull
: kCFAllocatorSystemDefault
);
1137 if (!list
) FAIL_FALSE
;
1138 Boolean madeSet
= false;
1139 if (!set
&& 15 < curDepth
) {
1140 set
= CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
);
1141 madeSet
= set
? true : false;
1143 if (set
) CFSetAddValue(set
, (const void *)(uintptr_t)startOffset
);
1144 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
1145 CFPropertyListRef pl
;
1146 off
= _getOffsetOfRefAt(databytes
, ptr
, trailer
);
1147 if (!__CFBinaryPlistCreateObject2(databytes
, datalen
, off
, trailer
, allocator
, mutabilityOption
, objects
, set
, curDepth
+ 1, &pl
)) {
1148 if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator
)) {
1150 CFRelease(list
[idx
]);
1153 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
1156 if (CF_IS_COLLECTABLE_ALLOCATOR(allocator
)) {
1157 CF_WRITE_BARRIER_BASE_ASSIGN(listAllocator
, list
, list
[idx
], CFMakeCollectable(pl
));
1161 ptr
+= trailer
->_objectRefSize
;
1163 if (set
) CFSetRemoveValue(set
, (const void *)(uintptr_t)startOffset
);
1168 if ((marker
& 0xf0) == kCFBinaryPlistMarkerArray
) {
1169 *plist
= _CFArrayCreate_ex(allocator
, (mutabilityOption
!= kCFPropertyListImmutable
), list
, cnt
);
1171 *plist
= _CFSetCreate_ex(allocator
, (mutabilityOption
!= kCFPropertyListImmutable
), list
, cnt
);
1173 if (objects
&& *plist
&& (mutabilityOption
== kCFPropertyListImmutable
)) {
1174 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1176 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
1177 return (*plist
) ? true : false;
1179 case kCFBinaryPlistMarkerDict
: {
1180 const uint8_t *ptr
= databytes
+ startOffset
;
1181 int32_t err
= CF_NO_ERROR
;
1182 ptr
= check_ptr_add(ptr
, 1, &err
);
1183 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1184 CFIndex cnt
= marker
& 0x0f;
1186 uint64_t bigint
= 0;
1187 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
1188 if (LONG_MAX
< bigint
) FAIL_FALSE
;
1189 cnt
= (CFIndex
)bigint
;
1191 cnt
= check_size_t_mul(cnt
, 2, &err
);
1192 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1193 size_t byte_cnt
= check_size_t_mul(cnt
, trailer
->_objectRefSize
, &err
);
1194 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1195 const uint8_t *extent
= check_ptr_add(ptr
, byte_cnt
, &err
) - 1;
1196 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1197 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1198 byte_cnt
= check_size_t_mul(cnt
, sizeof(CFPropertyListRef
), &err
);
1199 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1200 list
= (cnt
<= 256) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, byte_cnt
, __kCFAllocatorGCScannedMemory
);
1201 listAllocator
= (list
== buffer
? kCFAllocatorNull
: kCFAllocatorSystemDefault
);
1202 if (!list
) FAIL_FALSE
;
1203 Boolean madeSet
= false;
1204 if (!set
&& 15 < curDepth
) {
1205 set
= CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
);
1206 madeSet
= set
? true : false;
1208 if (set
) CFSetAddValue(set
, (const void *)(uintptr_t)startOffset
);
1209 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
1210 CFPropertyListRef pl
= NULL
;
1211 off
= _getOffsetOfRefAt(databytes
, ptr
, trailer
);
1212 if (!__CFBinaryPlistCreateObject2(databytes
, datalen
, off
, trailer
, allocator
, mutabilityOption
, objects
, set
, curDepth
+ 1, &pl
) || (idx
< cnt
/ 2 && !_plistIsPrimitive(pl
))) {
1213 if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator
)) {
1214 if (pl
) CFRelease(pl
);
1216 CFRelease(list
[idx
]);
1219 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
1222 if (CF_IS_COLLECTABLE_ALLOCATOR(allocator
)) {
1223 CF_WRITE_BARRIER_BASE_ASSIGN(listAllocator
, list
, list
[idx
], CFMakeCollectable(pl
));
1227 ptr
+= trailer
->_objectRefSize
;
1229 if (set
) CFSetRemoveValue(set
, (const void *)(uintptr_t)startOffset
);
1234 *plist
= _CFDictionaryCreate_ex(allocator
, (mutabilityOption
!= kCFPropertyListImmutable
), list
, list
+ cnt
/ 2, cnt
/ 2);
1235 if (objects
&& *plist
&& (mutabilityOption
== kCFPropertyListImmutable
)) {
1236 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1238 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
1239 return (*plist
) ? true : false;
1245 bool __CFBinaryPlistCreateObject(const uint8_t *databytes
, uint64_t datalen
, uint64_t startOffset
, const CFBinaryPlistTrailer
*trailer
, CFAllocatorRef allocator
, CFOptionFlags mutabilityOption
, CFMutableDictionaryRef objects
, CFPropertyListRef
*plist
) {
1246 // for compatibility with Foundation's use, need to leave this here
1247 return __CFBinaryPlistCreateObject2(databytes
, datalen
, startOffset
, trailer
, allocator
, mutabilityOption
, objects
, NULL
, 0, plist
);
1250 __private_extern__
bool __CFTryParseBinaryPlist(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags option
, CFPropertyListRef
*plist
, CFStringRef
*errorString
) {
1252 CFBinaryPlistTrailer trailer
;
1254 const uint8_t *databytes
= CFDataGetBytePtr(data
);
1255 uint64_t datalen
= CFDataGetLength(data
);
1257 if (8 <= datalen
&& __CFBinaryPlistGetTopLevelInfo(databytes
, datalen
, &marker
, &offset
, &trailer
)) {
1258 // FALSE: We know for binary plist parsing that the result objects will be retained
1259 // by their containing collections as the parsing proceeds, so we do not need
1260 // to use retaining callbacks for the objects map in this case. WHY: the file might
1261 // be malformed and contain hash-equal keys for the same dictionary (for example)
1262 // and the later key will cause the previous one to be released when we set the second
1263 // in the dictionary.
1264 CFMutableDictionaryRef objects
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
, &kCFTypeDictionaryValueCallBacks
);
1265 _CFDictionarySetCapacity(objects
, 4000);
1266 CFPropertyListRef pl
= NULL
;
1267 if (__CFBinaryPlistCreateObject2(databytes
, datalen
, offset
, &trailer
, allocator
, option
, objects
, NULL
, 0, &pl
)) {
1268 if (plist
) *plist
= pl
;
1270 if (plist
) *plist
= NULL
;
1271 if (errorString
) *errorString
= (CFStringRef
)CFRetain(CFSTR("binary data is corrupt"));