2 * Copyright (c) 2009 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 Copyright (c) 2000-2009, 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>
44 #include "CFInternal.h"
52 kCFNumberSInt128Type
= 17
55 CF_EXPORT CFNumberType
_CFNumberGetType2(CFNumberRef number
);
56 __private_extern__ CFErrorRef
__CFPropertyListCreateError(CFAllocatorRef allocator
, CFIndex code
, CFStringRef debugString
, ...);
60 CF_OVERFLOW_ERROR
= (1 << 0),
63 CF_INLINE
uint32_t __check_uint32_add_unsigned_unsigned(uint32_t x
, uint32_t y
, int32_t* err
) {
64 if((UINT_MAX
- y
) < x
)
65 *err
= *err
| CF_OVERFLOW_ERROR
;
69 CF_INLINE
uint64_t __check_uint64_add_unsigned_unsigned(uint64_t x
, uint64_t y
, int32_t* err
) {
70 if((ULLONG_MAX
- y
) < x
)
71 *err
= *err
| CF_OVERFLOW_ERROR
;
75 CF_INLINE
uint32_t __check_uint32_mul_unsigned_unsigned(uint32_t x
, uint32_t y
, int32_t* err
) {
76 uint64_t tmp
= (uint64_t) x
* (uint64_t) y
;
77 /* If any of the upper 32 bits touched, overflow */
78 if(tmp
& 0xffffffff00000000ULL
)
79 *err
= *err
| CF_OVERFLOW_ERROR
;
80 return (uint32_t) tmp
;
83 CF_INLINE
uint64_t __check_uint64_mul_unsigned_unsigned(uint64_t x
, uint64_t y
, int32_t* err
) {
86 *err
= *err
| CF_OVERFLOW_ERROR
;
91 #define check_ptr_add(p, a, err) (const uint8_t *)__check_uint64_add_unsigned_unsigned((uintptr_t)p, (uintptr_t)a, err)
92 #define check_size_t_mul(b, a, err) (size_t)__check_uint64_mul_unsigned_unsigned((size_t)b, (size_t)a, err)
94 #define check_ptr_add(p, a, err) (const uint8_t *)__check_uint32_add_unsigned_unsigned((uintptr_t)p, (uintptr_t)a, err)
95 #define check_size_t_mul(b, a, err) (size_t)__check_uint32_mul_unsigned_unsigned((size_t)b, (size_t)a, err)
99 CF_INLINE CFTypeID
__CFGenericTypeID_genericobj_inline(const void *cf
) {
100 CFTypeID typeID
= (*(uint32_t *)(((CFRuntimeBase
*)cf
)->_cfinfo
) >> 8) & 0xFFFF;
101 return CF_IS_OBJC(typeID
, cf
) ? CFGetTypeID(cf
) : typeID
;
104 struct __CFKeyedArchiverUID
{
109 static CFStringRef
__CFKeyedArchiverUIDCopyDescription(CFTypeRef cf
) {
110 CFKeyedArchiverUIDRef uid
= (CFKeyedArchiverUIDRef
)cf
;
111 return CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("<CFKeyedArchiverUID %p [%p]>{value = %u}"), cf
, CFGetAllocator(cf
), uid
->_value
);
114 static CFStringRef
__CFKeyedArchiverUIDCopyFormattingDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
115 CFKeyedArchiverUIDRef uid
= (CFKeyedArchiverUIDRef
)cf
;
116 return CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("@%u@"), uid
->_value
);
119 static CFTypeID __kCFKeyedArchiverUIDTypeID
= _kCFRuntimeNotATypeID
;
121 static const CFRuntimeClass __CFKeyedArchiverUIDClass
= {
123 "CFKeyedArchiverUID",
127 NULL
, // equal -- pointer equality only
128 NULL
, // hash -- pointer hashing only
129 __CFKeyedArchiverUIDCopyFormattingDescription
,
130 __CFKeyedArchiverUIDCopyDescription
133 __private_extern__
void __CFKeyedArchiverUIDInitialize(void) {
134 __kCFKeyedArchiverUIDTypeID
= _CFRuntimeRegisterClass(&__CFKeyedArchiverUIDClass
);
137 CFTypeID
_CFKeyedArchiverUIDGetTypeID(void) {
138 return __kCFKeyedArchiverUIDTypeID
;
141 CFKeyedArchiverUIDRef
_CFKeyedArchiverUIDCreate(CFAllocatorRef allocator
, uint32_t value
) {
142 CFKeyedArchiverUIDRef uid
;
143 uid
= (CFKeyedArchiverUIDRef
)_CFRuntimeCreateInstance(allocator
, __kCFKeyedArchiverUIDTypeID
, sizeof(struct __CFKeyedArchiverUID
) - sizeof(CFRuntimeBase
), NULL
);
147 ((struct __CFKeyedArchiverUID
*)uid
)->_value
= value
;
152 uint32_t _CFKeyedArchiverUIDGetValue(CFKeyedArchiverUIDRef uid
) {
163 uint8_t buffer
[8192 - 32];
164 } __CFBinaryPlistWriteBuffer
;
166 static void writeBytes(__CFBinaryPlistWriteBuffer
*buf
, const UInt8
*bytes
, CFIndex length
) {
167 if (0 == length
) return;
168 if (buf
->error
) return;
169 if (buf
->streamIsData
) {
170 CFDataAppendBytes((CFMutableDataRef
)buf
->stream
, bytes
, length
);
171 buf
->written
+= length
;
173 CFAssert(false, __kCFLogAssertion
, "Streams are not supported on this platform");
177 static void bufferWrite(__CFBinaryPlistWriteBuffer
*buf
, const uint8_t *buffer
, CFIndex count
) {
178 if (0 == count
) return;
179 if ((CFIndex
)sizeof(buf
->buffer
) <= count
) {
180 writeBytes(buf
, buf
->buffer
, buf
->used
);
182 writeBytes(buf
, buffer
, count
);
185 CFIndex copyLen
= __CFMin(count
, (CFIndex
)sizeof(buf
->buffer
) - buf
->used
);
186 memmove(buf
->buffer
+ buf
->used
, buffer
, copyLen
);
187 buf
->used
+= copyLen
;
188 if (sizeof(buf
->buffer
) == buf
->used
) {
189 writeBytes(buf
, buf
->buffer
, sizeof(buf
->buffer
));
190 memmove(buf
->buffer
, buffer
+ copyLen
, count
- copyLen
);
191 buf
->used
= count
- copyLen
;
195 static void bufferFlush(__CFBinaryPlistWriteBuffer
*buf
) {
196 writeBytes(buf
, buf
->buffer
, buf
->used
);
202 magic number ("bplist")
206 variable-sized objects
208 Object Formats (marker byte followed by additional info in some cases)
210 bool 0000 1000 // false
211 bool 0000 1001 // true
212 fill 0000 1111 // fill byte
213 int 0001 nnnn ... // # of bytes is 2^nnnn, big-endian bytes
214 real 0010 nnnn ... // # of bytes is 2^nnnn, big-endian bytes
215 date 0011 0011 ... // 8 byte float follows, big-endian bytes
216 data 0100 nnnn [int] ... // nnnn is number of bytes unless 1111 then int count follows, followed by bytes
217 string 0101 nnnn [int] ... // ASCII string, nnnn is # of chars, else 1111 then int count, then bytes
218 string 0110 nnnn [int] ... // Unicode string, nnnn is # of chars, else 1111 then int count, then big-endian 2-byte uint16_t
220 uid 1000 nnnn ... // nnnn+1 is # of bytes
222 array 1010 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows
224 set 1100 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows
225 dict 1101 nnnn [int] keyref* objref* // nnnn is count, unless '1111', then int count follows
230 list of ints, byte size of which is given in trailer
231 -- these are the byte offsets into the file
232 -- number of these is in the trailer
235 byte size of offset ints in offset table
236 byte size of object refs in arrays and dicts
237 number of offsets in offset table (also is number of objects)
238 element # in offset table which is top level object
244 static CFTypeID stringtype
= -1, datatype
= -1, numbertype
= -1, datetype
= -1;
245 static CFTypeID booltype
= -1, nulltype
= -1, dicttype
= -1, arraytype
= -1, settype
= -1;
247 static void initStatics() {
248 if ((CFTypeID
)-1 == stringtype
) {
249 stringtype
= CFStringGetTypeID();
251 if ((CFTypeID
)-1 == datatype
) {
252 datatype
= CFDataGetTypeID();
254 if ((CFTypeID
)-1 == numbertype
) {
255 numbertype
= CFNumberGetTypeID();
257 if ((CFTypeID
)-1 == booltype
) {
258 booltype
= CFBooleanGetTypeID();
260 if ((CFTypeID
)-1 == datetype
) {
261 datetype
= CFDateGetTypeID();
263 if ((CFTypeID
)-1 == dicttype
) {
264 dicttype
= CFDictionaryGetTypeID();
266 if ((CFTypeID
)-1 == arraytype
) {
267 arraytype
= CFArrayGetTypeID();
269 if ((CFTypeID
)-1 == settype
) {
270 settype
= CFSetGetTypeID();
272 if ((CFTypeID
)-1 == nulltype
) {
273 nulltype
= CFNullGetTypeID();
277 static void _appendInt(__CFBinaryPlistWriteBuffer
*buf
, uint64_t bigint
) {
281 if (bigint
<= (uint64_t)0xff) {
283 marker
= kCFBinaryPlistMarkerInt
| 0;
284 } else if (bigint
<= (uint64_t)0xffff) {
286 marker
= kCFBinaryPlistMarkerInt
| 1;
287 } else if (bigint
<= (uint64_t)0xffffffff) {
289 marker
= kCFBinaryPlistMarkerInt
| 2;
292 marker
= kCFBinaryPlistMarkerInt
| 3;
294 bigint
= CFSwapInt64HostToBig(bigint
);
295 bytes
= (uint8_t *)&bigint
+ sizeof(bigint
) - nbytes
;
296 bufferWrite(buf
, &marker
, 1);
297 bufferWrite(buf
, bytes
, nbytes
);
300 static void _appendUID(__CFBinaryPlistWriteBuffer
*buf
, CFKeyedArchiverUIDRef uid
) {
304 uint64_t bigint
= _CFKeyedArchiverUIDGetValue(uid
);
305 if (bigint
<= (uint64_t)0xff) {
307 } else if (bigint
<= (uint64_t)0xffff) {
309 } else if (bigint
<= (uint64_t)0xffffffff) {
314 marker
= kCFBinaryPlistMarkerUID
| (uint8_t)(nbytes
- 1);
315 bigint
= CFSwapInt64HostToBig(bigint
);
316 bytes
= (uint8_t *)&bigint
+ sizeof(bigint
) - nbytes
;
317 bufferWrite(buf
, &marker
, 1);
318 bufferWrite(buf
, bytes
, nbytes
);
321 static void _flattenPlist(CFPropertyListRef plist
, CFMutableArrayRef objlist
, CFMutableDictionaryRef objtable
, CFMutableSetRef uniquingset
) {
322 CFPropertyListRef unique
;
324 CFTypeID type
= __CFGenericTypeID_genericobj_inline(plist
);
326 CFPropertyListRef
*list
, buffer
[256];
328 // Do not unique dictionaries or arrays, because: they
329 // are slow to compare, and have poor hash codes.
330 // Uniquing bools is unnecessary.
331 if (stringtype
== type
|| numbertype
== type
|| datetype
== type
|| datatype
== type
) {
332 CFIndex before
= CFSetGetCount(uniquingset
);
333 CFSetAddValue(uniquingset
, plist
);
334 CFIndex after
= CFSetGetCount(uniquingset
);
335 if (after
== before
) { // already in set
336 unique
= CFSetGetValue(uniquingset
, plist
);
337 if (unique
!= plist
) {
338 refnum
= (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable
, unique
);
339 CFDictionaryAddValue(objtable
, plist
, (const void *)(uintptr_t)refnum
);
344 refnum
= CFArrayGetCount(objlist
);
345 CFArrayAppendValue(objlist
, plist
);
346 CFDictionaryAddValue(objtable
, plist
, (const void *)(uintptr_t)refnum
);
347 if (dicttype
== type
) {
348 CFIndex count
= CFDictionaryGetCount((CFDictionaryRef
)plist
);
349 list
= (count
<= 128) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, 2 * count
* sizeof(CFTypeRef
), 0);
350 CFDictionaryGetKeysAndValues((CFDictionaryRef
)plist
, list
, list
+ count
);
351 for (idx
= 0; idx
< 2 * count
; idx
++) {
352 _flattenPlist(list
[idx
], objlist
, objtable
, uniquingset
);
354 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
355 } else if (arraytype
== type
) {
356 CFIndex count
= CFArrayGetCount((CFArrayRef
)plist
);
357 list
= (count
<= 256) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, count
* sizeof(CFTypeRef
), 0);
358 CFArrayGetValues((CFArrayRef
)plist
, CFRangeMake(0, count
), list
);
359 for (idx
= 0; idx
< count
; idx
++) {
360 _flattenPlist(list
[idx
], objlist
, objtable
, uniquingset
);
362 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
366 /* Get the number of bytes required to hold the value in 'count'. Will return a power of 2 value big enough to hold 'count'.
368 CF_INLINE
uint8_t _byteCount(uint64_t count
) {
369 uint64_t mask
= ~(uint64_t)0;
372 // Find something big enough to hold 'count'
373 while (count
& mask
) {
378 // Ensure that 'count' is a power of 2
379 // For sizes bigger than 8, just use the required count
380 while ((size
!= 1 && size
!= 2 && size
!= 4 && size
!= 8) && size
<= 8) {
388 // stream must be a CFMutableDataRef
389 /* 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. */
390 CFIndex
__CFBinaryPlistWrite(CFPropertyListRef plist
, CFTypeRef stream
, uint64_t estimate
, CFOptionFlags options
, CFErrorRef
*error
) {
391 CFMutableDictionaryRef objtable
;
392 CFMutableArrayRef objlist
;
393 CFMutableSetRef uniquingset
;
394 CFBinaryPlistTrailer trailer
;
395 uint64_t *offsets
, length_so_far
;
397 int64_t idx
, idx2
, cnt
;
398 __CFBinaryPlistWriteBuffer
*buf
;
402 objtable
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
, NULL
);
403 objlist
= CFArrayCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
);
404 uniquingset
= CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
);
405 #if DEPLOYMENT_TARGET_MACOSX
406 _CFDictionarySetCapacity(objtable
, estimate
? estimate
: 650);
407 _CFArraySetCapacity(objlist
, estimate
? estimate
: 650);
408 _CFSetSetCapacity(uniquingset
, estimate
? estimate
: 1000);
411 _flattenPlist(plist
, objlist
, objtable
, uniquingset
);
413 CFRelease(uniquingset
);
415 cnt
= CFArrayGetCount(objlist
);
416 offsets
= (uint64_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault
, (CFIndex
)(cnt
* sizeof(*offsets
)), 0);
418 buf
= (__CFBinaryPlistWriteBuffer
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, sizeof(__CFBinaryPlistWriteBuffer
), 0);
419 buf
->stream
= stream
;
421 buf
->streamIsData
= (CFGetTypeID(stream
) == CFDataGetTypeID());
424 bufferWrite(buf
, (uint8_t *)"bplist00", 8); // header
426 memset(&trailer
, 0, sizeof(trailer
));
427 trailer
._numObjects
= CFSwapInt64HostToBig(cnt
);
428 trailer
._topObject
= 0; // true for this implementation
429 trailer
._objectRefSize
= _byteCount(cnt
);
430 for (idx
= 0; idx
< cnt
; idx
++) {
431 CFPropertyListRef obj
= CFArrayGetValueAtIndex(objlist
, (CFIndex
)idx
);
432 CFTypeID type
= __CFGenericTypeID_genericobj_inline(obj
);
433 offsets
[idx
] = buf
->written
+ buf
->used
;
434 if (stringtype
== type
) {
435 CFIndex ret
, count
= CFStringGetLength((CFStringRef
)obj
);
437 uint8_t *bytes
, buffer
[1024];
438 bytes
= (count
<= 1024) ? buffer
: (uint8_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault
, count
, 0);
439 // presumption, believed to be true, is that ASCII encoding may need
440 // less bytes, but will not need greater, than the # of unichars
441 ret
= CFStringGetBytes((CFStringRef
)obj
, CFRangeMake(0, count
), kCFStringEncodingASCII
, 0, false, bytes
, count
, &needed
);
443 uint8_t marker
= (uint8_t)(kCFBinaryPlistMarkerASCIIString
| (needed
< 15 ? needed
: 0xf));
444 bufferWrite(buf
, &marker
, 1);
446 _appendInt(buf
, (uint64_t)needed
);
448 bufferWrite(buf
, bytes
, needed
);
451 uint8_t marker
= (uint8_t)(kCFBinaryPlistMarkerUnicode16String
| (count
< 15 ? count
: 0xf));
452 bufferWrite(buf
, &marker
, 1);
454 _appendInt(buf
, (uint64_t)count
);
456 chars
= (UniChar
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, count
* sizeof(UniChar
), 0);
457 CFStringGetCharacters((CFStringRef
)obj
, CFRangeMake(0, count
), chars
);
458 for (idx2
= 0; idx2
< count
; idx2
++) {
459 chars
[idx2
] = CFSwapInt16HostToBig(chars
[idx2
]);
461 bufferWrite(buf
, (uint8_t *)chars
, count
* sizeof(UniChar
));
462 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, chars
);
464 if (bytes
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, bytes
);
465 } else if (numbertype
== type
) {
470 if (CFNumberIsFloatType((CFNumberRef
)obj
)) {
471 CFSwappedFloat64 swapped64
;
472 CFSwappedFloat32 swapped32
;
473 if (CFNumberGetByteSize((CFNumberRef
)obj
) <= (CFIndex
)sizeof(float)) {
475 CFNumberGetValue((CFNumberRef
)obj
, kCFNumberFloat32Type
, &v
);
476 swapped32
= CFConvertFloat32HostToSwapped(v
);
477 bytes
= (uint8_t *)&swapped32
;
478 nbytes
= sizeof(float);
479 marker
= kCFBinaryPlistMarkerReal
| 2;
482 CFNumberGetValue((CFNumberRef
)obj
, kCFNumberFloat64Type
, &v
);
483 swapped64
= CFConvertFloat64HostToSwapped(v
);
484 bytes
= (uint8_t *)&swapped64
;
485 nbytes
= sizeof(double);
486 marker
= kCFBinaryPlistMarkerReal
| 3;
488 bufferWrite(buf
, &marker
, 1);
489 bufferWrite(buf
, bytes
, nbytes
);
491 CFNumberType type
= _CFNumberGetType2((CFNumberRef
)obj
);
492 if (kCFNumberSInt128Type
== type
) {
494 CFNumberGetValue((CFNumberRef
)obj
, kCFNumberSInt128Type
, &s
);
499 storage
.high
= CFSwapInt64HostToBig(s
.high
);
500 storage
.low
= CFSwapInt64HostToBig(s
.low
);
501 uint8_t *bytes
= (uint8_t *)&storage
;
502 uint8_t marker
= kCFBinaryPlistMarkerInt
| 4;
504 bufferWrite(buf
, &marker
, 1);
505 bufferWrite(buf
, bytes
, nbytes
);
507 CFNumberGetValue((CFNumberRef
)obj
, kCFNumberSInt64Type
, &bigint
);
508 _appendInt(buf
, bigint
);
511 } else if (_CFKeyedArchiverUIDGetTypeID() == type
) {
512 _appendUID(buf
, (CFKeyedArchiverUIDRef
)obj
);
513 } else if (booltype
== type
) {
514 uint8_t marker
= CFBooleanGetValue((CFBooleanRef
)obj
) ? kCFBinaryPlistMarkerTrue
: kCFBinaryPlistMarkerFalse
;
515 bufferWrite(buf
, &marker
, 1);
516 } else if (datatype
== type
) {
517 CFIndex count
= CFDataGetLength((CFDataRef
)obj
);
518 uint8_t marker
= (uint8_t)(kCFBinaryPlistMarkerData
| (count
< 15 ? count
: 0xf));
519 bufferWrite(buf
, &marker
, 1);
521 _appendInt(buf
, (uint64_t)count
);
523 bufferWrite(buf
, CFDataGetBytePtr((CFDataRef
)obj
), count
);
524 } else if (datetype
== type
) {
525 CFSwappedFloat64 swapped
;
526 uint8_t marker
= kCFBinaryPlistMarkerDate
;
527 bufferWrite(buf
, &marker
, 1);
528 swapped
= CFConvertFloat64HostToSwapped(CFDateGetAbsoluteTime((CFDateRef
)obj
));
529 bufferWrite(buf
, (uint8_t *)&swapped
, sizeof(swapped
));
530 } else if (dicttype
== type
) {
531 CFIndex count
= CFDictionaryGetCount((CFDictionaryRef
)obj
);
533 uint8_t marker
= (uint8_t)(kCFBinaryPlistMarkerDict
| (count
< 15 ? count
: 0xf));
534 bufferWrite(buf
, &marker
, 1);
536 _appendInt(buf
, (uint64_t)count
);
539 CFPropertyListRef
*list
, buffer
[512];
541 list
= (count
<= 256) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, 2 * count
* sizeof(CFTypeRef
), 0);
542 CFDictionaryGetKeysAndValues((CFDictionaryRef
)obj
, list
, list
+ count
);
543 for (idx2
= 0; idx2
< 2 * count
; idx2
++) {
544 CFPropertyListRef value
= list
[idx2
];
545 uint32_t swapped
= 0;
546 uint8_t *source
= (uint8_t *)&swapped
;
547 refnum
= (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable
, value
);
548 swapped
= CFSwapInt32HostToBig((uint32_t)refnum
);
549 bufferWrite(buf
, source
+ sizeof(swapped
) - trailer
._objectRefSize
, trailer
._objectRefSize
);
551 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
552 } else if (arraytype
== type
) {
553 CFIndex count
= CFArrayGetCount((CFArrayRef
)obj
);
554 CFPropertyListRef
*list
, buffer
[256];
555 uint8_t marker
= (uint8_t)(kCFBinaryPlistMarkerArray
| (count
< 15 ? count
: 0xf));
556 bufferWrite(buf
, &marker
, 1);
558 _appendInt(buf
, (uint64_t)count
);
560 list
= (count
<= 256) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, count
* sizeof(CFTypeRef
), 0);
561 CFArrayGetValues((CFArrayRef
)obj
, CFRangeMake(0, count
), list
);
562 for (idx2
= 0; idx2
< count
; idx2
++) {
563 CFPropertyListRef value
= list
[idx2
];
564 uint32_t swapped
= 0;
565 uint8_t *source
= (uint8_t *)&swapped
;
566 refnum
= (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable
, value
);
567 swapped
= CFSwapInt32HostToBig((uint32_t)refnum
);
568 bufferWrite(buf
, source
+ sizeof(swapped
) - trailer
._objectRefSize
, trailer
._objectRefSize
);
570 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
574 if (error
&& buf
->error
) {
575 // caller will release error
577 } else if (buf
->error
) {
578 // caller is not interested in error, release it here
579 CFRelease(buf
->error
);
581 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, buf
);
582 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, offsets
);
589 length_so_far
= buf
->written
+ buf
->used
;
590 trailer
._offsetTableOffset
= CFSwapInt64HostToBig(length_so_far
);
591 trailer
._offsetIntSize
= _byteCount(length_so_far
);
593 for (idx
= 0; idx
< cnt
; idx
++) {
594 uint64_t swapped
= CFSwapInt64HostToBig(offsets
[idx
]);
595 uint8_t *source
= (uint8_t *)&swapped
;
596 bufferWrite(buf
, source
+ sizeof(*offsets
) - trailer
._offsetIntSize
, trailer
._offsetIntSize
);
598 length_so_far
+= cnt
* trailer
._offsetIntSize
;
600 bufferWrite(buf
, (uint8_t *)&trailer
, sizeof(trailer
));
602 length_so_far
+= sizeof(trailer
);
605 // caller will release error
608 CFRelease(buf
->error
);
612 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, buf
);
613 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, offsets
);
614 return (CFIndex
)length_so_far
;
618 CFIndex
__CFBinaryPlistWriteToStream(CFPropertyListRef plist
, CFTypeRef stream
) {
619 return __CFBinaryPlistWrite(plist
, stream
, 0, 0, NULL
);
622 // to be removed soon
623 CFIndex
__CFBinaryPlistWriteToStreamWithEstimate(CFPropertyListRef plist
, CFTypeRef stream
, uint64_t estimate
) {
624 return __CFBinaryPlistWrite(plist
, stream
, estimate
, 0, NULL
);
627 // to be removed soon
628 CFIndex
__CFBinaryPlistWriteToStreamWithOptions(CFPropertyListRef plist
, CFTypeRef stream
, uint64_t estimate
, CFOptionFlags options
) {
629 return __CFBinaryPlistWrite(plist
, stream
, estimate
, options
, NULL
);
632 #define FAIL_FALSE do { return false; } while (0)
633 #define FAIL_MAXOFFSET do { return UINT64_MAX; } while (0)
635 /* Grab a valSize-bytes integer out of the buffer pointed at by data and return it.
637 CF_INLINE
uint64_t _getSizedInt(const uint8_t *data
, uint8_t valSize
) {
639 return (uint64_t)*data
;
640 } else if (valSize
== 2) {
641 uint16_t val
= *(uint16_t *)data
;
642 return (uint64_t)CFSwapInt16BigToHost(val
);
643 } else if (valSize
== 4) {
644 uint32_t val
= *(uint32_t *)data
;
645 return (uint64_t)CFSwapInt32BigToHost(val
);
646 } else if (valSize
== 8) {
647 uint64_t val
= *(uint64_t *)data
;
648 return CFSwapInt64BigToHost(val
);
650 // Compatability with existing archives, including anything with a non-power-of-2 size and 16-byte values
652 for (CFIndex idx
= 0; idx
< valSize
; idx
++) {
653 res
= (res
<< 8) + data
[idx
];
657 // shouldn't get here
661 bool __CFBinaryPlistGetTopLevelInfo(const uint8_t *databytes
, uint64_t datalen
, uint8_t *marker
, uint64_t *offset
, CFBinaryPlistTrailer
*trailer
) {
662 CFBinaryPlistTrailer trail
;
666 if (!databytes
|| datalen
< sizeof(trail
) + 8 + 1) FAIL_FALSE
;
667 // Tiger and earlier will parse "bplist00"
668 // Leopard will parse "bplist00" or "bplist01"
669 // SnowLeopard will parse "bplist0?" where ? is any one character
670 if (0 != memcmp("bplist0", databytes
, 7)) {
673 memmove(&trail
, databytes
+ datalen
- sizeof(trail
), sizeof(trail
));
674 // In Leopard, the unused bytes in the trailer must be 0 or the parse will fail
675 // This check is not present in Tiger and earlier or after Leopard
676 trail
._numObjects
= CFSwapInt64BigToHost(trail
._numObjects
);
677 trail
._topObject
= CFSwapInt64BigToHost(trail
._topObject
);
678 trail
._offsetTableOffset
= CFSwapInt64BigToHost(trail
._offsetTableOffset
);
679 if (LONG_MAX
< trail
._numObjects
) FAIL_FALSE
;
680 if (LONG_MAX
< trail
._offsetTableOffset
) FAIL_FALSE
;
681 if (trail
._numObjects
< 1) FAIL_FALSE
;
682 if (trail
._numObjects
<= trail
._topObject
) FAIL_FALSE
;
683 if (trail
._offsetTableOffset
< 9) FAIL_FALSE
;
684 if (datalen
- sizeof(trail
) <= trail
._offsetTableOffset
) FAIL_FALSE
;
685 if (trail
._offsetIntSize
< 1) FAIL_FALSE
;
686 if (trail
._objectRefSize
< 1) FAIL_FALSE
;
687 int32_t err
= CF_NO_ERROR
;
688 uint64_t offsetIntSize
= trail
._offsetIntSize
;
689 uint64_t offsetTableSize
= __check_uint64_mul_unsigned_unsigned(trail
._numObjects
, offsetIntSize
, &err
);
690 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
691 if (offsetTableSize
< 1) FAIL_FALSE
;
692 uint64_t objectDataSize
= trail
._offsetTableOffset
- 8;
693 uint64_t tmpSum
= __check_uint64_add_unsigned_unsigned(8, objectDataSize
, &err
);
694 tmpSum
= __check_uint64_add_unsigned_unsigned(tmpSum
, offsetTableSize
, &err
);
695 tmpSum
= __check_uint64_add_unsigned_unsigned(tmpSum
, sizeof(trail
), &err
);
696 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
697 if (datalen
!= tmpSum
) FAIL_FALSE
;
698 if (trail
._objectRefSize
< 8 && (1ULL << (8 * trail
._objectRefSize
)) <= trail
._numObjects
) FAIL_FALSE
;
699 if (trail
._offsetIntSize
< 8 && (1ULL << (8 * trail
._offsetIntSize
)) <= trail
._offsetTableOffset
) FAIL_FALSE
;
700 const uint8_t *objectsFirstByte
;
701 objectsFirstByte
= check_ptr_add(databytes
, 8, &err
);
702 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
703 const uint8_t *offsetsFirstByte
= check_ptr_add(databytes
, trail
._offsetTableOffset
, &err
);
704 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
705 const uint8_t *offsetsLastByte
;
706 offsetsLastByte
= check_ptr_add(offsetsFirstByte
, offsetTableSize
- 1, &err
);
707 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
709 const uint8_t *bytesptr
= databytes
+ trail
._offsetTableOffset
;
710 uint64_t maxOffset
= trail
._offsetTableOffset
- 1;
711 for (CFIndex idx
= 0; idx
< trail
._numObjects
; idx
++) {
712 uint64_t off
= _getSizedInt(bytesptr
, trail
._offsetIntSize
);
713 if (maxOffset
< off
) FAIL_FALSE
;
714 bytesptr
+= trail
._offsetIntSize
;
717 bytesptr
= databytes
+ trail
._offsetTableOffset
+ trail
._topObject
* trail
._offsetIntSize
;
718 uint64_t off
= _getSizedInt(bytesptr
, trail
._offsetIntSize
);
719 if (off
< 8 || trail
._offsetTableOffset
<= off
) FAIL_FALSE
;
720 if (trailer
) *trailer
= trail
;
721 if (offset
) *offset
= off
;
722 if (marker
) *marker
= *(databytes
+ off
);
726 CF_INLINE Boolean
_plistIsPrimitive(CFPropertyListRef pl
) {
727 CFTypeID type
= __CFGenericTypeID_genericobj_inline(pl
);
728 if (dicttype
== type
|| arraytype
== type
|| settype
== type
) FAIL_FALSE
;
732 CF_INLINE
bool _readInt(const uint8_t *ptr
, const uint8_t *end_byte_ptr
, uint64_t *bigint
, const uint8_t **newptr
) {
733 if (end_byte_ptr
< ptr
) FAIL_FALSE
;
734 uint8_t marker
= *ptr
++;
735 if ((marker
& 0xf0) != kCFBinaryPlistMarkerInt
) FAIL_FALSE
;
736 uint64_t cnt
= 1 << (marker
& 0x0f);
737 int32_t err
= CF_NO_ERROR
;
738 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
739 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
740 if (end_byte_ptr
< extent
) FAIL_FALSE
;
741 // integers are not required to be in the most compact possible representation, but only the last 64 bits are significant currently
742 *bigint
= _getSizedInt(ptr
, cnt
);
744 if (newptr
) *newptr
= ptr
;
748 // bytesptr points at a ref
749 CF_INLINE
uint64_t _getOffsetOfRefAt(const uint8_t *databytes
, const uint8_t *bytesptr
, const CFBinaryPlistTrailer
*trailer
) {
750 // *trailer contents are trusted, even for overflows -- was checked when the trailer was parsed;
751 // this pointer arithmetic and the multiplication was also already done once and checked,
752 // and the offsetTable was already validated.
753 const uint8_t *objectsFirstByte
= databytes
+ 8;
754 const uint8_t *offsetsFirstByte
= databytes
+ trailer
->_offsetTableOffset
;
755 if (bytesptr
< objectsFirstByte
|| offsetsFirstByte
- trailer
->_objectRefSize
< bytesptr
) FAIL_MAXOFFSET
;
757 uint64_t ref
= _getSizedInt(bytesptr
, trailer
->_objectRefSize
);
758 if (trailer
->_numObjects
<= ref
) FAIL_MAXOFFSET
;
760 bytesptr
= databytes
+ trailer
->_offsetTableOffset
+ ref
* trailer
->_offsetIntSize
;
761 uint64_t off
= _getSizedInt(bytesptr
, trailer
->_offsetIntSize
);
765 bool __CFBinaryPlistGetOffsetForValueFromArray2(const uint8_t *databytes
, uint64_t datalen
, uint64_t startOffset
, const CFBinaryPlistTrailer
*trailer
, CFIndex idx
, uint64_t *offset
, CFMutableDictionaryRef objects
) {
766 uint64_t objectsRangeStart
= 8, objectsRangeEnd
= trailer
->_offsetTableOffset
- 1;
767 if (startOffset
< objectsRangeStart
|| objectsRangeEnd
< startOffset
) FAIL_FALSE
;
768 const uint8_t *ptr
= databytes
+ startOffset
;
769 uint8_t marker
= *ptr
;
770 if ((marker
& 0xf0) != kCFBinaryPlistMarkerArray
) FAIL_FALSE
;
771 int32_t err
= CF_NO_ERROR
;
772 ptr
= check_ptr_add(ptr
, 1, &err
);
773 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
774 uint64_t cnt
= (marker
& 0x0f);
777 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
778 if (LONG_MAX
< bigint
) FAIL_FALSE
;
781 if (cnt
<= idx
) FAIL_FALSE
;
782 size_t byte_cnt
= check_size_t_mul(cnt
, trailer
->_objectRefSize
, &err
);
783 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
784 const uint8_t *extent
= check_ptr_add(ptr
, byte_cnt
, &err
) - 1;
785 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
786 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
787 uint64_t off
= _getOffsetOfRefAt(databytes
, ptr
+ idx
* trailer
->_objectRefSize
, trailer
);
788 if (offset
) *offset
= off
;
792 // Compatibility method, to be removed soon
793 CF_EXPORT
bool __CFBinaryPlistGetOffsetForValueFromDictionary2(const uint8_t *databytes
, uint64_t datalen
, uint64_t startOffset
, const CFBinaryPlistTrailer
*trailer
, CFTypeRef key
, uint64_t *koffset
, uint64_t *voffset
, CFMutableDictionaryRef objects
) {
794 return __CFBinaryPlistGetOffsetForValueFromDictionary3(databytes
, datalen
, startOffset
, trailer
, key
, koffset
, voffset
, false, objects
);
797 /* Get the offset for a value in a dictionary in a binary property list.
798 @param databytes A pointer to the start of the binary property list data.
799 @param datalen The length of the data.
800 @param startOffset The offset at which the dictionary starts.
801 @param trailer A pointer to a filled out trailer structure (use __CFBinaryPlistGetTopLevelInfo).
802 @param key A string key in the dictionary that should be searched for.
803 @param koffset Will be filled out with the offset to the key in the data bytes.
804 @param voffset Will be filled out with the offset to the value in the data bytes.
805 @param unused Unused parameter.
806 @param objects Used for caching objects. Should be a valid CFMutableDictionaryRef.
807 @return True if the key was found, false otherwise.
809 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
) {
811 // Require a key that is a plist primitive
812 if (!key
|| !_plistIsPrimitive(key
)) FAIL_FALSE
;
814 // Require that startOffset is in the range of the object table
815 uint64_t objectsRangeStart
= 8, objectsRangeEnd
= trailer
->_offsetTableOffset
- 1;
816 if (startOffset
< objectsRangeStart
|| objectsRangeEnd
< startOffset
) FAIL_FALSE
;
818 // ptr is the start of the dictionary we are reading
819 const uint8_t *ptr
= databytes
+ startOffset
;
821 // Check that the data pointer actually points to a dictionary
822 uint8_t marker
= *ptr
;
823 if ((marker
& 0xf0) != kCFBinaryPlistMarkerDict
) FAIL_FALSE
;
825 // Get the number of objects in this dictionary
826 int32_t err
= CF_NO_ERROR
;
827 ptr
= check_ptr_add(ptr
, 1, &err
);
828 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
829 uint64_t cnt
= (marker
& 0x0f);
832 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
833 if (LONG_MAX
< bigint
) FAIL_FALSE
;
837 // Total number of objects (keys + values) is cnt * 2
838 cnt
= check_size_t_mul(cnt
, 2, &err
);
839 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
840 size_t byte_cnt
= check_size_t_mul(cnt
, trailer
->_objectRefSize
, &err
);
841 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
843 // Find the end of the dictionary
844 const uint8_t *extent
= check_ptr_add(ptr
, byte_cnt
, &err
) - 1;
845 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
847 // Check that we didn't overflow the size of the dictionary
848 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
850 // For short keys (15 bytes or less) in ASCII form, we can do a quick comparison check
851 // We get the pointer or copy the buffer here, outside of the loop
852 CFIndex stringKeyLen
= -1;
853 if (__CFGenericTypeID_genericobj_inline(key
) == stringtype
) {
854 stringKeyLen
= CFStringGetLength((CFStringRef
)key
);
857 // Find the object in the dictionary with this key
859 uint64_t totalKeySize
= cnt
* trailer
->_objectRefSize
;
861 Boolean match
= false;
862 CFPropertyListRef keyInData
= NULL
;
865 const char *keyBufferPtr
= keyBuffer
;
867 if (stringKeyLen
< 0xf) {
868 // Since we will only be comparing ASCII strings, we can attempt to get a pointer using MacRoman encoding
869 // (this is cheaper than a copy)
870 if (!(keyBufferPtr
= CFStringGetCStringPtr((CFStringRef
)key
, kCFStringEncodingMacRoman
))) {
871 CFStringGetCString((CFStringRef
)key
, keyBuffer
, 16, kCFStringEncodingMacRoman
);
872 // The pointer should now point to our keyBuffer instead of the original string buffer, since we've copied it
873 keyBufferPtr
= keyBuffer
;
877 // Perform linear search of the keys
878 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
879 off
= _getOffsetOfRefAt(databytes
, ptr
, trailer
);
880 marker
= *(databytes
+ off
);
881 CFIndex len
= marker
& 0x0f;
882 // if it is a short ascii string in the data, and the key is a string
883 if (stringKeyLen
!= -1 && len
< 0xf && (marker
& 0xf0) == kCFBinaryPlistMarkerASCIIString
) {
884 if (len
== stringKeyLen
) {
886 const uint8_t *ptr2
= databytes
+ off
;
887 extent
= check_ptr_add(ptr2
, len
, &err
);
888 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
890 if (databytes
+ trailer
->_offsetTableOffset
<= extent
) FAIL_FALSE
;
892 // Compare the key to this potential match (ptr2 + 1 moves past the marker)
893 if (memcmp(ptr2
+ 1, keyBufferPtr
, stringKeyLen
) == 0) {
899 if (!__CFBinaryPlistCreateObject2(databytes
, datalen
, off
, trailer
, kCFAllocatorSystemDefault
, kCFPropertyListImmutable
, objects
, NULL
, 0, &keyInData
) || !_plistIsPrimitive(keyInData
)) {
900 if (keyInData
) CFRelease(keyInData
);
904 match
= CFEqual(key
, keyInData
);
905 CFRelease(keyInData
);
909 if (koffset
) *koffset
= off
;
910 if (voffset
) *voffset
= _getOffsetOfRefAt(databytes
, ptr
+ totalKeySize
, trailer
);
914 ptr
+= trailer
->_objectRefSize
;
920 CF_EXPORT
bool __CFBinaryPlistCreateObject2(const uint8_t *databytes
, uint64_t datalen
, uint64_t startOffset
, const CFBinaryPlistTrailer
*trailer
, CFAllocatorRef allocator
, CFOptionFlags mutabilityOption
, CFMutableDictionaryRef objects
, CFMutableSetRef set
, CFIndex curDepth
, CFPropertyListRef
*plist
) {
923 *plist
= CFDictionaryGetValue(objects
, (const void *)(uintptr_t)startOffset
);
930 // at any one invocation of this function, set should contain the offsets in the "path" down to this object
931 if (set
&& CFSetContainsValue(set
, (const void *)(uintptr_t)startOffset
)) return false;
933 // databytes is trusted to be at least datalen bytes long
934 // *trailer contents are trusted, even for overflows -- was checked when the trailer was parsed
935 uint64_t objectsRangeStart
= 8, objectsRangeEnd
= trailer
->_offsetTableOffset
- 1;
936 if (startOffset
< objectsRangeStart
|| objectsRangeEnd
< startOffset
) FAIL_FALSE
;
939 CFPropertyListRef
*list
, buffer
[256];
940 CFAllocatorRef listAllocator
;
942 uint8_t marker
= *(databytes
+ startOffset
);
943 switch (marker
& 0xf0) {
944 case kCFBinaryPlistMarkerNull
:
946 case kCFBinaryPlistMarkerNull
:
949 case kCFBinaryPlistMarkerFalse
:
950 *plist
= CFRetain(kCFBooleanFalse
);
952 case kCFBinaryPlistMarkerTrue
:
953 *plist
= CFRetain(kCFBooleanTrue
);
957 case kCFBinaryPlistMarkerInt
:
959 const uint8_t *ptr
= (databytes
+ startOffset
);
960 int32_t err
= CF_NO_ERROR
;
961 ptr
= check_ptr_add(ptr
, 1, &err
);
962 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
963 uint64_t cnt
= 1 << (marker
& 0x0f);
964 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
965 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
966 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
967 if (16 < cnt
) FAIL_FALSE
;
968 // in format version '00', 1, 2, and 4-byte integers have to be interpreted as unsigned,
969 // whereas 8-byte integers are signed (and 16-byte when available)
970 // negative 1, 2, 4-byte integers are always emitted as 8 bytes in format '00'
971 // integers are not required to be in the most compact possible representation, but only the last 64 bits are significant currently
972 uint64_t bigint
= _getSizedInt(ptr
, cnt
);
978 *plist
= CFNumberCreate(allocator
, kCFNumberSInt128Type
, &val
);
980 *plist
= CFNumberCreate(allocator
, kCFNumberSInt64Type
, &bigint
);
982 // these are always immutable
983 if (objects
&& *plist
) {
984 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
986 return (*plist
) ? true : false;
988 case kCFBinaryPlistMarkerReal
:
989 switch (marker
& 0x0f) {
991 const uint8_t *ptr
= (databytes
+ startOffset
);
992 int32_t err
= CF_NO_ERROR
;
993 ptr
= check_ptr_add(ptr
, 1, &err
);
994 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
995 const uint8_t *extent
= check_ptr_add(ptr
, 4, &err
) - 1;
996 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
997 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
998 CFSwappedFloat32 swapped32
;
999 memmove(&swapped32
, ptr
, 4);
1000 float f
= CFConvertFloat32SwappedToHost(swapped32
);
1001 *plist
= CFNumberCreate(allocator
, kCFNumberFloat32Type
, &f
);
1002 // these are always immutable
1003 if (objects
&& *plist
) {
1004 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1006 return (*plist
) ? true : false;
1009 const uint8_t *ptr
= (databytes
+ startOffset
);
1010 int32_t err
= CF_NO_ERROR
;
1011 ptr
= check_ptr_add(ptr
, 1, &err
);
1012 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1013 const uint8_t *extent
= check_ptr_add(ptr
, 8, &err
) - 1;
1014 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1015 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1016 CFSwappedFloat64 swapped64
;
1017 memmove(&swapped64
, ptr
, 8);
1018 double d
= CFConvertFloat64SwappedToHost(swapped64
);
1019 *plist
= CFNumberCreate(allocator
, kCFNumberFloat64Type
, &d
);
1020 // these are always immutable
1021 if (objects
&& *plist
) {
1022 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1024 return (*plist
) ? true : false;
1028 case kCFBinaryPlistMarkerDate
& 0xf0:
1030 case kCFBinaryPlistMarkerDate
: {
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 const uint8_t *extent
= check_ptr_add(ptr
, 8, &err
) - 1;
1036 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1037 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1038 CFSwappedFloat64 swapped64
;
1039 memmove(&swapped64
, ptr
, 8);
1040 double d
= CFConvertFloat64SwappedToHost(swapped64
);
1041 *plist
= CFDateCreate(allocator
, d
);
1042 // these are always immutable
1043 if (objects
&& *plist
) {
1044 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1046 return (*plist
) ? true : false;
1050 case kCFBinaryPlistMarkerData
: {
1051 const uint8_t *ptr
= databytes
+ startOffset
;
1052 int32_t err
= CF_NO_ERROR
;
1053 ptr
= check_ptr_add(ptr
, 1, &err
);
1054 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1055 CFIndex cnt
= marker
& 0x0f;
1057 uint64_t bigint
= 0;
1058 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
1059 if (LONG_MAX
< bigint
) FAIL_FALSE
;
1060 cnt
= (CFIndex
)bigint
;
1062 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
1063 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1064 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1065 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
1066 *plist
= CFDataCreateMutable(allocator
, 0);
1067 if (*plist
) CFDataAppendBytes((CFMutableDataRef
)*plist
, ptr
, cnt
);
1069 *plist
= CFDataCreate(allocator
, ptr
, cnt
);
1071 if (objects
&& *plist
&& (mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
)) {
1072 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1074 return (*plist
) ? true : false;
1076 case kCFBinaryPlistMarkerASCIIString
: {
1077 const uint8_t *ptr
= databytes
+ startOffset
;
1078 int32_t err
= CF_NO_ERROR
;
1079 ptr
= check_ptr_add(ptr
, 1, &err
);
1080 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1081 CFIndex cnt
= marker
& 0x0f;
1083 uint64_t bigint
= 0;
1084 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
1085 if (LONG_MAX
< bigint
) FAIL_FALSE
;
1086 cnt
= (CFIndex
)bigint
;
1088 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
1089 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1090 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1091 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
1092 CFStringRef str
= CFStringCreateWithBytes(allocator
, ptr
, cnt
, kCFStringEncodingASCII
, false);
1093 *plist
= str
? CFStringCreateMutableCopy(allocator
, 0, str
) : NULL
;
1094 if (str
) CFRelease(str
);
1096 *plist
= CFStringCreateWithBytes(allocator
, ptr
, cnt
, kCFStringEncodingASCII
, false);
1098 if (objects
&& *plist
&& (mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
)) {
1099 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1101 return (*plist
) ? true : false;
1103 case kCFBinaryPlistMarkerUnicode16String
: {
1104 const uint8_t *ptr
= databytes
+ startOffset
;
1105 int32_t err
= CF_NO_ERROR
;
1106 ptr
= check_ptr_add(ptr
, 1, &err
);
1107 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1108 CFIndex cnt
= marker
& 0x0f;
1110 uint64_t bigint
= 0;
1111 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
1112 if (LONG_MAX
< bigint
) FAIL_FALSE
;
1113 cnt
= (CFIndex
)bigint
;
1115 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
1116 extent
= check_ptr_add(extent
, cnt
, &err
); // 2 bytes per character
1117 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1118 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1119 size_t byte_cnt
= check_size_t_mul(cnt
, sizeof(UniChar
), &err
);
1120 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1121 UniChar
*chars
= (UniChar
*)CFAllocatorAllocate(allocator
, byte_cnt
, 0);
1122 if (!chars
) FAIL_FALSE
;
1123 memmove(chars
, ptr
, byte_cnt
);
1124 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
1125 chars
[idx
] = CFSwapInt16BigToHost(chars
[idx
]);
1127 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
1128 CFStringRef str
= CFStringCreateWithCharactersNoCopy(allocator
, chars
, cnt
, allocator
);
1129 *plist
= str
? CFStringCreateMutableCopy(allocator
, 0, str
) : NULL
;
1130 if (str
) CFRelease(str
);
1132 *plist
= CFStringCreateWithCharactersNoCopy(allocator
, chars
, cnt
, allocator
);
1134 if (objects
&& *plist
&& (mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
)) {
1135 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1137 return (*plist
) ? true : false;
1139 case kCFBinaryPlistMarkerUID
: {
1140 const uint8_t *ptr
= databytes
+ startOffset
;
1141 int32_t err
= CF_NO_ERROR
;
1142 ptr
= check_ptr_add(ptr
, 1, &err
);
1143 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1144 CFIndex cnt
= (marker
& 0x0f) + 1;
1145 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
1146 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1147 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1148 // uids are not required to be in the most compact possible representation, but only the last 64 bits are significant currently
1149 uint64_t bigint
= _getSizedInt(ptr
, cnt
);
1151 if (UINT32_MAX
< bigint
) FAIL_FALSE
;
1152 *plist
= _CFKeyedArchiverUIDCreate(allocator
, (uint32_t)bigint
);
1153 // these are always immutable
1154 if (objects
&& *plist
) {
1155 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1157 return (*plist
) ? true : false;
1159 case kCFBinaryPlistMarkerArray
:
1160 case kCFBinaryPlistMarkerSet
: {
1161 const uint8_t *ptr
= databytes
+ startOffset
;
1162 int32_t err
= CF_NO_ERROR
;
1163 ptr
= check_ptr_add(ptr
, 1, &err
);
1164 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1165 CFIndex cnt
= marker
& 0x0f;
1167 uint64_t bigint
= 0;
1168 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
1169 if (LONG_MAX
< bigint
) FAIL_FALSE
;
1170 cnt
= (CFIndex
)bigint
;
1172 size_t byte_cnt
= check_size_t_mul(cnt
, trailer
->_objectRefSize
, &err
);
1173 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1174 const uint8_t *extent
= check_ptr_add(ptr
, byte_cnt
, &err
) - 1;
1175 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1176 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1177 byte_cnt
= check_size_t_mul(cnt
, sizeof(CFPropertyListRef
), &err
);
1178 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1179 list
= (cnt
<= 256) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, byte_cnt
, 0);
1180 listAllocator
= (list
== buffer
? kCFAllocatorNull
: kCFAllocatorSystemDefault
);
1181 if (!list
) FAIL_FALSE
;
1182 Boolean madeSet
= false;
1183 if (!set
&& 15 < curDepth
) {
1184 set
= CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
);
1185 madeSet
= set
? true : false;
1187 if (set
) CFSetAddValue(set
, (const void *)(uintptr_t)startOffset
);
1188 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
1189 CFPropertyListRef pl
;
1190 off
= _getOffsetOfRefAt(databytes
, ptr
, trailer
);
1191 if (!__CFBinaryPlistCreateObject2(databytes
, datalen
, off
, trailer
, allocator
, mutabilityOption
, objects
, set
, curDepth
+ 1, &pl
)) {
1193 CFRelease(list
[idx
]);
1195 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
1199 ptr
+= trailer
->_objectRefSize
;
1201 if (set
) CFSetRemoveValue(set
, (const void *)(uintptr_t)startOffset
);
1206 if ((marker
& 0xf0) == kCFBinaryPlistMarkerArray
) {
1207 if (mutabilityOption
!= kCFPropertyListImmutable
) {
1208 *plist
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
1209 CFArrayReplaceValues((CFMutableArrayRef
)*plist
, CFRangeMake(0, 0), list
, cnt
);
1211 *plist
= CFArrayCreate(allocator
, list
, cnt
, &kCFTypeArrayCallBacks
);
1214 if (mutabilityOption
!= kCFPropertyListImmutable
) {
1215 *plist
= CFSetCreateMutable(allocator
, 0, &kCFTypeSetCallBacks
);
1216 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
1217 CFSetAddValue((CFMutableSetRef
)*plist
, list
[idx
]);
1220 *plist
= CFSetCreate(allocator
, list
, cnt
, &kCFTypeSetCallBacks
);
1223 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
1224 CFRelease(list
[idx
]);
1226 if (objects
&& *plist
&& (mutabilityOption
== kCFPropertyListImmutable
)) {
1227 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1229 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
1230 return (*plist
) ? true : false;
1232 case kCFBinaryPlistMarkerDict
: {
1233 const uint8_t *ptr
= databytes
+ startOffset
;
1234 int32_t err
= CF_NO_ERROR
;
1235 ptr
= check_ptr_add(ptr
, 1, &err
);
1236 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1237 CFIndex cnt
= marker
& 0x0f;
1239 uint64_t bigint
= 0;
1240 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
1241 if (LONG_MAX
< bigint
) FAIL_FALSE
;
1242 cnt
= (CFIndex
)bigint
;
1244 cnt
= check_size_t_mul(cnt
, 2, &err
);
1245 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1246 size_t byte_cnt
= check_size_t_mul(cnt
, trailer
->_objectRefSize
, &err
);
1247 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1248 const uint8_t *extent
= check_ptr_add(ptr
, byte_cnt
, &err
) - 1;
1249 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1250 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1251 byte_cnt
= check_size_t_mul(cnt
, sizeof(CFPropertyListRef
), &err
);
1252 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1253 list
= (cnt
<= 256) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, byte_cnt
, 0);
1254 listAllocator
= (list
== buffer
? kCFAllocatorNull
: kCFAllocatorSystemDefault
);
1255 if (!list
) FAIL_FALSE
;
1256 Boolean madeSet
= false;
1257 if (!set
&& 15 < curDepth
) {
1258 set
= CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
);
1259 madeSet
= set
? true : false;
1261 if (set
) CFSetAddValue(set
, (const void *)(uintptr_t)startOffset
);
1262 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
1263 CFPropertyListRef pl
= NULL
;
1264 off
= _getOffsetOfRefAt(databytes
, ptr
, trailer
);
1265 if (!__CFBinaryPlistCreateObject2(databytes
, datalen
, off
, trailer
, allocator
, mutabilityOption
, objects
, set
, curDepth
+ 1, &pl
) || (idx
< cnt
/ 2 && !_plistIsPrimitive(pl
))) {
1266 if (pl
) CFRelease(pl
);
1268 CFRelease(list
[idx
]);
1270 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
1274 ptr
+= trailer
->_objectRefSize
;
1276 if (set
) CFSetRemoveValue(set
, (const void *)(uintptr_t)startOffset
);
1281 if (mutabilityOption
!= kCFPropertyListImmutable
) {
1282 *plist
= CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1283 for (CFIndex idx
= 0; idx
< cnt
/ 2; idx
++) {
1284 CFDictionaryAddValue((CFMutableDictionaryRef
)*plist
, list
[idx
], list
[idx
+ cnt
/ 2]);
1287 *plist
= CFDictionaryCreate(allocator
, list
, list
+ cnt
/ 2, cnt
/ 2, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1289 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
1290 CFRelease(list
[idx
]);
1292 if (objects
&& *plist
&& (mutabilityOption
== kCFPropertyListImmutable
)) {
1293 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1295 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
1296 return (*plist
) ? true : false;
1302 bool __CFBinaryPlistCreateObject(const uint8_t *databytes
, uint64_t datalen
, uint64_t startOffset
, const CFBinaryPlistTrailer
*trailer
, CFAllocatorRef allocator
, CFOptionFlags mutabilityOption
, CFMutableDictionaryRef objects
, CFPropertyListRef
*plist
) {
1303 // for compatibility with Foundation's use, need to leave this here
1304 return __CFBinaryPlistCreateObject2(databytes
, datalen
, startOffset
, trailer
, allocator
, mutabilityOption
, objects
, NULL
, 0, plist
);
1307 __private_extern__
bool __CFTryParseBinaryPlist(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags option
, CFPropertyListRef
*plist
, CFStringRef
*errorString
) {
1309 CFBinaryPlistTrailer trailer
;
1311 const uint8_t *databytes
= CFDataGetBytePtr(data
);
1312 uint64_t datalen
= CFDataGetLength(data
);
1314 if (8 <= datalen
&& __CFBinaryPlistGetTopLevelInfo(databytes
, datalen
, &marker
, &offset
, &trailer
)) {
1315 // FALSE: We know for binary plist parsing that the result objects will be retained
1316 // by their containing collections as the parsing proceeds, so we do not need
1317 // to use retaining callbacks for the objects map in this case. WHY: the file might
1318 // be malformed and contain hash-equal keys for the same dictionary (for example)
1319 // and the later key will cause the previous one to be released when we set the second
1320 // in the dictionary.
1321 CFMutableDictionaryRef objects
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
, &kCFTypeDictionaryValueCallBacks
);
1322 _CFDictionarySetCapacity(objects
, trailer
._numObjects
);
1323 CFPropertyListRef pl
= NULL
;
1324 if (__CFBinaryPlistCreateObject2(databytes
, datalen
, offset
, &trailer
, allocator
, option
, objects
, NULL
, 0, &pl
)) {
1325 if (plist
) *plist
= pl
;
1327 if (plist
) *plist
= NULL
;
1328 if (errorString
) *errorString
= (CFStringRef
)CFRetain(CFSTR("binary data is corrupt"));