2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 Copyright 2000-2002, Apple, Inc. All rights reserved.
25 Responsibility: Christopher Kane
28 #include <CoreFoundation/CFBase.h>
29 #include <CoreFoundation/CFString.h>
30 #include <CoreFoundation/CFNumber.h>
31 #include <CoreFoundation/CFDate.h>
32 #include <CoreFoundation/CFData.h>
33 #include <CoreFoundation/CFArray.h>
34 #include <CoreFoundation/CFDictionary.h>
35 #include <CoreFoundation/CFSet.h>
36 #include <CoreFoundation/CFPropertyList.h>
37 #include <CoreFoundation/CFByteOrder.h>
38 #include <CoreFoundation/CFRuntime.h>
39 #include <CoreFoundation/CFStream.h>
43 #include "CFInternal.h"
46 CF_INLINE CFTypeID
__CFGenericTypeID_genericobj_inline(const void *cf
) {
47 CFTypeID typeID
= __CFBitfieldGetValue(((const CFRuntimeBase
*)cf
)->_info
, 15, 8);
48 return CF_IS_OBJC(typeID
, cf
) ? CFGetTypeID(cf
) : typeID
;
51 struct __CFKeyedArchiverUID
{
56 static CFStringRef
__CFKeyedArchiverUIDCopyDescription(CFTypeRef cf
) {
57 CFKeyedArchiverUIDRef uid
= (CFKeyedArchiverUIDRef
)cf
;
58 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("<CFKeyedArchiverUID %p [%p]>{value = %u}"), cf
, CFGetAllocator(cf
), uid
->_value
);
61 static CFStringRef
__CFKeyedArchiverUIDCopyFormattingDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
62 CFKeyedArchiverUIDRef uid
= (CFKeyedArchiverUIDRef
)cf
;
63 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("@%u@"), uid
->_value
);
66 static CFTypeID __kCFKeyedArchiverUIDTypeID
= _kCFRuntimeNotATypeID
;
68 static const CFRuntimeClass __CFKeyedArchiverUIDClass
= {
74 NULL
, // equal -- pointer equality only
75 NULL
, // hash -- pointer hashing only
76 __CFKeyedArchiverUIDCopyFormattingDescription
,
77 __CFKeyedArchiverUIDCopyDescription
80 __private_extern__
void __CFKeyedArchiverUIDInitialize(void) {
81 __kCFKeyedArchiverUIDTypeID
= _CFRuntimeRegisterClass(&__CFKeyedArchiverUIDClass
);
84 CFTypeID
_CFKeyedArchiverUIDGetTypeID(void) {
85 return __kCFKeyedArchiverUIDTypeID
;
88 CFKeyedArchiverUIDRef
_CFKeyedArchiverUIDCreate(CFAllocatorRef allocator
, uint32_t value
) {
89 CFKeyedArchiverUIDRef uid
;
90 uid
= (CFKeyedArchiverUIDRef
)_CFRuntimeCreateInstance(allocator
, __kCFKeyedArchiverUIDTypeID
, sizeof(struct __CFKeyedArchiverUID
) - sizeof(CFRuntimeBase
), NULL
);
94 ((struct __CFKeyedArchiverUID
*)uid
)->_value
= value
;
99 uint32_t _CFKeyedArchiverUIDGetValue(CFKeyedArchiverUIDRef uid
) {
109 uint8_t buffer
[8192 - 16];
110 } __CFBinaryPlistWriteBuffer
;
112 CF_INLINE
void writeBytes(__CFBinaryPlistWriteBuffer
*buf
, const UInt8
*bytes
, CFIndex length
) {
113 if (buf
->streamIsData
) {
114 CFDataAppendBytes((CFMutableDataRef
)buf
->stream
, bytes
, length
);
116 CFWriteStreamWrite((CFWriteStreamRef
)buf
->stream
, bytes
, length
);
120 static void bufferWrite(__CFBinaryPlistWriteBuffer
*buf
, const uint8_t *buffer
, CFIndex count
) {
122 if ((CFIndex
)sizeof(buf
->buffer
) <= count
) {
123 writeBytes(buf
, buf
->buffer
, buf
->used
);
124 buf
->written
+= buf
->used
;
126 writeBytes(buf
, buffer
, count
);
127 buf
->written
+= count
;
130 copyLen
= __CFMin(count
, (CFIndex
)sizeof(buf
->buffer
) - buf
->used
);
131 memmove(buf
->buffer
+ buf
->used
, buffer
, copyLen
);
132 buf
->used
+= copyLen
;
133 if (sizeof(buf
->buffer
) == buf
->used
) {
134 writeBytes(buf
, buf
->buffer
, sizeof(buf
->buffer
));
135 buf
->written
+= sizeof(buf
->buffer
);
136 memmove(buf
->buffer
, buffer
+ copyLen
, count
- copyLen
);
137 buf
->used
= count
- copyLen
;
141 static void bufferFlush(__CFBinaryPlistWriteBuffer
*buf
) {
142 writeBytes(buf
, buf
->buffer
, buf
->used
);
143 buf
->written
+= buf
->used
;
149 magic number ("bplist")
153 variable-sized objects
155 Object Formats (marker byte followed by additional info in some cases)
157 bool 0000 1000 // false
158 bool 0000 1001 // true
159 fill 0000 1111 // fill byte
160 int 0001 nnnn ... // # of bytes is 2^nnnn, big-endian bytes
161 real 0010 nnnn ... // # of bytes is 2^nnnn, big-endian bytes
162 date 0011 0011 ... // 8 byte float follows, big-endian bytes
163 data 0100 nnnn [int] ... // nnnn is number of bytes unless 1111 then int count follows, followed by bytes
164 string 0101 nnnn [int] ... // ASCII string, nnnn is # of chars, else 1111 then int count, then bytes
165 string 0110 nnnn [int] ... // Unicode string, nnnn is # of chars, else 1111 then int count, then big-endian 2-byte uint16_t
167 uid 1000 nnnn ... // nnnn+1 is # of bytes
169 array 1010 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows
172 dict 1101 nnnn [int] keyref* objref* // nnnn is count, unless '1111', then int count follows
177 list of ints, byte size of which is given in trailer
178 -- these are the byte offsets into the file
179 -- number of these is in the trailer
182 byte size of offset ints in offset table
183 byte size of object refs in arrays and dicts
184 number of offsets in offset table (also is number of objects)
185 element # in offset table which is top level object
190 static CFTypeID stringtype
= -1, datatype
= -1, numbertype
= -1, datetype
= -1;
191 static CFTypeID booltype
= -1, dicttype
= -1, arraytype
= -1;
193 static void _appendInt(__CFBinaryPlistWriteBuffer
*buf
, uint64_t bigint
) {
197 if (bigint
<= (uint64_t)0xff) {
199 marker
= kCFBinaryPlistMarkerInt
| 0;
200 } else if (bigint
<= (uint64_t)0xffff) {
202 marker
= kCFBinaryPlistMarkerInt
| 1;
203 } else if (bigint
<= (uint64_t)0xffffffff) {
205 marker
= kCFBinaryPlistMarkerInt
| 2;
208 marker
= kCFBinaryPlistMarkerInt
| 3;
210 bigint
= CFSwapInt64HostToBig(bigint
);
211 bytes
= (uint8_t *)&bigint
+ sizeof(bigint
) - nbytes
;
212 bufferWrite(buf
, &marker
, 1);
213 bufferWrite(buf
, bytes
, nbytes
);
216 static void _appendUID(__CFBinaryPlistWriteBuffer
*buf
, CFKeyedArchiverUIDRef uid
) {
220 uint64_t bigint
= _CFKeyedArchiverUIDGetValue(uid
);
221 if (bigint
<= (uint64_t)0xff) {
223 } else if (bigint
<= (uint64_t)0xffff) {
225 } else if (bigint
<= (uint64_t)0xffffffff) {
230 marker
= kCFBinaryPlistMarkerUID
| (nbytes
- 1);
231 bigint
= CFSwapInt64HostToBig(bigint
);
232 bytes
= (uint8_t *)&bigint
+ sizeof(bigint
) - nbytes
;
233 bufferWrite(buf
, &marker
, 1);
234 bufferWrite(buf
, bytes
, nbytes
);
237 static Boolean
__plistUniquingEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
238 // As long as this equals function is more restrictive than the
239 // existing one, for any given type, the hash function need not
240 // also be provided for the uniquing set.
241 if (__CFGenericTypeID_genericobj_inline(cf1
) != __CFGenericTypeID_genericobj_inline(cf2
)) return false;
242 if (__CFGenericTypeID_genericobj_inline(cf1
) == numbertype
) {
243 if (CFNumberIsFloatType(cf1
) != CFNumberIsFloatType(cf2
)) return false;
244 return CFEqual(cf1
, cf2
);
246 return CFEqual(cf1
, cf2
);
249 static void _flattenPlist(CFPropertyListRef plist
, CFMutableArrayRef objlist
, CFMutableDictionaryRef objtable
, CFMutableSetRef uniquingsets
[]) {
250 CFPropertyListRef unique
;
252 CFTypeID type
= __CFGenericTypeID_genericobj_inline(plist
);
254 CFPropertyListRef
*list
, buffer
[256];
256 // Do not unique dictionaries or arrays, because: they
257 // are slow to compare, and have poor hash codes.
258 // Uniquing bools is unnecessary.
260 if (stringtype
== type
) {
262 } else if (numbertype
== type
) {
264 } else if (datatype
== type
) {
266 } else if (datetype
== type
) {
269 if (1 && -1 != which
) {
270 CFMutableSetRef uniquingset
= uniquingsets
[which
];
271 CFIndex before
= CFSetGetCount(uniquingset
);
272 CFSetAddValue(uniquingset
, plist
);
273 CFIndex after
= CFSetGetCount(uniquingset
);
274 if (after
== before
) { // already in set
275 unique
= CFSetGetValue(uniquingset
, plist
);
276 if (unique
!= plist
) {
277 refnum
= (uint32_t)CFDictionaryGetValue(objtable
, unique
);
278 CFDictionaryAddValue(objtable
, plist
, (const void *)refnum
);
283 refnum
= CFArrayGetCount(objlist
);
284 CFArrayAppendValue(objlist
, plist
);
285 CFDictionaryAddValue(objtable
, plist
, (const void *)refnum
);
286 if (dicttype
== type
) {
287 CFIndex count
= CFDictionaryGetCount(plist
);
288 list
= (count
<= 128) ? buffer
: CFAllocatorAllocate(kCFAllocatorSystemDefault
, 2 * count
* sizeof(CFTypeRef
), 0);
289 CFDictionaryGetKeysAndValues(plist
, list
, list
+ count
);
290 for (idx
= 0; idx
< 2 * count
; idx
++) {
291 _flattenPlist(list
[idx
], objlist
, objtable
, uniquingsets
);
293 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
294 } else if (arraytype
== type
) {
295 CFIndex count
= CFArrayGetCount(plist
);
296 list
= (count
<= 256) ? buffer
: CFAllocatorAllocate(kCFAllocatorSystemDefault
, count
* sizeof(CFTypeRef
), 0);
297 CFArrayGetValues(plist
, CFRangeMake(0, count
), list
);
298 for (idx
= 0; idx
< count
; idx
++) {
299 _flattenPlist(list
[idx
], objlist
, objtable
, uniquingsets
);
301 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
305 // stream can be a CFWriteStreamRef or a CFMutableDataRef
306 CFIndex
__CFBinaryPlistWriteToStream(CFPropertyListRef plist
, CFTypeRef stream
) {
307 CFMutableDictionaryRef objtable
;
308 CFMutableArrayRef objlist
;
309 CFBinaryPlistTrailer trailer
;
310 uint64_t *offsets
, length_so_far
;
311 uint64_t mask
, refnum
;
312 int64_t idx
, idx2
, cnt
;
313 __CFBinaryPlistWriteBuffer
*buf
;
314 CFSetCallBacks cb
= kCFTypeSetCallBacks
;
316 if ((CFTypeID
)-1 == stringtype
) {
317 stringtype
= CFStringGetTypeID();
318 datatype
= CFDataGetTypeID();
319 numbertype
= CFNumberGetTypeID();
320 booltype
= CFBooleanGetTypeID();
321 datetype
= CFDateGetTypeID();
322 dicttype
= CFDictionaryGetTypeID();
323 arraytype
= CFArrayGetTypeID();
325 objtable
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
, NULL
);
326 _CFDictionarySetCapacity(objtable
, 640);
327 objlist
= CFArrayCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
);
328 _CFArraySetCapacity(objlist
, 640);
329 cb
.equal
= __plistUniquingEqual
;
330 CFMutableSetRef uniquingsets
[4];
331 uniquingsets
[0] = CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, &cb
);
332 _CFSetSetCapacity(uniquingsets
[0], 1000);
333 uniquingsets
[1] = CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, &cb
);
334 _CFSetSetCapacity(uniquingsets
[1], 500);
335 uniquingsets
[2] = CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, &cb
);
336 _CFSetSetCapacity(uniquingsets
[2], 250);
337 uniquingsets
[3] = CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, &cb
);
338 _CFSetSetCapacity(uniquingsets
[3], 250);
340 _flattenPlist(plist
, objlist
, objtable
, uniquingsets
);
342 CFRelease(uniquingsets
[0]);
343 CFRelease(uniquingsets
[1]);
344 CFRelease(uniquingsets
[2]);
345 CFRelease(uniquingsets
[3]);
347 cnt
= CFArrayGetCount(objlist
);
348 offsets
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, cnt
* sizeof(*offsets
), 0);
350 buf
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, sizeof(__CFBinaryPlistWriteBuffer
), 0);
351 buf
->stream
= stream
;
352 buf
->streamIsData
= (CFGetTypeID(stream
) == CFDataGetTypeID());
355 bufferWrite(buf
, "bplist00", 8); // header
357 memset(&trailer
, 0, sizeof(trailer
));
358 trailer
._numObjects
= CFSwapInt64HostToBig(cnt
);
359 trailer
._topObject
= 0; // true for this implementation
362 trailer
._objectRefSize
++;
366 for (idx
= 0; idx
< cnt
; idx
++) {
367 CFPropertyListRef obj
= CFArrayGetValueAtIndex(objlist
, idx
);
368 CFTypeID type
= CFGetTypeID(obj
);
369 offsets
[idx
] = buf
->written
+ buf
->used
;
370 if (stringtype
== type
) {
371 CFIndex ret
, count
= CFStringGetLength(obj
);
373 uint8_t *bytes
, buffer
[1024];
374 bytes
= (count
<= 1024) ? buffer
: CFAllocatorAllocate(kCFAllocatorDefault
, count
, 0);
375 // presumption, believed to be true, is that ASCII encoding may need
376 // less bytes, but will not need greater, than the # of unichars
377 ret
= CFStringGetBytes(obj
, CFRangeMake(0, count
), kCFStringEncodingASCII
, 0, false, bytes
, count
, &needed
);
379 uint8_t marker
= kCFBinaryPlistMarkerASCIIString
| (needed
< 15 ? needed
: 0xf);
380 bufferWrite(buf
, &marker
, 1);
382 _appendInt(buf
, (uint64_t)needed
);
384 bufferWrite(buf
, bytes
, needed
);
387 uint8_t marker
= kCFBinaryPlistMarkerUnicode16String
| (count
< 15 ? count
: 0xf);
388 bufferWrite(buf
, &marker
, 1);
390 _appendInt(buf
, (uint64_t)count
);
392 chars
= CFAllocatorAllocate(kCFAllocatorDefault
, count
* sizeof(UniChar
), 0);
393 CFStringGetCharacters(obj
, CFRangeMake(0, count
), chars
);
394 for (idx2
= 0; idx2
< count
; idx2
++) {
395 chars
[idx2
] = CFSwapInt16HostToBig(chars
[idx2
]);
397 bufferWrite(buf
, (uint8_t *)chars
, count
* sizeof(UniChar
));
398 CFAllocatorDeallocate(kCFAllocatorDefault
, chars
);
400 if (bytes
!= buffer
) CFAllocatorDeallocate(kCFAllocatorDefault
, bytes
);
401 } else if (numbertype
== type
) {
403 CFSwappedFloat64 swapped64
;
404 CFSwappedFloat32 swapped32
;
408 if (CFNumberIsFloatType(obj
)) {
409 if (CFNumberGetByteSize(obj
) <= (CFIndex
)sizeof(float)) {
411 CFNumberGetValue(obj
, kCFNumberFloat32Type
, &v
);
412 swapped32
= CFConvertFloat32HostToSwapped(v
);
413 bytes
= (uint8_t *)&swapped32
;
414 nbytes
= sizeof(float);
415 marker
= kCFBinaryPlistMarkerReal
| 2;
418 CFNumberGetValue(obj
, kCFNumberFloat64Type
, &v
);
419 swapped64
= CFConvertFloat64HostToSwapped(v
);
420 bytes
= (uint8_t *)&swapped64
;
421 nbytes
= sizeof(double);
422 marker
= kCFBinaryPlistMarkerReal
| 3;
424 bufferWrite(buf
, &marker
, 1);
425 bufferWrite(buf
, bytes
, nbytes
);
427 CFNumberGetValue(obj
, kCFNumberSInt64Type
, &bigint
);
428 _appendInt(buf
, bigint
);
430 } else if (_CFKeyedArchiverUIDGetTypeID() == type
) {
431 _appendUID(buf
, (CFKeyedArchiverUIDRef
)obj
);
432 } else if (booltype
== type
) {
433 uint8_t marker
= CFBooleanGetValue(obj
) ? kCFBinaryPlistMarkerTrue
: kCFBinaryPlistMarkerFalse
;
434 bufferWrite(buf
, &marker
, 1);
435 } else if (datatype
== type
) {
436 CFIndex count
= CFDataGetLength(obj
);
437 uint8_t marker
= kCFBinaryPlistMarkerData
| (count
< 15 ? count
: 0xf);
438 bufferWrite(buf
, &marker
, 1);
440 _appendInt(buf
, (uint64_t)count
);
442 bufferWrite(buf
, CFDataGetBytePtr(obj
), count
);
443 } else if (datetype
== type
) {
444 CFSwappedFloat64 swapped
;
445 uint8_t marker
= kCFBinaryPlistMarkerDate
;
446 bufferWrite(buf
, &marker
, 1);
447 swapped
= CFConvertFloat64HostToSwapped(CFDateGetAbsoluteTime(obj
));
448 bufferWrite(buf
, (uint8_t *)&swapped
, sizeof(swapped
));
449 } else if (dicttype
== type
) {
450 CFIndex count
= CFDictionaryGetCount(obj
);
451 CFPropertyListRef
*list
, buffer
[512];
452 uint8_t marker
= kCFBinaryPlistMarkerDict
| (count
< 15 ? count
: 0xf);
453 bufferWrite(buf
, &marker
, 1);
455 _appendInt(buf
, (uint64_t)count
);
457 list
= (count
<= 256) ? buffer
: CFAllocatorAllocate(kCFAllocatorSystemDefault
, 2 * count
* sizeof(CFTypeRef
), 0);
458 CFDictionaryGetKeysAndValues(obj
, list
, list
+ count
);
459 for (idx2
= 0; idx2
< 2 * count
; idx2
++) {
460 CFPropertyListRef value
= list
[idx2
];
461 uint32_t swapped
= 0;
462 uint8_t *source
= (uint8_t *)&swapped
;
463 refnum
= (uint32_t)CFDictionaryGetValue(objtable
, value
);
464 swapped
= CFSwapInt32HostToBig(refnum
);
465 bufferWrite(buf
, source
+ sizeof(swapped
) - trailer
._objectRefSize
, trailer
._objectRefSize
);
467 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
468 } else if (arraytype
== type
) {
469 CFIndex count
= CFArrayGetCount(obj
);
470 CFPropertyListRef
*list
, buffer
[256];
471 uint8_t marker
= kCFBinaryPlistMarkerArray
| (count
< 15 ? count
: 0xf);
472 bufferWrite(buf
, &marker
, 1);
474 _appendInt(buf
, (uint64_t)count
);
476 list
= (count
<= 256) ? buffer
: CFAllocatorAllocate(kCFAllocatorSystemDefault
, count
* sizeof(CFTypeRef
), 0);
477 CFArrayGetValues(obj
, CFRangeMake(0, count
), list
);
478 for (idx2
= 0; idx2
< count
; idx2
++) {
479 CFPropertyListRef value
= list
[idx2
];
480 uint32_t swapped
= 0;
481 uint8_t *source
= (uint8_t *)&swapped
;
482 refnum
= (uint32_t)CFDictionaryGetValue(objtable
, value
);
483 swapped
= CFSwapInt32HostToBig(refnum
);
484 bufferWrite(buf
, source
+ sizeof(swapped
) - trailer
._objectRefSize
, trailer
._objectRefSize
);
486 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
490 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, buf
);
491 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, offsets
);
498 length_so_far
= buf
->written
+ buf
->used
;
499 trailer
._offsetTableOffset
= CFSwapInt64HostToBig(length_so_far
);
500 trailer
._offsetIntSize
= 0;
502 while (length_so_far
& mask
) {
503 trailer
._offsetIntSize
++;
507 for (idx
= 0; idx
< cnt
; idx
++) {
508 uint64_t swapped
= CFSwapInt64HostToBig(offsets
[idx
]);
509 uint8_t *source
= (uint8_t *)&swapped
;
510 bufferWrite(buf
, source
+ sizeof(*offsets
) - trailer
._offsetIntSize
, trailer
._offsetIntSize
);
512 length_so_far
+= cnt
* trailer
._offsetIntSize
;
514 bufferWrite(buf
, (uint8_t *)&trailer
, sizeof(trailer
));
516 length_so_far
+= sizeof(trailer
);
517 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, buf
);
518 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, offsets
);
519 return (CFIndex
)length_so_far
;
522 bool __CFBinaryPlistGetTopLevelInfo(const uint8_t *databytes
, uint64_t datalen
, uint8_t *marker
, uint64_t *offset
, CFBinaryPlistTrailer
*trailer
) {
523 const uint8_t *bytesptr
;
524 CFBinaryPlistTrailer trail
;
528 if ((CFTypeID
)-1 == stringtype
) {
529 stringtype
= CFStringGetTypeID();
530 datatype
= CFDataGetTypeID();
531 numbertype
= CFNumberGetTypeID();
532 booltype
= CFBooleanGetTypeID();
533 datetype
= CFDateGetTypeID();
534 dicttype
= CFDictionaryGetTypeID();
535 arraytype
= CFArrayGetTypeID();
537 if (!databytes
|| datalen
< 8 || 0 != memcmp("bplist00", databytes
, 8)) return false;
538 if (datalen
< sizeof(trail
) + 8 + 1) return false;
539 memmove(&trail
, databytes
+ datalen
- sizeof(trail
), sizeof(trail
));
540 trail
._numObjects
= CFSwapInt64BigToHost(trail
._numObjects
);
541 trail
._topObject
= CFSwapInt64BigToHost(trail
._topObject
);
542 if (trail
._numObjects
< trail
._topObject
) return false;
543 trail
._offsetTableOffset
= CFSwapInt64BigToHost(trail
._offsetTableOffset
);
544 if (datalen
< trail
._offsetTableOffset
+ trail
._numObjects
* trail
._offsetIntSize
+ sizeof(trail
)) return false;
545 bytesptr
= databytes
+ trail
._offsetTableOffset
+ trail
._topObject
* trail
._offsetIntSize
;
547 for (idx
= 0; idx
< trail
._offsetIntSize
; idx
++) {
548 off
= (off
<< 8) + bytesptr
[idx
];
550 if (trail
._offsetTableOffset
<= off
) return false;
551 if (trailer
) *trailer
= trail
;
552 if (offset
) *offset
= off
;
553 if (marker
) *marker
= *(databytes
+ off
);
557 static bool _readInt(const uint8_t *ptr
, uint64_t *bigint
, const uint8_t **newptr
) {
561 if ((marker
& 0xf0) != kCFBinaryPlistMarkerInt
) return false;
562 cnt
= 1 << (marker
& 0xf);
564 for (idx
= 0; idx
< cnt
; idx
++) {
565 *bigint
= (*bigint
<< 8) + *ptr
++;
567 if (newptr
) *newptr
= ptr
;
571 static uint64_t _getOffsetOfRefAt(const uint8_t *databytes
, const uint8_t *bytesptr
, const CFBinaryPlistTrailer
*trailer
) {
572 uint64_t ref
= 0, off
= 0;
574 for (idx
= 0; idx
< trailer
->_objectRefSize
; idx
++) {
575 ref
= (ref
<< 8) + bytesptr
[idx
];
577 bytesptr
= databytes
+ trailer
->_offsetTableOffset
+ ref
* trailer
->_offsetIntSize
;
578 for (idx
= 0; idx
< trailer
->_offsetIntSize
; idx
++) {
579 off
= (off
<< 8) + bytesptr
[idx
];
584 bool __CFBinaryPlistGetOffsetForValueFromArray(const uint8_t *databytes
, uint64_t datalen
, uint64_t startOffset
, const CFBinaryPlistTrailer
*trailer
, CFIndex idx
, uint64_t *offset
) {
585 const uint8_t *bytesptr
;
590 marker
= *(databytes
+ startOffset
);
591 if ((marker
& 0xf0) != kCFBinaryPlistMarkerArray
) return false;
592 cnt
= (marker
& 0x0f);
593 if (cnt
< 15 && cnt
<= idx
) return false;
594 bytesptr
= databytes
+ startOffset
+ 1;
597 if (!_readInt(bytesptr
, &bigint
, &bytesptr
)) return false;
598 if (INT_MAX
< bigint
) return false;
599 cnt
= (CFIndex
)bigint
;
601 if (cnt
<= idx
) return false;
602 off
= _getOffsetOfRefAt(databytes
, bytesptr
+ idx
* trailer
->_objectRefSize
, trailer
);
603 if (datalen
<= off
) return false;
604 if (offset
) *offset
= off
;
608 bool __CFBinaryPlistGetOffsetForValueFromDictionary(const uint8_t *databytes
, uint64_t datalen
, uint64_t startOffset
, const CFBinaryPlistTrailer
*trailer
, CFTypeRef key
, uint64_t *koffset
, uint64_t *voffset
) {
609 const uint8_t *refsptr
, *bytesptr
;
612 CFTypeID keytype
= CFGetTypeID(key
);
613 CFIndex idx
, keyn
, cnt
, cnt2
;
615 marker
= *(databytes
+ startOffset
);
616 if ((marker
& 0xf0) != kCFBinaryPlistMarkerDict
) return false;
617 cnt
= (marker
& 0x0f);
618 refsptr
= databytes
+ startOffset
+ 1 + 0;
621 if (!_readInt(refsptr
, &bigint
, &refsptr
)) return false;
622 if (INT_MAX
< bigint
) return false;
623 cnt
= (CFIndex
)bigint
;
625 for (keyn
= 0; keyn
< cnt
; keyn
++) {
626 off
= _getOffsetOfRefAt(databytes
, refsptr
, trailer
);
627 if (datalen
<= off
) return false;
628 refsptr
+= trailer
->_objectRefSize
;
629 bytesptr
= databytes
+ off
;
630 marker
= *bytesptr
& 0xf0;
631 cnt2
= *bytesptr
& 0x0f;
632 if (kCFBinaryPlistMarkerASCIIString
== marker
|| kCFBinaryPlistMarkerUnicode16String
== marker
) {
633 CFStringInlineBuffer strbuf
;
635 if (keytype
!= stringtype
) goto miss
;
636 if (0xf == cnt2
&& CFStringGetLength(key
) < 15) goto miss
;
640 if (!_readInt(bytesptr
, &bigint
, &bytesptr
)) return false;
641 if (INT_MAX
< bigint
) return false;
642 cnt2
= (CFIndex
)bigint
;
644 if (cnt2
!= CFStringGetLength(key
)) goto miss
;
645 uchar
= (kCFBinaryPlistMarkerASCIIString
== marker
) ? (UniChar
)bytesptr
[0] : (UniChar
)(bytesptr
[0] * 256 + bytesptr
[1]);
646 if (uchar
!= CFStringGetCharacterAtIndex(key
, 0)) goto miss
;
647 bytesptr
+= (kCFBinaryPlistMarkerASCIIString
== marker
) ? 1 : 2;
648 CFStringInitInlineBuffer(key
, &strbuf
, CFRangeMake(0, cnt2
));
649 for (idx
= 1; idx
< cnt2
; idx
++) {
650 uchar
= (kCFBinaryPlistMarkerASCIIString
== marker
) ? (UniChar
)bytesptr
[0] : (UniChar
)(bytesptr
[0] * 256 + bytesptr
[1]);
651 if (uchar
!= __CFStringGetCharacterFromInlineBufferQuick(&strbuf
, idx
)) goto miss
;
652 bytesptr
+= (kCFBinaryPlistMarkerASCIIString
== marker
) ? 1 : 2;
654 if (koffset
) *koffset
= off
;
655 off
= _getOffsetOfRefAt(databytes
, refsptr
+ (cnt
- 1) * trailer
->_objectRefSize
, trailer
);
656 if (datalen
<= off
) return false;
657 if (voffset
) *voffset
= off
;
660 //#warning the other primitive types should be allowed as keys in a binary plist dictionary, I think
668 extern CFArrayRef
_CFArrayCreate_ex(CFAllocatorRef allocator
, bool mutable, const void **values
, CFIndex numValues
);
670 extern CFDictionaryRef
_CFDictionaryCreate_ex(CFAllocatorRef allocator
, bool mutable, const void **keys
, const void **values
, CFIndex numValues
);
673 static bool _getUIDFromData(const uint8_t *datap
, uint64_t *vp
) {
675 uint8_t marker
= *datap
;
677 if ((marker
& 0xf0) != kCFBinaryPlistMarkerUID
) return false;
678 cnt
= (marker
& 0x0f) + 1;
681 for (idx
= 0; idx
< cnt
; idx
++) {
682 bigint
= (bigint
<< 8) + *datap
++;
689 static bool _getFloatFromData(const uint8_t *datap
, float *vp
) {
690 CFSwappedFloat32 swapped32
;
691 if (*datap
!= (kCFBinaryPlistMarkerReal
| 2)) return false;
693 memmove(&swapped32
, datap
, sizeof(swapped32
));
694 *vp
= CFConvertFloat32SwappedToHost(swapped32
);
698 static bool _getDoubleFromData(const uint8_t *datap
, double *vp
) {
699 CFSwappedFloat64 swapped64
;
700 if (*datap
!= (kCFBinaryPlistMarkerReal
| 3)) return false;
702 memmove(&swapped64
, datap
, sizeof(swapped64
));
703 *vp
= CFConvertFloat64SwappedToHost(swapped64
);
707 bool __CFBinaryPlistCreateObject(const uint8_t *databytes
, uint64_t datalen
, uint64_t startOffset
, const CFBinaryPlistTrailer
*trailer
, CFAllocatorRef allocator
, CFOptionFlags mutabilityOption
, CFMutableDictionaryRef objects
, CFPropertyListRef
*plist
) {
708 const uint8_t *bytesptr
;
714 CFPropertyListRef
*list
, buffer
[256];
715 CFAllocatorRef listAllocator
;
718 *plist
= CFDictionaryGetValue(objects
, (const void *)(intptr_t)startOffset
);
725 marker
= *(databytes
+ startOffset
);
726 switch (marker
& 0xf0) {
727 case kCFBinaryPlistMarkerNull
:
729 case kCFBinaryPlistMarkerNull
:
732 case kCFBinaryPlistMarkerFalse
:
733 *plist
= CFRetain(kCFBooleanFalse
);
735 case kCFBinaryPlistMarkerTrue
:
736 *plist
= CFRetain(kCFBooleanTrue
);
740 case kCFBinaryPlistMarkerInt
:
741 if (!_readInt(databytes
+ startOffset
, &bigint
, NULL
)) return false;
742 *plist
= CFNumberCreate(allocator
, kCFNumberSInt64Type
, &bigint
);
743 if (objects
) CFDictionarySetValue(objects
, (const void *)(intptr_t)startOffset
, *plist
);
744 return (*plist
) ? true : false;
745 case kCFBinaryPlistMarkerReal
:
749 _getFloatFromData(databytes
+ startOffset
, &f
);
750 *plist
= CFNumberCreate(allocator
, kCFNumberFloat32Type
, &f
);
751 if (objects
) CFDictionarySetValue(objects
, (const void *)(intptr_t)startOffset
, *plist
);
752 return (*plist
) ? true : false;
753 } else if (3 == cnt
) {
755 _getDoubleFromData(databytes
+ startOffset
, &d
);
756 *plist
= CFNumberCreate(allocator
, kCFNumberFloat64Type
, &d
);
757 if (objects
) CFDictionarySetValue(objects
, (const void *)(intptr_t)startOffset
, *plist
);
758 return (*plist
) ? true : false;
761 case kCFBinaryPlistMarkerDate
& 0xf0: {
762 CFSwappedFloat64 swapped64
;
765 if (3 != cnt
) return false;
766 memmove(&swapped64
, databytes
+ startOffset
+ 1, sizeof(swapped64
));
767 d
= CFConvertFloat64SwappedToHost(swapped64
);
768 *plist
= CFDateCreate(allocator
, d
);
769 if (objects
) CFDictionarySetValue(objects
, (const void *)(intptr_t)startOffset
, *plist
);
770 return (*plist
) ? true : false;
772 case kCFBinaryPlistMarkerData
:
774 bytesptr
= databytes
+ startOffset
+ 1;
776 if (!_readInt(bytesptr
, &bigint
, &bytesptr
)) return false;
777 if (INT_MAX
< bigint
) return false;
778 cnt
= (CFIndex
)bigint
;
780 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
781 *plist
= CFDataCreateMutable(allocator
, 0);
782 CFDataAppendBytes((CFMutableDataRef
)*plist
, bytesptr
, cnt
);
784 *plist
= CFDataCreate(allocator
, bytesptr
, cnt
);
786 if (objects
) CFDictionarySetValue(objects
, (const void *)(intptr_t)startOffset
, *plist
);
787 return (*plist
) ? true : false;
788 case kCFBinaryPlistMarkerASCIIString
:
790 bytesptr
= databytes
+ startOffset
+ 1;
792 if (!_readInt(bytesptr
, &bigint
, &bytesptr
)) return false;
793 if (INT_MAX
< bigint
) return false;
794 cnt
= (CFIndex
)bigint
;
796 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
797 CFStringRef str
= CFStringCreateWithBytes(allocator
, bytesptr
, cnt
, kCFStringEncodingASCII
, false);
798 *plist
= CFStringCreateMutableCopy(allocator
, 0, str
);
801 *plist
= CFStringCreateWithBytes(allocator
, bytesptr
, cnt
, kCFStringEncodingASCII
, false);
803 if (objects
) CFDictionarySetValue(objects
, (const void *)(intptr_t)startOffset
, *plist
);
804 return (*plist
) ? true : false;
805 case kCFBinaryPlistMarkerUnicode16String
:
807 bytesptr
= databytes
+ startOffset
+ 1;
809 if (!_readInt(bytesptr
, &bigint
, &bytesptr
)) return false;
810 if (INT_MAX
< bigint
) return false;
811 cnt
= (CFIndex
)bigint
;
813 chars
= CFAllocatorAllocate(allocator
, cnt
* sizeof(UniChar
), 0);
814 memmove(chars
, bytesptr
, cnt
* sizeof(UniChar
));
815 for (idx
= 0; idx
< cnt
; idx
++) {
816 chars
[idx
] = CFSwapInt16BigToHost(chars
[idx
]);
818 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
819 CFStringRef str
= CFStringCreateWithCharactersNoCopy(allocator
, chars
, cnt
, allocator
);
820 *plist
= CFStringCreateMutableCopy(allocator
, 0, str
);
823 *plist
= CFStringCreateWithCharactersNoCopy(allocator
, chars
, cnt
, allocator
);
825 if (objects
) CFDictionarySetValue(objects
, (const void *)(intptr_t)startOffset
, *plist
);
826 return (*plist
) ? true : false;
827 case kCFBinaryPlistMarkerUID
:
828 cnt
= (marker
& 0x0f) + 1;
829 bytesptr
= databytes
+ startOffset
+ 1;
831 for (idx
= 0; idx
< cnt
; idx
++) {
832 bigint
= (bigint
<< 8) + *bytesptr
++;
834 if (UINT_MAX
< bigint
) return false;
835 *plist
= _CFKeyedArchiverUIDCreate(allocator
, (uint32_t)bigint
);
836 if (objects
) CFDictionarySetValue(objects
, (const void *)(intptr_t)startOffset
, *plist
);
837 return (*plist
) ? true : false;
838 case kCFBinaryPlistMarkerArray
:
840 bytesptr
= databytes
+ startOffset
+ 1;
842 if (!_readInt(bytesptr
, &bigint
, &bytesptr
)) return false;
843 if (INT_MAX
< bigint
) return false;
844 cnt
= (CFIndex
)bigint
;
846 list
= (cnt
<= 256) ? buffer
: CFAllocatorAllocate(kCFAllocatorSystemDefault
, sizeof(CFPropertyListRef
) * cnt
, 0);
847 listAllocator
= (list
== buffer
? kCFAllocatorNull
: kCFAllocatorSystemDefault
);
848 for (idx
= 0; idx
< cnt
; idx
++) {
849 CFPropertyListRef pl
;
850 off
= _getOffsetOfRefAt(databytes
, bytesptr
, trailer
);
851 if (datalen
<= off
) return false;
852 if (!__CFBinaryPlistCreateObject(databytes
, datalen
, off
, trailer
, allocator
, mutabilityOption
, objects
, &pl
)) {
853 if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator
)) {
855 CFRelease(list
[idx
]);
858 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
861 if (CF_IS_COLLECTABLE_ALLOCATOR(allocator
)) {
862 CF_WRITE_BARRIER_BASE_ASSIGN(listAllocator
, list
, list
[idx
], CFMakeCollectable(pl
));
866 bytesptr
+= trailer
->_objectRefSize
;
868 *plist
= _CFArrayCreate_ex(allocator
, (mutabilityOption
!= kCFPropertyListImmutable
), list
, cnt
);
869 if (objects
) CFDictionarySetValue(objects
, (const void *)(intptr_t)startOffset
, *plist
);
870 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
871 return (*plist
) ? true : false;
872 case kCFBinaryPlistMarkerDict
:
874 bytesptr
= databytes
+ startOffset
+ 1;
876 if (!_readInt(bytesptr
, &bigint
, &bytesptr
)) return false;
877 if (INT_MAX
< bigint
) return false;
878 cnt
= (CFIndex
)bigint
;
881 list
= (cnt
<= 256) ? buffer
: CFAllocatorAllocate(kCFAllocatorSystemDefault
, sizeof(CFPropertyListRef
) * cnt
, 0);
882 listAllocator
= (list
== buffer
? kCFAllocatorNull
: kCFAllocatorSystemDefault
);
883 for (idx
= 0; idx
< cnt
; idx
++) {
884 CFPropertyListRef pl
;
885 off
= _getOffsetOfRefAt(databytes
, bytesptr
, trailer
);
886 if (datalen
<= off
) return false;
887 if (!__CFBinaryPlistCreateObject(databytes
, datalen
, off
, trailer
, allocator
, mutabilityOption
, objects
, &pl
)) {
888 if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator
)) {
890 CFRelease(list
[idx
]);
893 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
896 if (CF_IS_COLLECTABLE_ALLOCATOR(allocator
)) {
897 CF_WRITE_BARRIER_BASE_ASSIGN(listAllocator
, list
, list
[idx
], CFMakeCollectable(pl
));
901 bytesptr
+= trailer
->_objectRefSize
;
903 *plist
= _CFDictionaryCreate_ex(allocator
, (mutabilityOption
!= kCFPropertyListImmutable
), list
, list
+ cnt
/ 2, cnt
/ 2);
904 if (objects
) CFDictionarySetValue(objects
, (const void *)(intptr_t)startOffset
, *plist
);
905 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
906 return (*plist
) ? true : false;
911 __private_extern__
bool __CFTryParseBinaryPlist(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags option
, CFPropertyListRef
*plist
, CFStringRef
*errorString
) {
913 CFBinaryPlistTrailer trailer
;
915 CFPropertyListRef pl
;
916 const uint8_t *databytes
= CFDataGetBytePtr(data
);
917 uint64_t datalen
= CFDataGetLength(data
);
919 if (8 <= datalen
&& __CFBinaryPlistGetTopLevelInfo(databytes
, datalen
, &marker
, &offset
, &trailer
)) {
920 if (__CFBinaryPlistCreateObject(databytes
, datalen
, offset
, &trailer
, allocator
, option
, NULL
, &pl
)) {
921 if (plist
) *plist
= pl
;
923 if (plist
) *plist
= NULL
;
924 if (errorString
) *errorString
= CFRetain(CFSTR("binary data is corrupt"));