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@
24 Copyright (c) 2000-2009, Apple Inc. All rights reserved.
25 Responsibility: Tony Parker
29 #include <CoreFoundation/CFString.h>
30 #include <CoreFoundation/CFNumber.h>
31 #include <CoreFoundation/CFDate.h>
32 #include <CoreFoundation/CFData.h>
33 #include <CoreFoundation/CFError.h>
34 #include <CoreFoundation/CFArray.h>
35 #include <CoreFoundation/CFDictionary.h>
36 #include <CoreFoundation/CFSet.h>
37 #include <CoreFoundation/CFPropertyList.h>
38 #include <CoreFoundation/CFByteOrder.h>
39 #include <CoreFoundation/CFRuntime.h>
43 #include "CFInternal.h"
51 kCFNumberSInt128Type
= 17
54 CF_EXPORT CFNumberType
_CFNumberGetType2(CFNumberRef number
);
55 __private_extern__ CFErrorRef
__CFPropertyListCreateError(CFAllocatorRef allocator
, CFIndex code
, CFStringRef debugString
, ...);
59 CF_OVERFLOW_ERROR
= (1 << 0),
62 CF_INLINE
uint32_t __check_uint32_add_unsigned_unsigned(uint32_t x
, uint32_t y
, int32_t* err
) {
63 if((UINT_MAX
- y
) < x
)
64 *err
= *err
| CF_OVERFLOW_ERROR
;
68 CF_INLINE
uint64_t __check_uint64_add_unsigned_unsigned(uint64_t x
, uint64_t y
, int32_t* err
) {
69 if((ULLONG_MAX
- y
) < x
)
70 *err
= *err
| CF_OVERFLOW_ERROR
;
74 CF_INLINE
uint32_t __check_uint32_mul_unsigned_unsigned(uint32_t x
, uint32_t y
, int32_t* err
) {
75 uint64_t tmp
= (uint64_t) x
* (uint64_t) y
;
76 /* If any of the upper 32 bits touched, overflow */
77 if(tmp
& 0xffffffff00000000ULL
)
78 *err
= *err
| CF_OVERFLOW_ERROR
;
79 return (uint32_t) tmp
;
82 CF_INLINE
uint64_t __check_uint64_mul_unsigned_unsigned(uint64_t x
, uint64_t y
, int32_t* err
) {
85 *err
= *err
| CF_OVERFLOW_ERROR
;
90 #define check_ptr_add(p, a, err) (const uint8_t *)__check_uint64_add_unsigned_unsigned((uintptr_t)p, (uintptr_t)a, err)
91 #define check_size_t_mul(b, a, err) (size_t)__check_uint64_mul_unsigned_unsigned((size_t)b, (size_t)a, err)
93 #define check_ptr_add(p, a, err) (const uint8_t *)__check_uint32_add_unsigned_unsigned((uintptr_t)p, (uintptr_t)a, err)
94 #define check_size_t_mul(b, a, err) (size_t)__check_uint32_mul_unsigned_unsigned((size_t)b, (size_t)a, err)
98 CF_INLINE CFTypeID
__CFGenericTypeID_genericobj_inline(const void *cf
) {
99 CFTypeID typeID
= (*(uint32_t *)(((CFRuntimeBase
*)cf
)->_cfinfo
) >> 8) & 0xFFFF;
100 return CF_IS_OBJC(typeID
, cf
) ? CFGetTypeID(cf
) : typeID
;
103 struct __CFKeyedArchiverUID
{
108 static CFStringRef
__CFKeyedArchiverUIDCopyDescription(CFTypeRef cf
) {
109 CFKeyedArchiverUIDRef uid
= (CFKeyedArchiverUIDRef
)cf
;
110 return CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("<CFKeyedArchiverUID %p [%p]>{value = %u}"), cf
, CFGetAllocator(cf
), uid
->_value
);
113 static CFStringRef
__CFKeyedArchiverUIDCopyFormattingDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
114 CFKeyedArchiverUIDRef uid
= (CFKeyedArchiverUIDRef
)cf
;
115 return CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("@%u@"), uid
->_value
);
118 static CFTypeID __kCFKeyedArchiverUIDTypeID
= _kCFRuntimeNotATypeID
;
120 static const CFRuntimeClass __CFKeyedArchiverUIDClass
= {
122 "CFKeyedArchiverUID",
126 NULL
, // equal -- pointer equality only
127 NULL
, // hash -- pointer hashing only
128 __CFKeyedArchiverUIDCopyFormattingDescription
,
129 __CFKeyedArchiverUIDCopyDescription
132 __private_extern__
void __CFKeyedArchiverUIDInitialize(void) {
133 __kCFKeyedArchiverUIDTypeID
= _CFRuntimeRegisterClass(&__CFKeyedArchiverUIDClass
);
136 CFTypeID
_CFKeyedArchiverUIDGetTypeID(void) {
137 return __kCFKeyedArchiverUIDTypeID
;
140 CFKeyedArchiverUIDRef
_CFKeyedArchiverUIDCreate(CFAllocatorRef allocator
, uint32_t value
) {
141 CFKeyedArchiverUIDRef uid
;
142 uid
= (CFKeyedArchiverUIDRef
)_CFRuntimeCreateInstance(allocator
, __kCFKeyedArchiverUIDTypeID
, sizeof(struct __CFKeyedArchiverUID
) - sizeof(CFRuntimeBase
), NULL
);
146 ((struct __CFKeyedArchiverUID
*)uid
)->_value
= value
;
151 uint32_t _CFKeyedArchiverUIDGetValue(CFKeyedArchiverUIDRef uid
) {
162 uint8_t buffer
[8192 - 32];
163 } __CFBinaryPlistWriteBuffer
;
165 static void writeBytes(__CFBinaryPlistWriteBuffer
*buf
, const UInt8
*bytes
, CFIndex length
) {
166 if (0 == length
) return;
167 if (buf
->error
) return;
168 if (buf
->streamIsData
) {
169 CFDataAppendBytes((CFMutableDataRef
)buf
->stream
, bytes
, length
);
170 buf
->written
+= length
;
172 CFAssert(false, __kCFLogAssertion
, "Streams are not supported on this platform");
176 static void bufferWrite(__CFBinaryPlistWriteBuffer
*buf
, const uint8_t *buffer
, CFIndex count
) {
177 if (0 == count
) return;
178 if ((CFIndex
)sizeof(buf
->buffer
) <= count
) {
179 writeBytes(buf
, buf
->buffer
, buf
->used
);
181 writeBytes(buf
, buffer
, count
);
184 CFIndex copyLen
= __CFMin(count
, (CFIndex
)sizeof(buf
->buffer
) - buf
->used
);
185 memmove(buf
->buffer
+ buf
->used
, buffer
, copyLen
);
186 buf
->used
+= copyLen
;
187 if (sizeof(buf
->buffer
) == buf
->used
) {
188 writeBytes(buf
, buf
->buffer
, sizeof(buf
->buffer
));
189 memmove(buf
->buffer
, buffer
+ copyLen
, count
- copyLen
);
190 buf
->used
= count
- copyLen
;
194 static void bufferFlush(__CFBinaryPlistWriteBuffer
*buf
) {
195 writeBytes(buf
, buf
->buffer
, buf
->used
);
201 magic number ("bplist")
205 variable-sized objects
207 Object Formats (marker byte followed by additional info in some cases)
209 bool 0000 1000 // false
210 bool 0000 1001 // true
211 fill 0000 1111 // fill byte
212 int 0001 nnnn ... // # of bytes is 2^nnnn, big-endian bytes
213 real 0010 nnnn ... // # of bytes is 2^nnnn, big-endian bytes
214 date 0011 0011 ... // 8 byte float follows, big-endian bytes
215 data 0100 nnnn [int] ... // nnnn is number of bytes unless 1111 then int count follows, followed by bytes
216 string 0101 nnnn [int] ... // ASCII string, nnnn is # of chars, else 1111 then int count, then bytes
217 string 0110 nnnn [int] ... // Unicode string, nnnn is # of chars, else 1111 then int count, then big-endian 2-byte uint16_t
219 uid 1000 nnnn ... // nnnn+1 is # of bytes
221 array 1010 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows
223 set 1100 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows
224 dict 1101 nnnn [int] keyref* objref* // nnnn is count, unless '1111', then int count follows
229 list of ints, byte size of which is given in trailer
230 -- these are the byte offsets into the file
231 -- number of these is in the trailer
234 byte size of offset ints in offset table
235 byte size of object refs in arrays and dicts
236 number of offsets in offset table (also is number of objects)
237 element # in offset table which is top level object
243 static CFTypeID stringtype
= -1, datatype
= -1, numbertype
= -1, datetype
= -1;
244 static CFTypeID booltype
= -1, nulltype
= -1, dicttype
= -1, arraytype
= -1, settype
= -1;
246 static void initStatics() {
247 if ((CFTypeID
)-1 == stringtype
) {
248 stringtype
= CFStringGetTypeID();
250 if ((CFTypeID
)-1 == datatype
) {
251 datatype
= CFDataGetTypeID();
253 if ((CFTypeID
)-1 == numbertype
) {
254 numbertype
= CFNumberGetTypeID();
256 if ((CFTypeID
)-1 == booltype
) {
257 booltype
= CFBooleanGetTypeID();
259 if ((CFTypeID
)-1 == datetype
) {
260 datetype
= CFDateGetTypeID();
262 if ((CFTypeID
)-1 == dicttype
) {
263 dicttype
= CFDictionaryGetTypeID();
265 if ((CFTypeID
)-1 == arraytype
) {
266 arraytype
= CFArrayGetTypeID();
268 if ((CFTypeID
)-1 == settype
) {
269 settype
= CFSetGetTypeID();
271 if ((CFTypeID
)-1 == nulltype
) {
272 nulltype
= CFNullGetTypeID();
276 static void _appendInt(__CFBinaryPlistWriteBuffer
*buf
, uint64_t bigint
) {
280 if (bigint
<= (uint64_t)0xff) {
282 marker
= kCFBinaryPlistMarkerInt
| 0;
283 } else if (bigint
<= (uint64_t)0xffff) {
285 marker
= kCFBinaryPlistMarkerInt
| 1;
286 } else if (bigint
<= (uint64_t)0xffffffff) {
288 marker
= kCFBinaryPlistMarkerInt
| 2;
291 marker
= kCFBinaryPlistMarkerInt
| 3;
293 bigint
= CFSwapInt64HostToBig(bigint
);
294 bytes
= (uint8_t *)&bigint
+ sizeof(bigint
) - nbytes
;
295 bufferWrite(buf
, &marker
, 1);
296 bufferWrite(buf
, bytes
, nbytes
);
299 static void _appendUID(__CFBinaryPlistWriteBuffer
*buf
, CFKeyedArchiverUIDRef uid
) {
303 uint64_t bigint
= _CFKeyedArchiverUIDGetValue(uid
);
304 if (bigint
<= (uint64_t)0xff) {
306 } else if (bigint
<= (uint64_t)0xffff) {
308 } else if (bigint
<= (uint64_t)0xffffffff) {
313 marker
= kCFBinaryPlistMarkerUID
| (uint8_t)(nbytes
- 1);
314 bigint
= CFSwapInt64HostToBig(bigint
);
315 bytes
= (uint8_t *)&bigint
+ sizeof(bigint
) - nbytes
;
316 bufferWrite(buf
, &marker
, 1);
317 bufferWrite(buf
, bytes
, nbytes
);
320 static void _flattenPlist(CFPropertyListRef plist
, CFMutableArrayRef objlist
, CFMutableDictionaryRef objtable
, CFMutableSetRef uniquingset
) {
321 CFPropertyListRef unique
;
323 CFTypeID type
= __CFGenericTypeID_genericobj_inline(plist
);
325 CFPropertyListRef
*list
, buffer
[256];
327 // Do not unique dictionaries or arrays, because: they
328 // are slow to compare, and have poor hash codes.
329 // Uniquing bools is unnecessary.
330 if (stringtype
== type
|| numbertype
== type
|| datetype
== type
|| datatype
== type
) {
331 CFIndex before
= CFSetGetCount(uniquingset
);
332 CFSetAddValue(uniquingset
, plist
);
333 CFIndex after
= CFSetGetCount(uniquingset
);
334 if (after
== before
) { // already in set
335 unique
= CFSetGetValue(uniquingset
, plist
);
336 if (unique
!= plist
) {
337 refnum
= (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable
, unique
);
338 CFDictionaryAddValue(objtable
, plist
, (const void *)(uintptr_t)refnum
);
343 refnum
= CFArrayGetCount(objlist
);
344 CFArrayAppendValue(objlist
, plist
);
345 CFDictionaryAddValue(objtable
, plist
, (const void *)(uintptr_t)refnum
);
346 if (dicttype
== type
) {
347 CFIndex count
= CFDictionaryGetCount((CFDictionaryRef
)plist
);
348 list
= (count
<= 128) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, 2 * count
* sizeof(CFTypeRef
), 0);
349 CFDictionaryGetKeysAndValues((CFDictionaryRef
)plist
, list
, list
+ count
);
350 for (idx
= 0; idx
< 2 * count
; idx
++) {
351 _flattenPlist(list
[idx
], objlist
, objtable
, uniquingset
);
353 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
354 } else if (arraytype
== type
) {
355 CFIndex count
= CFArrayGetCount((CFArrayRef
)plist
);
356 list
= (count
<= 256) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, count
* sizeof(CFTypeRef
), 0);
357 CFArrayGetValues((CFArrayRef
)plist
, CFRangeMake(0, count
), list
);
358 for (idx
= 0; idx
< count
; idx
++) {
359 _flattenPlist(list
[idx
], objlist
, objtable
, uniquingset
);
361 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
365 /* Get the number of bytes required to hold the value in 'count'. Will return a power of 2 value big enough to hold 'count'.
367 CF_INLINE
uint8_t _byteCount(uint64_t count
) {
368 uint64_t mask
= ~(uint64_t)0;
371 // Find something big enough to hold 'count'
372 while (count
& mask
) {
377 // Ensure that 'count' is a power of 2
378 // For sizes bigger than 8, just use the required count
379 while ((size
!= 1 && size
!= 2 && size
!= 4 && size
!= 8) && size
<= 8) {
387 // stream must be a CFMutableDataRef
388 /* 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. */
389 CFIndex
__CFBinaryPlistWrite(CFPropertyListRef plist
, CFTypeRef stream
, uint64_t estimate
, CFOptionFlags options
, CFErrorRef
*error
) {
390 CFMutableDictionaryRef objtable
;
391 CFMutableArrayRef objlist
;
392 CFMutableSetRef uniquingset
;
393 CFBinaryPlistTrailer trailer
;
394 uint64_t *offsets
, length_so_far
;
396 int64_t idx
, idx2
, cnt
;
397 __CFBinaryPlistWriteBuffer
*buf
;
401 objtable
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
, NULL
);
402 objlist
= CFArrayCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
);
403 uniquingset
= CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
);
404 #if DEPLOYMENT_TARGET_MACOSX
405 _CFDictionarySetCapacity(objtable
, estimate
? estimate
: 650);
406 _CFArraySetCapacity(objlist
, estimate
? estimate
: 650);
407 _CFSetSetCapacity(uniquingset
, estimate
? estimate
: 1000);
410 _flattenPlist(plist
, objlist
, objtable
, uniquingset
);
412 CFRelease(uniquingset
);
414 cnt
= CFArrayGetCount(objlist
);
415 offsets
= (uint64_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault
, (CFIndex
)(cnt
* sizeof(*offsets
)), 0);
417 buf
= (__CFBinaryPlistWriteBuffer
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, sizeof(__CFBinaryPlistWriteBuffer
), 0);
418 buf
->stream
= stream
;
420 buf
->streamIsData
= (CFGetTypeID(stream
) == CFDataGetTypeID());
423 bufferWrite(buf
, (uint8_t *)"bplist00", 8); // header
425 memset(&trailer
, 0, sizeof(trailer
));
426 trailer
._numObjects
= CFSwapInt64HostToBig(cnt
);
427 trailer
._topObject
= 0; // true for this implementation
428 trailer
._objectRefSize
= _byteCount(cnt
);
429 for (idx
= 0; idx
< cnt
; idx
++) {
430 CFPropertyListRef obj
= CFArrayGetValueAtIndex(objlist
, (CFIndex
)idx
);
431 CFTypeID type
= __CFGenericTypeID_genericobj_inline(obj
);
432 offsets
[idx
] = buf
->written
+ buf
->used
;
433 if (stringtype
== type
) {
434 CFIndex ret
, count
= CFStringGetLength((CFStringRef
)obj
);
436 uint8_t *bytes
, buffer
[1024];
437 bytes
= (count
<= 1024) ? buffer
: (uint8_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault
, count
, 0);
438 // presumption, believed to be true, is that ASCII encoding may need
439 // less bytes, but will not need greater, than the # of unichars
440 ret
= CFStringGetBytes((CFStringRef
)obj
, CFRangeMake(0, count
), kCFStringEncodingASCII
, 0, false, bytes
, count
, &needed
);
442 uint8_t marker
= (uint8_t)(kCFBinaryPlistMarkerASCIIString
| (needed
< 15 ? needed
: 0xf));
443 bufferWrite(buf
, &marker
, 1);
445 _appendInt(buf
, (uint64_t)needed
);
447 bufferWrite(buf
, bytes
, needed
);
450 uint8_t marker
= (uint8_t)(kCFBinaryPlistMarkerUnicode16String
| (count
< 15 ? count
: 0xf));
451 bufferWrite(buf
, &marker
, 1);
453 _appendInt(buf
, (uint64_t)count
);
455 chars
= (UniChar
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, count
* sizeof(UniChar
), 0);
456 CFStringGetCharacters((CFStringRef
)obj
, CFRangeMake(0, count
), chars
);
457 for (idx2
= 0; idx2
< count
; idx2
++) {
458 chars
[idx2
] = CFSwapInt16HostToBig(chars
[idx2
]);
460 bufferWrite(buf
, (uint8_t *)chars
, count
* sizeof(UniChar
));
461 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, chars
);
463 if (bytes
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, bytes
);
464 } else if (numbertype
== type
) {
469 if (CFNumberIsFloatType((CFNumberRef
)obj
)) {
470 CFSwappedFloat64 swapped64
;
471 CFSwappedFloat32 swapped32
;
472 if (CFNumberGetByteSize((CFNumberRef
)obj
) <= (CFIndex
)sizeof(float)) {
474 CFNumberGetValue((CFNumberRef
)obj
, kCFNumberFloat32Type
, &v
);
475 swapped32
= CFConvertFloat32HostToSwapped(v
);
476 bytes
= (uint8_t *)&swapped32
;
477 nbytes
= sizeof(float);
478 marker
= kCFBinaryPlistMarkerReal
| 2;
481 CFNumberGetValue((CFNumberRef
)obj
, kCFNumberFloat64Type
, &v
);
482 swapped64
= CFConvertFloat64HostToSwapped(v
);
483 bytes
= (uint8_t *)&swapped64
;
484 nbytes
= sizeof(double);
485 marker
= kCFBinaryPlistMarkerReal
| 3;
487 bufferWrite(buf
, &marker
, 1);
488 bufferWrite(buf
, bytes
, nbytes
);
490 CFNumberType type
= _CFNumberGetType2((CFNumberRef
)obj
);
491 if (kCFNumberSInt128Type
== type
) {
493 CFNumberGetValue((CFNumberRef
)obj
, kCFNumberSInt128Type
, &s
);
498 storage
.high
= CFSwapInt64HostToBig(s
.high
);
499 storage
.low
= CFSwapInt64HostToBig(s
.low
);
500 uint8_t *bytes
= (uint8_t *)&storage
;
501 uint8_t marker
= kCFBinaryPlistMarkerInt
| 4;
503 bufferWrite(buf
, &marker
, 1);
504 bufferWrite(buf
, bytes
, nbytes
);
506 CFNumberGetValue((CFNumberRef
)obj
, kCFNumberSInt64Type
, &bigint
);
507 _appendInt(buf
, bigint
);
510 } else if (_CFKeyedArchiverUIDGetTypeID() == type
) {
511 _appendUID(buf
, (CFKeyedArchiverUIDRef
)obj
);
512 } else if (booltype
== type
) {
513 uint8_t marker
= CFBooleanGetValue((CFBooleanRef
)obj
) ? kCFBinaryPlistMarkerTrue
: kCFBinaryPlistMarkerFalse
;
514 bufferWrite(buf
, &marker
, 1);
515 } else if (datatype
== type
) {
516 CFIndex count
= CFDataGetLength((CFDataRef
)obj
);
517 uint8_t marker
= (uint8_t)(kCFBinaryPlistMarkerData
| (count
< 15 ? count
: 0xf));
518 bufferWrite(buf
, &marker
, 1);
520 _appendInt(buf
, (uint64_t)count
);
522 bufferWrite(buf
, CFDataGetBytePtr((CFDataRef
)obj
), count
);
523 } else if (datetype
== type
) {
524 CFSwappedFloat64 swapped
;
525 uint8_t marker
= kCFBinaryPlistMarkerDate
;
526 bufferWrite(buf
, &marker
, 1);
527 swapped
= CFConvertFloat64HostToSwapped(CFDateGetAbsoluteTime((CFDateRef
)obj
));
528 bufferWrite(buf
, (uint8_t *)&swapped
, sizeof(swapped
));
529 } else if (dicttype
== type
) {
530 CFIndex count
= CFDictionaryGetCount((CFDictionaryRef
)obj
);
532 uint8_t marker
= (uint8_t)(kCFBinaryPlistMarkerDict
| (count
< 15 ? count
: 0xf));
533 bufferWrite(buf
, &marker
, 1);
535 _appendInt(buf
, (uint64_t)count
);
538 CFPropertyListRef
*list
, buffer
[512];
540 list
= (count
<= 256) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, 2 * count
* sizeof(CFTypeRef
), 0);
541 CFDictionaryGetKeysAndValues((CFDictionaryRef
)obj
, list
, list
+ count
);
542 for (idx2
= 0; idx2
< 2 * count
; idx2
++) {
543 CFPropertyListRef value
= list
[idx2
];
544 uint32_t swapped
= 0;
545 uint8_t *source
= (uint8_t *)&swapped
;
546 refnum
= (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable
, value
);
547 swapped
= CFSwapInt32HostToBig((uint32_t)refnum
);
548 bufferWrite(buf
, source
+ sizeof(swapped
) - trailer
._objectRefSize
, trailer
._objectRefSize
);
550 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
551 } else if (arraytype
== type
) {
552 CFIndex count
= CFArrayGetCount((CFArrayRef
)obj
);
553 CFPropertyListRef
*list
, buffer
[256];
554 uint8_t marker
= (uint8_t)(kCFBinaryPlistMarkerArray
| (count
< 15 ? count
: 0xf));
555 bufferWrite(buf
, &marker
, 1);
557 _appendInt(buf
, (uint64_t)count
);
559 list
= (count
<= 256) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, count
* sizeof(CFTypeRef
), 0);
560 CFArrayGetValues((CFArrayRef
)obj
, CFRangeMake(0, count
), list
);
561 for (idx2
= 0; idx2
< count
; idx2
++) {
562 CFPropertyListRef value
= list
[idx2
];
563 uint32_t swapped
= 0;
564 uint8_t *source
= (uint8_t *)&swapped
;
565 refnum
= (uint32_t)(uintptr_t)CFDictionaryGetValue(objtable
, value
);
566 swapped
= CFSwapInt32HostToBig((uint32_t)refnum
);
567 bufferWrite(buf
, source
+ sizeof(swapped
) - trailer
._objectRefSize
, trailer
._objectRefSize
);
569 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
573 if (error
&& buf
->error
) {
574 // caller will release error
576 } else if (buf
->error
) {
577 // caller is not interested in error, release it here
578 CFRelease(buf
->error
);
580 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, buf
);
581 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, offsets
);
588 length_so_far
= buf
->written
+ buf
->used
;
589 trailer
._offsetTableOffset
= CFSwapInt64HostToBig(length_so_far
);
590 trailer
._offsetIntSize
= _byteCount(length_so_far
);
592 for (idx
= 0; idx
< cnt
; idx
++) {
593 uint64_t swapped
= CFSwapInt64HostToBig(offsets
[idx
]);
594 uint8_t *source
= (uint8_t *)&swapped
;
595 bufferWrite(buf
, source
+ sizeof(*offsets
) - trailer
._offsetIntSize
, trailer
._offsetIntSize
);
597 length_so_far
+= cnt
* trailer
._offsetIntSize
;
599 bufferWrite(buf
, (uint8_t *)&trailer
, sizeof(trailer
));
601 length_so_far
+= sizeof(trailer
);
604 // caller will release error
607 CFRelease(buf
->error
);
611 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, buf
);
612 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, offsets
);
613 return (CFIndex
)length_so_far
;
617 CFIndex
__CFBinaryPlistWriteToStream(CFPropertyListRef plist
, CFTypeRef stream
) {
618 return __CFBinaryPlistWrite(plist
, stream
, 0, 0, NULL
);
621 // to be removed soon
622 CFIndex
__CFBinaryPlistWriteToStreamWithEstimate(CFPropertyListRef plist
, CFTypeRef stream
, uint64_t estimate
) {
623 return __CFBinaryPlistWrite(plist
, stream
, estimate
, 0, NULL
);
626 // to be removed soon
627 CFIndex
__CFBinaryPlistWriteToStreamWithOptions(CFPropertyListRef plist
, CFTypeRef stream
, uint64_t estimate
, CFOptionFlags options
) {
628 return __CFBinaryPlistWrite(plist
, stream
, estimate
, options
, NULL
);
631 #define FAIL_FALSE do { return false; } while (0)
632 #define FAIL_MAXOFFSET do { return UINT64_MAX; } while (0)
634 /* Grab a valSize-bytes integer out of the buffer pointed at by data and return it.
636 CF_INLINE
uint64_t _getSizedInt(const uint8_t *data
, uint8_t valSize
) {
638 return (uint64_t)*data
;
639 } else if (valSize
== 2) {
640 uint16_t val
= *(uint16_t *)data
;
641 return (uint64_t)CFSwapInt16BigToHost(val
);
642 } else if (valSize
== 4) {
643 uint32_t val
= *(uint32_t *)data
;
644 return (uint64_t)CFSwapInt32BigToHost(val
);
645 } else if (valSize
== 8) {
646 uint64_t val
= *(uint64_t *)data
;
647 return CFSwapInt64BigToHost(val
);
649 // Compatability with existing archives, including anything with a non-power-of-2 size and 16-byte values
651 for (CFIndex idx
= 0; idx
< valSize
; idx
++) {
652 res
= (res
<< 8) + data
[idx
];
656 // shouldn't get here
660 bool __CFBinaryPlistGetTopLevelInfo(const uint8_t *databytes
, uint64_t datalen
, uint8_t *marker
, uint64_t *offset
, CFBinaryPlistTrailer
*trailer
) {
661 CFBinaryPlistTrailer trail
;
665 if (!databytes
|| datalen
< sizeof(trail
) + 8 + 1) FAIL_FALSE
;
666 // Tiger and earlier will parse "bplist00"
667 // Leopard will parse "bplist00" or "bplist01"
668 // SnowLeopard will parse "bplist0?" where ? is any one character
669 if (0 != memcmp("bplist0", databytes
, 7)) {
672 memmove(&trail
, databytes
+ datalen
- sizeof(trail
), sizeof(trail
));
673 // In Leopard, the unused bytes in the trailer must be 0 or the parse will fail
674 // This check is not present in Tiger and earlier or after Leopard
675 trail
._numObjects
= CFSwapInt64BigToHost(trail
._numObjects
);
676 trail
._topObject
= CFSwapInt64BigToHost(trail
._topObject
);
677 trail
._offsetTableOffset
= CFSwapInt64BigToHost(trail
._offsetTableOffset
);
678 if (LONG_MAX
< trail
._numObjects
) FAIL_FALSE
;
679 if (LONG_MAX
< trail
._offsetTableOffset
) FAIL_FALSE
;
680 if (trail
._numObjects
< 1) FAIL_FALSE
;
681 if (trail
._numObjects
<= trail
._topObject
) FAIL_FALSE
;
682 if (trail
._offsetTableOffset
< 9) FAIL_FALSE
;
683 if (datalen
- sizeof(trail
) <= trail
._offsetTableOffset
) FAIL_FALSE
;
684 if (trail
._offsetIntSize
< 1) FAIL_FALSE
;
685 if (trail
._objectRefSize
< 1) FAIL_FALSE
;
686 int32_t err
= CF_NO_ERROR
;
687 uint64_t offsetIntSize
= trail
._offsetIntSize
;
688 uint64_t offsetTableSize
= __check_uint64_mul_unsigned_unsigned(trail
._numObjects
, offsetIntSize
, &err
);
689 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
690 if (offsetTableSize
< 1) FAIL_FALSE
;
691 uint64_t objectDataSize
= trail
._offsetTableOffset
- 8;
692 uint64_t tmpSum
= __check_uint64_add_unsigned_unsigned(8, objectDataSize
, &err
);
693 tmpSum
= __check_uint64_add_unsigned_unsigned(tmpSum
, offsetTableSize
, &err
);
694 tmpSum
= __check_uint64_add_unsigned_unsigned(tmpSum
, sizeof(trail
), &err
);
695 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
696 if (datalen
!= tmpSum
) FAIL_FALSE
;
697 if (trail
._objectRefSize
< 8 && (1ULL << (8 * trail
._objectRefSize
)) <= trail
._numObjects
) FAIL_FALSE
;
698 if (trail
._offsetIntSize
< 8 && (1ULL << (8 * trail
._offsetIntSize
)) <= trail
._offsetTableOffset
) FAIL_FALSE
;
699 const uint8_t *objectsFirstByte
;
700 objectsFirstByte
= check_ptr_add(databytes
, 8, &err
);
701 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
702 const uint8_t *offsetsFirstByte
= check_ptr_add(databytes
, trail
._offsetTableOffset
, &err
);
703 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
704 const uint8_t *offsetsLastByte
;
705 offsetsLastByte
= check_ptr_add(offsetsFirstByte
, offsetTableSize
- 1, &err
);
706 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
708 const uint8_t *bytesptr
= databytes
+ trail
._offsetTableOffset
;
709 uint64_t maxOffset
= trail
._offsetTableOffset
- 1;
710 for (CFIndex idx
= 0; idx
< trail
._numObjects
; idx
++) {
711 uint64_t off
= _getSizedInt(bytesptr
, trail
._offsetIntSize
);
712 if (maxOffset
< off
) FAIL_FALSE
;
713 bytesptr
+= trail
._offsetIntSize
;
716 bytesptr
= databytes
+ trail
._offsetTableOffset
+ trail
._topObject
* trail
._offsetIntSize
;
717 uint64_t off
= _getSizedInt(bytesptr
, trail
._offsetIntSize
);
718 if (off
< 8 || trail
._offsetTableOffset
<= off
) FAIL_FALSE
;
719 if (trailer
) *trailer
= trail
;
720 if (offset
) *offset
= off
;
721 if (marker
) *marker
= *(databytes
+ off
);
725 CF_INLINE Boolean
_plistIsPrimitive(CFPropertyListRef pl
) {
726 CFTypeID type
= __CFGenericTypeID_genericobj_inline(pl
);
727 if (dicttype
== type
|| arraytype
== type
|| settype
== type
) FAIL_FALSE
;
731 CF_INLINE
bool _readInt(const uint8_t *ptr
, const uint8_t *end_byte_ptr
, uint64_t *bigint
, const uint8_t **newptr
) {
732 if (end_byte_ptr
< ptr
) FAIL_FALSE
;
733 uint8_t marker
= *ptr
++;
734 if ((marker
& 0xf0) != kCFBinaryPlistMarkerInt
) FAIL_FALSE
;
735 uint64_t cnt
= 1 << (marker
& 0x0f);
736 int32_t err
= CF_NO_ERROR
;
737 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
738 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
739 if (end_byte_ptr
< extent
) FAIL_FALSE
;
740 // integers are not required to be in the most compact possible representation, but only the last 64 bits are significant currently
741 *bigint
= _getSizedInt(ptr
, cnt
);
743 if (newptr
) *newptr
= ptr
;
747 // bytesptr points at a ref
748 CF_INLINE
uint64_t _getOffsetOfRefAt(const uint8_t *databytes
, const uint8_t *bytesptr
, const CFBinaryPlistTrailer
*trailer
) {
749 // *trailer contents are trusted, even for overflows -- was checked when the trailer was parsed;
750 // this pointer arithmetic and the multiplication was also already done once and checked,
751 // and the offsetTable was already validated.
752 const uint8_t *objectsFirstByte
= databytes
+ 8;
753 const uint8_t *offsetsFirstByte
= databytes
+ trailer
->_offsetTableOffset
;
754 if (bytesptr
< objectsFirstByte
|| offsetsFirstByte
- trailer
->_objectRefSize
< bytesptr
) FAIL_MAXOFFSET
;
756 uint64_t ref
= _getSizedInt(bytesptr
, trailer
->_objectRefSize
);
757 if (trailer
->_numObjects
<= ref
) FAIL_MAXOFFSET
;
759 bytesptr
= databytes
+ trailer
->_offsetTableOffset
+ ref
* trailer
->_offsetIntSize
;
760 uint64_t off
= _getSizedInt(bytesptr
, trailer
->_offsetIntSize
);
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 // Compatibility method, to be removed soon
792 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
) {
793 return __CFBinaryPlistGetOffsetForValueFromDictionary3(databytes
, datalen
, startOffset
, trailer
, key
, koffset
, voffset
, false, objects
);
796 /* Get the offset for a value in a dictionary in a binary property list.
797 @param databytes A pointer to the start of the binary property list data.
798 @param datalen The length of the data.
799 @param startOffset The offset at which the dictionary starts.
800 @param trailer A pointer to a filled out trailer structure (use __CFBinaryPlistGetTopLevelInfo).
801 @param key A string key in the dictionary that should be searched for.
802 @param koffset Will be filled out with the offset to the key in the data bytes.
803 @param voffset Will be filled out with the offset to the value in the data bytes.
804 @param unused Unused parameter.
805 @param objects Used for caching objects. Should be a valid CFMutableDictionaryRef.
806 @return True if the key was found, false otherwise.
808 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
) {
810 // Require a key that is a plist primitive
811 if (!key
|| !_plistIsPrimitive(key
)) FAIL_FALSE
;
813 // Require that startOffset is in the range of the object table
814 uint64_t objectsRangeStart
= 8, objectsRangeEnd
= trailer
->_offsetTableOffset
- 1;
815 if (startOffset
< objectsRangeStart
|| objectsRangeEnd
< startOffset
) FAIL_FALSE
;
817 // ptr is the start of the dictionary we are reading
818 const uint8_t *ptr
= databytes
+ startOffset
;
820 // Check that the data pointer actually points to a dictionary
821 uint8_t marker
= *ptr
;
822 if ((marker
& 0xf0) != kCFBinaryPlistMarkerDict
) FAIL_FALSE
;
824 // Get the number of objects in this dictionary
825 int32_t err
= CF_NO_ERROR
;
826 ptr
= check_ptr_add(ptr
, 1, &err
);
827 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
828 uint64_t cnt
= (marker
& 0x0f);
831 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
832 if (LONG_MAX
< bigint
) FAIL_FALSE
;
836 // Total number of objects (keys + values) is cnt * 2
837 cnt
= check_size_t_mul(cnt
, 2, &err
);
838 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
839 size_t byte_cnt
= check_size_t_mul(cnt
, trailer
->_objectRefSize
, &err
);
840 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
842 // Find the end of the dictionary
843 const uint8_t *extent
= check_ptr_add(ptr
, byte_cnt
, &err
) - 1;
844 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
846 // Check that we didn't overflow the size of the dictionary
847 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
849 // For short keys (15 bytes or less) in ASCII form, we can do a quick comparison check
850 // We get the pointer or copy the buffer here, outside of the loop
851 CFIndex stringKeyLen
= -1;
852 if (__CFGenericTypeID_genericobj_inline(key
) == stringtype
) {
853 stringKeyLen
= CFStringGetLength((CFStringRef
)key
);
856 // Find the object in the dictionary with this key
858 uint64_t totalKeySize
= cnt
* trailer
->_objectRefSize
;
860 Boolean match
= false;
861 CFPropertyListRef keyInData
= NULL
;
864 const char *keyBufferPtr
= keyBuffer
;
866 if (stringKeyLen
< 0xf) {
867 // Since we will only be comparing ASCII strings, we can attempt to get a pointer using MacRoman encoding
868 // (this is cheaper than a copy)
869 if (!(keyBufferPtr
= CFStringGetCStringPtr((CFStringRef
)key
, kCFStringEncodingMacRoman
))) {
870 CFStringGetCString((CFStringRef
)key
, keyBuffer
, 16, kCFStringEncodingMacRoman
);
871 // The pointer should now point to our keyBuffer instead of the original string buffer, since we've copied it
872 keyBufferPtr
= keyBuffer
;
876 // Perform linear search of the keys
877 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
878 off
= _getOffsetOfRefAt(databytes
, ptr
, trailer
);
879 marker
= *(databytes
+ off
);
880 CFIndex len
= marker
& 0x0f;
881 // if it is a short ascii string in the data, and the key is a string
882 if (stringKeyLen
!= -1 && len
< 0xf && (marker
& 0xf0) == kCFBinaryPlistMarkerASCIIString
) {
883 if (len
== stringKeyLen
) {
885 const uint8_t *ptr2
= databytes
+ off
;
886 extent
= check_ptr_add(ptr2
, len
, &err
);
887 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
889 if (databytes
+ trailer
->_offsetTableOffset
<= extent
) FAIL_FALSE
;
891 // Compare the key to this potential match (ptr2 + 1 moves past the marker)
892 if (memcmp(ptr2
+ 1, keyBufferPtr
, stringKeyLen
) == 0) {
898 if (!__CFBinaryPlistCreateObject2(databytes
, datalen
, off
, trailer
, kCFAllocatorSystemDefault
, kCFPropertyListImmutable
, objects
, NULL
, 0, &keyInData
) || !_plistIsPrimitive(keyInData
)) {
899 if (keyInData
) CFRelease(keyInData
);
903 match
= CFEqual(key
, keyInData
);
904 CFRelease(keyInData
);
908 if (koffset
) *koffset
= off
;
909 if (voffset
) *voffset
= _getOffsetOfRefAt(databytes
, ptr
+ totalKeySize
, trailer
);
913 ptr
+= trailer
->_objectRefSize
;
919 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
) {
922 *plist
= CFDictionaryGetValue(objects
, (const void *)(uintptr_t)startOffset
);
929 // at any one invocation of this function, set should contain the offsets in the "path" down to this object
930 if (set
&& CFSetContainsValue(set
, (const void *)(uintptr_t)startOffset
)) return false;
932 // databytes is trusted to be at least datalen bytes long
933 // *trailer contents are trusted, even for overflows -- was checked when the trailer was parsed
934 uint64_t objectsRangeStart
= 8, objectsRangeEnd
= trailer
->_offsetTableOffset
- 1;
935 if (startOffset
< objectsRangeStart
|| objectsRangeEnd
< startOffset
) FAIL_FALSE
;
938 CFPropertyListRef
*list
, buffer
[256];
939 CFAllocatorRef listAllocator
;
941 uint8_t marker
= *(databytes
+ startOffset
);
942 switch (marker
& 0xf0) {
943 case kCFBinaryPlistMarkerNull
:
945 case kCFBinaryPlistMarkerNull
:
948 case kCFBinaryPlistMarkerFalse
:
949 *plist
= CFRetain(kCFBooleanFalse
);
951 case kCFBinaryPlistMarkerTrue
:
952 *plist
= CFRetain(kCFBooleanTrue
);
956 case kCFBinaryPlistMarkerInt
:
958 const uint8_t *ptr
= (databytes
+ startOffset
);
959 int32_t err
= CF_NO_ERROR
;
960 ptr
= check_ptr_add(ptr
, 1, &err
);
961 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
962 uint64_t cnt
= 1 << (marker
& 0x0f);
963 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
964 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
965 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
966 if (16 < cnt
) FAIL_FALSE
;
967 // in format version '00', 1, 2, and 4-byte integers have to be interpreted as unsigned,
968 // whereas 8-byte integers are signed (and 16-byte when available)
969 // negative 1, 2, 4-byte integers are always emitted as 8 bytes in format '00'
970 // integers are not required to be in the most compact possible representation, but only the last 64 bits are significant currently
971 uint64_t bigint
= _getSizedInt(ptr
, cnt
);
977 *plist
= CFNumberCreate(allocator
, kCFNumberSInt128Type
, &val
);
979 *plist
= CFNumberCreate(allocator
, kCFNumberSInt64Type
, &bigint
);
981 // these are always immutable
982 if (objects
&& *plist
) {
983 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
985 return (*plist
) ? true : false;
987 case kCFBinaryPlistMarkerReal
:
988 switch (marker
& 0x0f) {
990 const uint8_t *ptr
= (databytes
+ startOffset
);
991 int32_t err
= CF_NO_ERROR
;
992 ptr
= check_ptr_add(ptr
, 1, &err
);
993 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
994 const uint8_t *extent
= check_ptr_add(ptr
, 4, &err
) - 1;
995 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
996 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
997 CFSwappedFloat32 swapped32
;
998 memmove(&swapped32
, ptr
, 4);
999 float f
= CFConvertFloat32SwappedToHost(swapped32
);
1000 *plist
= CFNumberCreate(allocator
, kCFNumberFloat32Type
, &f
);
1001 // these are always immutable
1002 if (objects
&& *plist
) {
1003 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1005 return (*plist
) ? true : false;
1008 const uint8_t *ptr
= (databytes
+ startOffset
);
1009 int32_t err
= CF_NO_ERROR
;
1010 ptr
= check_ptr_add(ptr
, 1, &err
);
1011 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1012 const uint8_t *extent
= check_ptr_add(ptr
, 8, &err
) - 1;
1013 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1014 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1015 CFSwappedFloat64 swapped64
;
1016 memmove(&swapped64
, ptr
, 8);
1017 double d
= CFConvertFloat64SwappedToHost(swapped64
);
1018 *plist
= CFNumberCreate(allocator
, kCFNumberFloat64Type
, &d
);
1019 // these are always immutable
1020 if (objects
&& *plist
) {
1021 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1023 return (*plist
) ? true : false;
1027 case kCFBinaryPlistMarkerDate
& 0xf0:
1029 case kCFBinaryPlistMarkerDate
: {
1030 const uint8_t *ptr
= (databytes
+ startOffset
);
1031 int32_t err
= CF_NO_ERROR
;
1032 ptr
= check_ptr_add(ptr
, 1, &err
);
1033 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1034 const uint8_t *extent
= check_ptr_add(ptr
, 8, &err
) - 1;
1035 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1036 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1037 CFSwappedFloat64 swapped64
;
1038 memmove(&swapped64
, ptr
, 8);
1039 double d
= CFConvertFloat64SwappedToHost(swapped64
);
1040 *plist
= CFDateCreate(allocator
, d
);
1041 // these are always immutable
1042 if (objects
&& *plist
) {
1043 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1045 return (*plist
) ? true : false;
1049 case kCFBinaryPlistMarkerData
: {
1050 const uint8_t *ptr
= databytes
+ startOffset
;
1051 int32_t err
= CF_NO_ERROR
;
1052 ptr
= check_ptr_add(ptr
, 1, &err
);
1053 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1054 CFIndex cnt
= marker
& 0x0f;
1056 uint64_t bigint
= 0;
1057 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
1058 if (LONG_MAX
< bigint
) FAIL_FALSE
;
1059 cnt
= (CFIndex
)bigint
;
1061 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
1062 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1063 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1064 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
1065 *plist
= CFDataCreateMutable(allocator
, 0);
1066 if (*plist
) CFDataAppendBytes((CFMutableDataRef
)*plist
, ptr
, cnt
);
1068 *plist
= CFDataCreate(allocator
, ptr
, cnt
);
1070 if (objects
&& *plist
&& (mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
)) {
1071 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1073 return (*plist
) ? true : false;
1075 case kCFBinaryPlistMarkerASCIIString
: {
1076 const uint8_t *ptr
= databytes
+ startOffset
;
1077 int32_t err
= CF_NO_ERROR
;
1078 ptr
= check_ptr_add(ptr
, 1, &err
);
1079 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1080 CFIndex cnt
= marker
& 0x0f;
1082 uint64_t bigint
= 0;
1083 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
1084 if (LONG_MAX
< bigint
) FAIL_FALSE
;
1085 cnt
= (CFIndex
)bigint
;
1087 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
1088 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1089 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1090 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
1091 CFStringRef str
= CFStringCreateWithBytes(allocator
, ptr
, cnt
, kCFStringEncodingASCII
, false);
1092 *plist
= str
? CFStringCreateMutableCopy(allocator
, 0, str
) : NULL
;
1093 if (str
) CFRelease(str
);
1095 *plist
= CFStringCreateWithBytes(allocator
, ptr
, cnt
, kCFStringEncodingASCII
, false);
1097 if (objects
&& *plist
&& (mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
)) {
1098 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1100 return (*plist
) ? true : false;
1102 case kCFBinaryPlistMarkerUnicode16String
: {
1103 const uint8_t *ptr
= databytes
+ startOffset
;
1104 int32_t err
= CF_NO_ERROR
;
1105 ptr
= check_ptr_add(ptr
, 1, &err
);
1106 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1107 CFIndex cnt
= marker
& 0x0f;
1109 uint64_t bigint
= 0;
1110 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
1111 if (LONG_MAX
< bigint
) FAIL_FALSE
;
1112 cnt
= (CFIndex
)bigint
;
1114 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
1115 extent
= check_ptr_add(extent
, cnt
, &err
); // 2 bytes per character
1116 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1117 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1118 size_t byte_cnt
= check_size_t_mul(cnt
, sizeof(UniChar
), &err
);
1119 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1120 UniChar
*chars
= (UniChar
*)CFAllocatorAllocate(allocator
, byte_cnt
, 0);
1121 if (!chars
) FAIL_FALSE
;
1122 memmove(chars
, ptr
, byte_cnt
);
1123 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
1124 chars
[idx
] = CFSwapInt16BigToHost(chars
[idx
]);
1126 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
1127 CFStringRef str
= CFStringCreateWithCharactersNoCopy(allocator
, chars
, cnt
, allocator
);
1128 *plist
= str
? CFStringCreateMutableCopy(allocator
, 0, str
) : NULL
;
1129 if (str
) CFRelease(str
);
1131 *plist
= CFStringCreateWithCharactersNoCopy(allocator
, chars
, cnt
, allocator
);
1133 if (objects
&& *plist
&& (mutabilityOption
!= kCFPropertyListMutableContainersAndLeaves
)) {
1134 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1136 return (*plist
) ? true : false;
1138 case kCFBinaryPlistMarkerUID
: {
1139 const uint8_t *ptr
= databytes
+ startOffset
;
1140 int32_t err
= CF_NO_ERROR
;
1141 ptr
= check_ptr_add(ptr
, 1, &err
);
1142 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1143 CFIndex cnt
= (marker
& 0x0f) + 1;
1144 const uint8_t *extent
= check_ptr_add(ptr
, cnt
, &err
) - 1;
1145 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1146 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1147 // uids are not required to be in the most compact possible representation, but only the last 64 bits are significant currently
1148 uint64_t bigint
= _getSizedInt(ptr
, cnt
);
1150 if (UINT32_MAX
< bigint
) FAIL_FALSE
;
1151 *plist
= _CFKeyedArchiverUIDCreate(allocator
, (uint32_t)bigint
);
1152 // these are always immutable
1153 if (objects
&& *plist
) {
1154 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1156 return (*plist
) ? true : false;
1158 case kCFBinaryPlistMarkerArray
:
1159 case kCFBinaryPlistMarkerSet
: {
1160 const uint8_t *ptr
= databytes
+ startOffset
;
1161 int32_t err
= CF_NO_ERROR
;
1162 ptr
= check_ptr_add(ptr
, 1, &err
);
1163 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1164 CFIndex cnt
= marker
& 0x0f;
1166 uint64_t bigint
= 0;
1167 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
1168 if (LONG_MAX
< bigint
) FAIL_FALSE
;
1169 cnt
= (CFIndex
)bigint
;
1171 size_t byte_cnt
= check_size_t_mul(cnt
, trailer
->_objectRefSize
, &err
);
1172 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1173 const uint8_t *extent
= check_ptr_add(ptr
, byte_cnt
, &err
) - 1;
1174 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1175 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1176 byte_cnt
= check_size_t_mul(cnt
, sizeof(CFPropertyListRef
), &err
);
1177 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1178 list
= (cnt
<= 256) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, byte_cnt
, 0);
1179 listAllocator
= (list
== buffer
? kCFAllocatorNull
: kCFAllocatorSystemDefault
);
1180 if (!list
) FAIL_FALSE
;
1181 Boolean madeSet
= false;
1182 if (!set
&& 15 < curDepth
) {
1183 set
= CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
);
1184 madeSet
= set
? true : false;
1186 if (set
) CFSetAddValue(set
, (const void *)(uintptr_t)startOffset
);
1187 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
1188 CFPropertyListRef pl
;
1189 off
= _getOffsetOfRefAt(databytes
, ptr
, trailer
);
1190 if (!__CFBinaryPlistCreateObject2(databytes
, datalen
, off
, trailer
, allocator
, mutabilityOption
, objects
, set
, curDepth
+ 1, &pl
)) {
1192 CFRelease(list
[idx
]);
1194 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
1198 ptr
+= trailer
->_objectRefSize
;
1200 if (set
) CFSetRemoveValue(set
, (const void *)(uintptr_t)startOffset
);
1205 if ((marker
& 0xf0) == kCFBinaryPlistMarkerArray
) {
1206 if (mutabilityOption
!= kCFPropertyListImmutable
) {
1207 *plist
= CFArrayCreateMutable(allocator
, 0, &kCFTypeArrayCallBacks
);
1208 CFArrayReplaceValues((CFMutableArrayRef
)*plist
, CFRangeMake(0, 0), list
, cnt
);
1210 *plist
= CFArrayCreate(allocator
, list
, cnt
, &kCFTypeArrayCallBacks
);
1213 if (mutabilityOption
!= kCFPropertyListImmutable
) {
1214 *plist
= CFSetCreateMutable(allocator
, 0, &kCFTypeSetCallBacks
);
1215 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
1216 CFSetAddValue((CFMutableSetRef
)*plist
, list
[idx
]);
1219 *plist
= CFSetCreate(allocator
, list
, cnt
, &kCFTypeSetCallBacks
);
1222 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
1223 CFRelease(list
[idx
]);
1225 if (objects
&& *plist
&& (mutabilityOption
== kCFPropertyListImmutable
)) {
1226 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1228 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
1229 return (*plist
) ? true : false;
1231 case kCFBinaryPlistMarkerDict
: {
1232 const uint8_t *ptr
= databytes
+ startOffset
;
1233 int32_t err
= CF_NO_ERROR
;
1234 ptr
= check_ptr_add(ptr
, 1, &err
);
1235 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1236 CFIndex cnt
= marker
& 0x0f;
1238 uint64_t bigint
= 0;
1239 if (!_readInt(ptr
, databytes
+ objectsRangeEnd
, &bigint
, &ptr
)) FAIL_FALSE
;
1240 if (LONG_MAX
< bigint
) FAIL_FALSE
;
1241 cnt
= (CFIndex
)bigint
;
1243 cnt
= check_size_t_mul(cnt
, 2, &err
);
1244 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1245 size_t byte_cnt
= check_size_t_mul(cnt
, trailer
->_objectRefSize
, &err
);
1246 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1247 const uint8_t *extent
= check_ptr_add(ptr
, byte_cnt
, &err
) - 1;
1248 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1249 if (databytes
+ objectsRangeEnd
< extent
) FAIL_FALSE
;
1250 byte_cnt
= check_size_t_mul(cnt
, sizeof(CFPropertyListRef
), &err
);
1251 if (CF_NO_ERROR
!= err
) FAIL_FALSE
;
1252 list
= (cnt
<= 256) ? buffer
: (CFPropertyListRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, byte_cnt
, 0);
1253 listAllocator
= (list
== buffer
? kCFAllocatorNull
: kCFAllocatorSystemDefault
);
1254 if (!list
) FAIL_FALSE
;
1255 Boolean madeSet
= false;
1256 if (!set
&& 15 < curDepth
) {
1257 set
= CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
);
1258 madeSet
= set
? true : false;
1260 if (set
) CFSetAddValue(set
, (const void *)(uintptr_t)startOffset
);
1261 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
1262 CFPropertyListRef pl
= NULL
;
1263 off
= _getOffsetOfRefAt(databytes
, ptr
, trailer
);
1264 if (!__CFBinaryPlistCreateObject2(databytes
, datalen
, off
, trailer
, allocator
, mutabilityOption
, objects
, set
, curDepth
+ 1, &pl
) || (idx
< cnt
/ 2 && !_plistIsPrimitive(pl
))) {
1265 if (pl
) CFRelease(pl
);
1267 CFRelease(list
[idx
]);
1269 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
1273 ptr
+= trailer
->_objectRefSize
;
1275 if (set
) CFSetRemoveValue(set
, (const void *)(uintptr_t)startOffset
);
1280 if (mutabilityOption
!= kCFPropertyListImmutable
) {
1281 *plist
= CFDictionaryCreateMutable(allocator
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1282 for (CFIndex idx
= 0; idx
< cnt
/ 2; idx
++) {
1283 CFDictionaryAddValue((CFMutableDictionaryRef
)*plist
, list
[idx
], list
[idx
+ cnt
/ 2]);
1286 *plist
= CFDictionaryCreate(allocator
, list
, list
+ cnt
/ 2, cnt
/ 2, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1288 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
1289 CFRelease(list
[idx
]);
1291 if (objects
&& *plist
&& (mutabilityOption
== kCFPropertyListImmutable
)) {
1292 CFDictionarySetValue(objects
, (const void *)(uintptr_t)startOffset
, *plist
);
1294 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
1295 return (*plist
) ? true : false;
1301 bool __CFBinaryPlistCreateObject(const uint8_t *databytes
, uint64_t datalen
, uint64_t startOffset
, const CFBinaryPlistTrailer
*trailer
, CFAllocatorRef allocator
, CFOptionFlags mutabilityOption
, CFMutableDictionaryRef objects
, CFPropertyListRef
*plist
) {
1302 // for compatibility with Foundation's use, need to leave this here
1303 return __CFBinaryPlistCreateObject2(databytes
, datalen
, startOffset
, trailer
, allocator
, mutabilityOption
, objects
, NULL
, 0, plist
);
1306 __private_extern__
bool __CFTryParseBinaryPlist(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags option
, CFPropertyListRef
*plist
, CFStringRef
*errorString
) {
1308 CFBinaryPlistTrailer trailer
;
1310 const uint8_t *databytes
= CFDataGetBytePtr(data
);
1311 uint64_t datalen
= CFDataGetLength(data
);
1313 if (8 <= datalen
&& __CFBinaryPlistGetTopLevelInfo(databytes
, datalen
, &marker
, &offset
, &trailer
)) {
1314 // FALSE: We know for binary plist parsing that the result objects will be retained
1315 // by their containing collections as the parsing proceeds, so we do not need
1316 // to use retaining callbacks for the objects map in this case. WHY: the file might
1317 // be malformed and contain hash-equal keys for the same dictionary (for example)
1318 // and the later key will cause the previous one to be released when we set the second
1319 // in the dictionary.
1320 CFMutableDictionaryRef objects
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
, &kCFTypeDictionaryValueCallBacks
);
1321 _CFDictionarySetCapacity(objects
, trailer
._numObjects
);
1322 CFPropertyListRef pl
= NULL
;
1323 if (__CFBinaryPlistCreateObject2(databytes
, datalen
, offset
, &trailer
, allocator
, option
, objects
, NULL
, 0, &pl
)) {
1324 if (plist
) *plist
= pl
;
1326 if (plist
) *plist
= NULL
;
1327 if (errorString
) *errorString
= (CFStringRef
)CFRetain(CFSTR("binary data is corrupt"));