2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 Copyright 2000-2002, Apple, Inc. All rights reserved.
27 Responsibility: Christopher Kane
30 #include <CoreFoundation/CFBase.h>
31 #include <CoreFoundation/CFString.h>
32 #include <CoreFoundation/CFNumber.h>
33 #include <CoreFoundation/CFDate.h>
34 #include <CoreFoundation/CFData.h>
35 #include <CoreFoundation/CFArray.h>
36 #include <CoreFoundation/CFDictionary.h>
37 #include <CoreFoundation/CFSet.h>
38 #include <CoreFoundation/CFPropertyList.h>
39 #include <CoreFoundation/CFByteOrder.h>
40 #include <CoreFoundation/CFRuntime.h>
41 #include "ForFoundationOnly.h"
45 #include "CFInternal.h"
48 struct __CFKeyedArchiverUID
{
53 static CFStringRef
__CFKeyedArchiverUIDCopyDescription(CFTypeRef cf
) {
54 CFKeyedArchiverUIDRef uid
= (CFKeyedArchiverUIDRef
)cf
;
55 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("<CFKeyedArchiverUID %p [%p]>{value = %u}"), cf
, CFGetAllocator(cf
), uid
->_value
);
58 static CFStringRef
__CFKeyedArchiverUIDCopyFormattingDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
59 CFKeyedArchiverUIDRef uid
= (CFKeyedArchiverUIDRef
)cf
;
60 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("@%u@"), uid
->_value
);
63 static CFTypeID __kCFKeyedArchiverUIDTypeID
= _kCFRuntimeNotATypeID
;
65 static const CFRuntimeClass __CFKeyedArchiverUIDClass
= {
71 NULL
, // equal -- pointer equality only
72 NULL
, // hash -- pointer hashing only
73 __CFKeyedArchiverUIDCopyFormattingDescription
,
74 __CFKeyedArchiverUIDCopyDescription
77 __private_extern__
void __CFKeyedArchiverUIDInitialize(void) {
78 __kCFKeyedArchiverUIDTypeID
= _CFRuntimeRegisterClass(&__CFKeyedArchiverUIDClass
);
81 CFTypeID
_CFKeyedArchiverUIDGetTypeID(void) {
82 return __kCFKeyedArchiverUIDTypeID
;
85 CFKeyedArchiverUIDRef
_CFKeyedArchiverUIDCreate(CFAllocatorRef allocator
, uint32_t value
) {
86 CFKeyedArchiverUIDRef uid
;
87 uid
= (CFKeyedArchiverUIDRef
)_CFRuntimeCreateInstance(allocator
, __kCFKeyedArchiverUIDTypeID
, sizeof(struct __CFKeyedArchiverUID
) - sizeof(CFRuntimeBase
), NULL
);
91 ((struct __CFKeyedArchiverUID
*)uid
)->_value
= value
;
96 uint32_t _CFKeyedArchiverUIDGetValue(CFKeyedArchiverUIDRef uid
) {
106 uint8_t buffer
[8192 - 16];
107 } __CFBinaryPlistWriteBuffer
;
109 static void bufferWrite(__CFBinaryPlistWriteBuffer
*buf
, const uint8_t *buffer
, CFIndex count
) {
111 if ((CFIndex
)sizeof(buf
->buffer
) <= count
) {
112 if (buf
->streamIsData
) {
113 CFDataAppendBytes((CFMutableDataRef
)buf
->stream
, buf
->buffer
, buf
->used
);
116 buf
->written
+= buf
->used
;
118 if (buf
->streamIsData
) {
119 CFDataAppendBytes((CFMutableDataRef
)buf
->stream
, buffer
, count
);
122 buf
->written
+= count
;
125 copyLen
= __CFMin(count
, (CFIndex
)sizeof(buf
->buffer
) - buf
->used
);
126 memmove(buf
->buffer
+ buf
->used
, buffer
, copyLen
);
127 buf
->used
+= copyLen
;
128 if (sizeof(buf
->buffer
) == buf
->used
) {
129 if (buf
->streamIsData
) {
130 CFDataAppendBytes((CFMutableDataRef
)buf
->stream
, buf
->buffer
, sizeof(buf
->buffer
));
133 buf
->written
+= sizeof(buf
->buffer
);
134 memmove(buf
->buffer
, buffer
+ copyLen
, count
- copyLen
);
135 buf
->used
= count
- copyLen
;
139 static void bufferFlush(__CFBinaryPlistWriteBuffer
*buf
) {
140 if (buf
->streamIsData
) {
141 CFDataAppendBytes((CFMutableDataRef
)buf
->stream
, buf
->buffer
, buf
->used
);
144 buf
->written
+= buf
->used
;
150 magic number ("bplist")
154 variable-sized objects
156 Object Formats (marker byte followed by additional info in some cases)
158 bool 0000 1000 // false
159 bool 0000 1001 // true
160 fill 0000 1111 // fill byte
161 int 0001 nnnn ... // # of bytes is 2^nnnn, big-endian bytes
162 real 0010 nnnn ... // # of bytes is 2^nnnn, big-endian bytes
163 date 0011 0011 ... // 8 byte float follows, big-endian bytes
164 data 0100 nnnn [int] ... // nnnn is number of bytes unless 1111 then int count follows, followed by bytes
165 string 0101 nnnn [int] ... // ASCII string, nnnn is # of chars, else 1111 then int count, then bytes
166 string 0110 nnnn [int] ... // Unicode string, nnnn is # of chars, else 1111 then int count, then big-endian 2-byte shorts
168 uid 1000 nnnn ... // nnnn+1 is # of bytes
170 array 1010 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows
173 dict 1101 nnnn [int] keyref* objref* // nnnn is count, unless '1111', then int count follows
178 list of ints, byte size of which is given in trailer
179 -- these are the byte offsets into the file
180 -- number of these is in the trailer
183 byte size of offset ints in offset table
184 byte size of object refs in arrays and dicts
185 number of offsets in offset table (also is number of objects)
186 element # in offset table which is top level object
191 static CFTypeID stringtype
= -1, datatype
= -1, numbertype
= -1, booltype
= -1;
192 static CFTypeID datetype
= -1, dicttype
= -1, arraytype
= -1;
194 static void _appendInt(__CFBinaryPlistWriteBuffer
*buf
, uint64_t bigint
) {
198 if (bigint
<= (uint64_t)0xff) {
200 marker
= kCFBinaryPlistMarkerInt
| 0;
201 } else if (bigint
<= (uint64_t)0xffff) {
203 marker
= kCFBinaryPlistMarkerInt
| 1;
204 } else if (bigint
<= (uint64_t)0xffffffff) {
206 marker
= kCFBinaryPlistMarkerInt
| 2;
209 marker
= kCFBinaryPlistMarkerInt
| 3;
211 bigint
= CFSwapInt64HostToBig(bigint
);
212 bytes
= (uint8_t *)&bigint
+ sizeof(bigint
) - nbytes
;
213 bufferWrite(buf
, &marker
, 1);
214 bufferWrite(buf
, bytes
, nbytes
);
217 static void _appendUID(__CFBinaryPlistWriteBuffer
*buf
, CFKeyedArchiverUIDRef uid
) {
221 uint64_t bigint
= _CFKeyedArchiverUIDGetValue(uid
);
222 if (bigint
<= (uint64_t)0xff) {
224 } else if (bigint
<= (uint64_t)0xffff) {
226 } else if (bigint
<= (uint64_t)0xffffffff) {
231 marker
= kCFBinaryPlistMarkerUID
| (nbytes
- 1);
232 bigint
= CFSwapInt64HostToBig(bigint
);
233 bytes
= (uint8_t *)&bigint
+ sizeof(bigint
) - nbytes
;
234 bufferWrite(buf
, &marker
, 1);
235 bufferWrite(buf
, bytes
, nbytes
);
238 static Boolean
__plistUniquingEqual(CFTypeRef cf1
, CFTypeRef cf2
) {
239 // As long as this equals function is more restrictive than the
240 // existing one, for any given type, the hash function need not
241 // also be provided for the uniquing set.
242 if (CFGetTypeID(cf1
) != CFGetTypeID(cf2
)) return false;
243 if (CFGetTypeID(cf1
) == CFNumberGetTypeID()) {
244 if (CFNumberIsFloatType(cf1
) != CFNumberIsFloatType(cf2
)) return false;
245 return CFEqual(cf1
, cf2
);
247 return CFEqual(cf1
, cf2
);
250 static void _flattenPlist(CFPropertyListRef plist
, CFMutableArrayRef objlist
, CFMutableDictionaryRef objtable
, CFMutableSetRef uniquingset
) {
251 CFPropertyListRef unique
;
253 CFTypeID type
= CFGetTypeID(plist
);
254 CFIndex idx
, before
, after
;
255 CFPropertyListRef
*list
, buffer
[256];
257 // Do not unique dictionaries, because: they are
258 // slow to compare, and produce poor hash codes.
259 // Same is true for arrays, but we still unique them;
260 // they aren't as slow.
261 if (dicttype
!= type
) {
262 before
= CFSetGetCount(uniquingset
);
263 CFSetAddValue(uniquingset
, plist
);
264 after
= CFSetGetCount(uniquingset
);
265 if (after
== before
) { // already in set
266 unique
= CFSetGetValue(uniquingset
, plist
);
267 if (unique
!= plist
) {
268 refnum
= (uint32_t)CFDictionaryGetValue(objtable
, unique
);
269 CFDictionaryAddValue(objtable
, plist
, (const void *)refnum
);
274 refnum
= CFArrayGetCount(objlist
);
275 CFArrayAppendValue(objlist
, plist
);
276 CFDictionaryAddValue(objtable
, plist
, (const void *)refnum
);
277 if (dicttype
== type
) {
278 CFIndex count
= CFDictionaryGetCount(plist
);
279 list
= (count
<= 128) ? buffer
: CFAllocatorAllocate(kCFAllocatorSystemDefault
, 2 * count
* sizeof(CFTypeRef
), 0);
280 CFDictionaryGetKeysAndValues(plist
, list
, list
+ count
);
281 for (idx
= 0; idx
< 2 * count
; idx
++) {
282 _flattenPlist(list
[idx
], objlist
, objtable
, uniquingset
);
284 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
285 } else if (arraytype
== type
) {
286 CFIndex count
= CFArrayGetCount(plist
);
287 list
= (count
<= 256) ? buffer
: CFAllocatorAllocate(kCFAllocatorSystemDefault
, count
* sizeof(CFTypeRef
), 0);
288 CFArrayGetValues(plist
, CFRangeMake(0, count
), list
);
289 for (idx
= 0; idx
< count
; idx
++) {
290 _flattenPlist(list
[idx
], objlist
, objtable
, uniquingset
);
292 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
296 // stream must be a CFMutableDataRef
297 CFIndex
__CFBinaryPlistWriteToStream(CFPropertyListRef plist
, CFTypeRef stream
) {
298 CFMutableDictionaryRef objtable
;
299 CFMutableSetRef uniquingset
;
300 CFMutableArrayRef objlist
;
301 CFBinaryPlistTrailer trailer
;
302 uint64_t *offsets
, length_so_far
;
303 uint64_t mask
, refnum
;
304 int64_t idx
, idx2
, cnt
;
305 __CFBinaryPlistWriteBuffer
*buf
;
306 CFSetCallBacks cb
= kCFTypeSetCallBacks
;
308 if ((CFTypeID
)-1 == stringtype
) {
309 stringtype
= CFStringGetTypeID();
310 datatype
= CFDataGetTypeID();
311 numbertype
= CFNumberGetTypeID();
312 booltype
= CFBooleanGetTypeID();
313 datetype
= CFDateGetTypeID();
314 dicttype
= CFDictionaryGetTypeID();
315 arraytype
= CFArrayGetTypeID();
317 objtable
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
, NULL
);
318 _CFDictionarySetCapacity(objtable
, 320);
319 objlist
= CFArrayCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
);
320 _CFArraySetCapacity(objlist
, 320);
321 cb
.equal
= __plistUniquingEqual
;
322 uniquingset
= CFSetCreateMutable(kCFAllocatorSystemDefault
, 0, &cb
);
323 _CFSetSetCapacity(uniquingset
, 320);
325 _flattenPlist(plist
, objlist
, objtable
, uniquingset
);
327 CFRelease(uniquingset
);
329 cnt
= CFArrayGetCount(objlist
);
330 offsets
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, cnt
* sizeof(*offsets
), 0);
332 buf
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, sizeof(__CFBinaryPlistWriteBuffer
), 0);
333 buf
->stream
= stream
;
334 buf
->streamIsData
= (CFGetTypeID(stream
) == CFDataGetTypeID());
337 bufferWrite(buf
, "bplist00", 8); // header
339 memset(&trailer
, 0, sizeof(trailer
));
340 trailer
._numObjects
= CFSwapInt64HostToBig(cnt
);
341 trailer
._topObject
= 0; // true for this implementation
344 trailer
._objectRefSize
++;
348 for (idx
= 0; idx
< cnt
; idx
++) {
349 CFPropertyListRef obj
= CFArrayGetValueAtIndex(objlist
, idx
);
350 CFTypeID type
= CFGetTypeID(obj
);
351 offsets
[idx
] = buf
->written
+ buf
->used
;
352 if (stringtype
== type
) {
353 CFIndex ret
, count
= CFStringGetLength(obj
);
355 uint8_t *bytes
, buffer
[1024];
356 bytes
= (count
<= 1024) ? buffer
: CFAllocatorAllocate(kCFAllocatorDefault
, count
, 0);
357 // presumption, believed to be true, is that ASCII encoding may need
358 // less bytes, but will not need greater, than the # of unichars
359 ret
= CFStringGetBytes(obj
, CFRangeMake(0, count
), kCFStringEncodingASCII
, 0, false, bytes
, count
, &needed
);
361 uint8_t marker
= kCFBinaryPlistMarkerASCIIString
| (needed
< 15 ? needed
: 0xf);
362 bufferWrite(buf
, &marker
, 1);
364 _appendInt(buf
, (uint64_t)needed
);
366 bufferWrite(buf
, bytes
, needed
);
369 uint8_t marker
= kCFBinaryPlistMarkerUnicode16String
| (count
< 15 ? count
: 0xf);
370 bufferWrite(buf
, &marker
, 1);
372 _appendInt(buf
, (uint64_t)count
);
374 chars
= CFAllocatorAllocate(kCFAllocatorDefault
, count
* sizeof(UniChar
), 0);
375 CFStringGetCharacters(obj
, CFRangeMake(0, count
), chars
);
376 for (idx2
= 0; idx2
< count
; idx2
++) {
377 chars
[idx2
] = CFSwapInt16HostToBig(chars
[idx2
]);
379 bufferWrite(buf
, (uint8_t *)chars
, count
* sizeof(UniChar
));
380 CFAllocatorDeallocate(kCFAllocatorDefault
, chars
);
382 if (bytes
!= buffer
) CFAllocatorDeallocate(kCFAllocatorDefault
, bytes
);
383 } else if (numbertype
== type
) {
385 CFSwappedFloat64 swapped64
;
386 CFSwappedFloat32 swapped32
;
390 if (CFNumberIsFloatType(obj
)) {
391 if (CFNumberGetByteSize(obj
) <= (CFIndex
)sizeof(float)) {
393 CFNumberGetValue(obj
, kCFNumberFloat32Type
, &v
);
394 swapped32
= CFConvertFloat32HostToSwapped(v
);
395 bytes
= (uint8_t *)&swapped32
;
396 nbytes
= sizeof(float);
397 marker
= kCFBinaryPlistMarkerReal
| 2;
400 CFNumberGetValue(obj
, kCFNumberFloat64Type
, &v
);
401 swapped64
= CFConvertFloat64HostToSwapped(v
);
402 bytes
= (uint8_t *)&swapped64
;
403 nbytes
= sizeof(double);
404 marker
= kCFBinaryPlistMarkerReal
| 3;
406 bufferWrite(buf
, &marker
, 1);
407 bufferWrite(buf
, bytes
, nbytes
);
409 CFNumberGetValue(obj
, kCFNumberSInt64Type
, &bigint
);
410 _appendInt(buf
, bigint
);
412 } else if (_CFKeyedArchiverUIDGetTypeID() == type
) {
413 _appendUID(buf
, (CFKeyedArchiverUIDRef
)obj
);
414 } else if (booltype
== type
) {
415 uint8_t marker
= CFBooleanGetValue(obj
) ? kCFBinaryPlistMarkerTrue
: kCFBinaryPlistMarkerFalse
;
416 bufferWrite(buf
, &marker
, 1);
417 } else if (datatype
== type
) {
418 CFIndex count
= CFDataGetLength(obj
);
419 uint8_t marker
= kCFBinaryPlistMarkerData
| (count
< 15 ? count
: 0xf);
420 bufferWrite(buf
, &marker
, 1);
422 _appendInt(buf
, (uint64_t)count
);
424 bufferWrite(buf
, CFDataGetBytePtr(obj
), count
);
425 } else if (datetype
== type
) {
426 CFSwappedFloat64 swapped
;
427 uint8_t marker
= kCFBinaryPlistMarkerDate
;
428 bufferWrite(buf
, &marker
, 1);
429 swapped
= CFConvertFloat64HostToSwapped(CFDateGetAbsoluteTime(obj
));
430 bufferWrite(buf
, (uint8_t *)&swapped
, sizeof(swapped
));
431 } else if (dicttype
== type
) {
432 CFIndex count
= CFDictionaryGetCount(obj
);
433 CFPropertyListRef
*list
, buffer
[512];
434 uint8_t marker
= kCFBinaryPlistMarkerDict
| (count
< 15 ? count
: 0xf);
435 bufferWrite(buf
, &marker
, 1);
437 _appendInt(buf
, (uint64_t)count
);
439 list
= (count
<= 256) ? buffer
: CFAllocatorAllocate(kCFAllocatorSystemDefault
, 2 * count
* sizeof(CFTypeRef
), 0);
440 CFDictionaryGetKeysAndValues(obj
, list
, list
+ count
);
441 for (idx2
= 0; idx2
< 2 * count
; idx2
++) {
442 CFPropertyListRef value
= list
[idx2
];
443 uint32_t swapped
= 0;
444 uint8_t *source
= (uint8_t *)&swapped
;
445 refnum
= (uint32_t)CFDictionaryGetValue(objtable
, value
);
446 swapped
= CFSwapInt32HostToBig(refnum
);
447 bufferWrite(buf
, source
+ sizeof(swapped
) - trailer
._objectRefSize
, trailer
._objectRefSize
);
449 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
450 } else if (arraytype
== type
) {
451 CFIndex count
= CFArrayGetCount(obj
);
452 CFPropertyListRef
*list
, buffer
[256];
453 uint8_t marker
= kCFBinaryPlistMarkerArray
| (count
< 15 ? count
: 0xf);
454 bufferWrite(buf
, &marker
, 1);
456 _appendInt(buf
, (uint64_t)count
);
458 list
= (count
<= 256) ? buffer
: CFAllocatorAllocate(kCFAllocatorSystemDefault
, count
* sizeof(CFTypeRef
), 0);
459 CFArrayGetValues(obj
, CFRangeMake(0, count
), list
);
460 for (idx2
= 0; idx2
< count
; idx2
++) {
461 CFPropertyListRef value
= list
[idx2
];
462 uint32_t swapped
= 0;
463 uint8_t *source
= (uint8_t *)&swapped
;
464 refnum
= (uint32_t)CFDictionaryGetValue(objtable
, value
);
465 swapped
= CFSwapInt32HostToBig(refnum
);
466 bufferWrite(buf
, source
+ sizeof(swapped
) - trailer
._objectRefSize
, trailer
._objectRefSize
);
468 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
472 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, offsets
);
479 length_so_far
= buf
->written
+ buf
->used
;
480 trailer
._offsetTableOffset
= CFSwapInt64HostToBig(length_so_far
);
481 trailer
._offsetIntSize
= 0;
483 while (length_so_far
& mask
) {
484 trailer
._offsetIntSize
++;
488 for (idx
= 0; idx
< cnt
; idx
++) {
489 uint64_t swapped
= CFSwapInt64HostToBig(offsets
[idx
]);
490 uint8_t *source
= (uint8_t *)&swapped
;
491 bufferWrite(buf
, source
+ sizeof(*offsets
) - trailer
._offsetIntSize
, trailer
._offsetIntSize
);
493 length_so_far
+= cnt
* trailer
._offsetIntSize
;
495 bufferWrite(buf
, (uint8_t *)&trailer
, sizeof(trailer
));
497 length_so_far
+= sizeof(trailer
);
498 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, buf
);
499 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, offsets
);
500 return (CFIndex
)length_so_far
;
503 bool __CFBinaryPlistGetTopLevelInfo(CFDataRef data
, uint8_t *marker
, uint64_t *offset
, CFBinaryPlistTrailer
*trailer
) {
504 const uint8_t *databytes
, *bytesptr
;
506 CFBinaryPlistTrailer trail
;
510 if ((CFTypeID
)-1 == stringtype
) {
511 stringtype
= CFStringGetTypeID();
512 datatype
= CFDataGetTypeID();
513 numbertype
= CFNumberGetTypeID();
514 booltype
= CFBooleanGetTypeID();
515 datetype
= CFDateGetTypeID();
516 dicttype
= CFDictionaryGetTypeID();
517 arraytype
= CFArrayGetTypeID();
519 databytes
= CFDataGetBytePtr(data
);
520 datalen
= CFDataGetLength(data
);
521 if (!databytes
|| datalen
< 8 || 0 != memcmp("bplist00", databytes
, 8)) return false;
522 if (datalen
< sizeof(trail
) + 8 + 1) return false;
523 memmove(&trail
, databytes
+ datalen
- sizeof(trail
), sizeof(trail
));
524 trail
._numObjects
= CFSwapInt64BigToHost(trail
._numObjects
);
525 trail
._topObject
= CFSwapInt64BigToHost(trail
._topObject
);
526 if (trail
._numObjects
< trail
._topObject
) return false;
527 trail
._offsetTableOffset
= CFSwapInt64BigToHost(trail
._offsetTableOffset
);
528 if (datalen
< trail
._offsetTableOffset
+ trail
._numObjects
* trail
._offsetIntSize
+ sizeof(trail
)) return false;
529 bytesptr
= databytes
+ trail
._offsetTableOffset
+ trail
._topObject
* trail
._offsetIntSize
;
531 for (idx
= 0; idx
< trail
._offsetIntSize
; idx
++) {
532 off
= (off
<< 8) + bytesptr
[idx
];
534 if (trail
._offsetTableOffset
<= off
) return false;
535 if (trailer
) *trailer
= trail
;
536 if (offset
) *offset
= off
;
537 if (marker
) *marker
= *(databytes
+ off
);
541 static bool _readInt(const uint8_t *ptr
, uint64_t *bigint
, const uint8_t **newptr
) {
545 if ((marker
& 0xf0) != kCFBinaryPlistMarkerInt
) return false;
546 cnt
= 1 << (marker
& 0xf);
548 for (idx
= 0; idx
< cnt
; idx
++) {
549 *bigint
= (*bigint
<< 8) + *ptr
++;
551 if (newptr
) *newptr
= ptr
;
555 static uint64_t _getOffsetOfRefAt(const uint8_t *databytes
, const uint8_t *bytesptr
, const CFBinaryPlistTrailer
*trailer
) {
556 uint64_t ref
= 0, off
= 0;
558 for (idx
= 0; idx
< trailer
->_objectRefSize
; idx
++) {
559 ref
= (ref
<< 8) + bytesptr
[idx
];
561 bytesptr
= databytes
+ trailer
->_offsetTableOffset
+ ref
* trailer
->_offsetIntSize
;
562 for (idx
= 0; idx
< trailer
->_offsetIntSize
; idx
++) {
563 off
= (off
<< 8) + bytesptr
[idx
];
568 bool __CFBinaryPlistGetOffsetForValueFromArray(CFDataRef data
, uint64_t startOffset
, const CFBinaryPlistTrailer
*trailer
, CFIndex idx
, uint64_t *offset
) {
569 const uint8_t *databytes
, *bytesptr
;
573 databytes
= CFDataGetBytePtr(data
);
574 marker
= *(databytes
+ startOffset
);
575 if ((marker
& 0xf0) != kCFBinaryPlistMarkerArray
) return false;
576 cnt
= (marker
& 0x0f);
577 if (cnt
< 15 && cnt
<= idx
) return false;
578 bytesptr
= databytes
+ startOffset
+ 1;
581 if (!_readInt(bytesptr
, &bigint
, &bytesptr
)) return false;
582 if (INT_MAX
< bigint
) return false;
583 cnt
= (CFIndex
)bigint
;
585 if (cnt
<= idx
) return false;
586 off
= _getOffsetOfRefAt(databytes
, bytesptr
+ idx
* trailer
->_objectRefSize
, trailer
);
587 if ((uint64_t)CFDataGetLength(data
) <= off
) return false;
588 if (offset
) *offset
= off
;
592 bool __CFBinaryPlistGetOffsetForValueFromDictionary(CFDataRef data
, uint64_t startOffset
, const CFBinaryPlistTrailer
*trailer
, CFTypeRef key
, uint64_t *koffset
, uint64_t *voffset
) {
593 const uint8_t *databytes
, *refsptr
, *bytesptr
;
596 CFTypeID keytype
= CFGetTypeID(key
);
597 CFIndex idx
, keyn
, cnt
, cnt2
;
599 databytes
= CFDataGetBytePtr(data
);
600 marker
= *(databytes
+ startOffset
);
601 if ((marker
& 0xf0) != kCFBinaryPlistMarkerDict
) return false;
602 cnt
= (marker
& 0x0f);
603 refsptr
= databytes
+ startOffset
+ 1 + 0;
606 if (!_readInt(refsptr
, &bigint
, &refsptr
)) return false;
607 if (INT_MAX
< bigint
) return false;
608 cnt
= (CFIndex
)bigint
;
610 for (keyn
= 0; keyn
< cnt
; keyn
++) {
611 off
= _getOffsetOfRefAt(databytes
, refsptr
, trailer
);
612 if ((uint64_t)CFDataGetLength(data
) <= off
) return false;
613 refsptr
+= trailer
->_objectRefSize
;
614 bytesptr
= databytes
+ off
;
615 marker
= *bytesptr
& 0xf0;
616 cnt2
= *bytesptr
& 0x0f;
617 if (kCFBinaryPlistMarkerASCIIString
== marker
|| kCFBinaryPlistMarkerUnicode16String
== marker
) {
618 CFStringInlineBuffer strbuf
;
620 if (keytype
!= stringtype
) goto miss
;
621 if (0xf == cnt2
&& CFStringGetLength(key
) < 15) goto miss
;
625 if (!_readInt(bytesptr
, &bigint
, &bytesptr
)) return false;
626 if (INT_MAX
< bigint
) return false;
627 cnt2
= (CFIndex
)bigint
;
629 if (cnt2
!= CFStringGetLength(key
)) goto miss
;
630 uchar
= (kCFBinaryPlistMarkerASCIIString
== marker
) ? (UniChar
)bytesptr
[0] : (UniChar
)(bytesptr
[0] * 256 + bytesptr
[1]);
631 if (uchar
!= CFStringGetCharacterAtIndex(key
, 0)) goto miss
;
632 bytesptr
+= (kCFBinaryPlistMarkerASCIIString
== marker
) ? 1 : 2;
633 CFStringInitInlineBuffer(key
, &strbuf
, CFRangeMake(0, cnt2
));
634 for (idx
= 1; idx
< cnt2
; idx
++) {
635 uchar
= (kCFBinaryPlistMarkerASCIIString
== marker
) ? (UniChar
)bytesptr
[0] : (UniChar
)(bytesptr
[0] * 256 + bytesptr
[1]);
636 if (uchar
!= __CFStringGetCharacterFromInlineBufferQuick(&strbuf
, idx
)) goto miss
;
637 bytesptr
+= (kCFBinaryPlistMarkerASCIIString
== marker
) ? 1 : 2;
639 if (koffset
) *koffset
= off
;
640 off
= _getOffsetOfRefAt(databytes
, refsptr
+ (cnt
- 1) * trailer
->_objectRefSize
, trailer
);
641 if ((uint64_t)CFDataGetLength(data
) <= off
) return false;
642 if (voffset
) *voffset
= off
;
645 //#warning the other primitive types should be allowed as keys in a binary plist dictionary, I think
653 extern CFArrayRef
_CFArrayCreate_ex(CFAllocatorRef allocator
, bool mutable, const void **values
, CFIndex numValues
);
655 extern CFDictionaryRef
_CFDictionaryCreate_ex(CFAllocatorRef allocator
, bool mutable, const void **keys
, const void **values
, CFIndex numValues
);
658 static bool _getUIDFromData(const uint8_t *datap
, uint64_t *vp
) {
660 uint8_t marker
= *datap
;
662 if ((marker
& 0xf0) != kCFBinaryPlistMarkerUID
) return false;
663 cnt
= (marker
& 0x0f) + 1;
666 for (idx
= 0; idx
< cnt
; idx
++) {
667 bigint
= (bigint
<< 8) + *datap
++;
674 static bool _getFloatFromData(const uint8_t *datap
, float *vp
) {
675 CFSwappedFloat32 swapped32
;
676 if (*datap
!= (kCFBinaryPlistMarkerReal
| 2)) return false;
678 memmove(&swapped32
, datap
, sizeof(swapped32
));
679 *vp
= CFConvertFloat32SwappedToHost(swapped32
);
683 static bool _getDoubleFromData(const uint8_t *datap
, double *vp
) {
684 CFSwappedFloat64 swapped64
;
685 if (*datap
!= (kCFBinaryPlistMarkerReal
| 3)) return false;
687 memmove(&swapped64
, datap
, sizeof(swapped64
));
688 *vp
= CFConvertFloat64SwappedToHost(swapped64
);
692 bool __CFBinaryPlistCreateObject(CFDataRef data
, uint64_t startOffset
, const CFBinaryPlistTrailer
*trailer
, CFAllocatorRef allocator
, CFOptionFlags mutabilityOption
, CFMutableDictionaryRef objects
, CFPropertyListRef
*plist
) {
693 const uint8_t *databytes
, *bytesptr
;
699 CFPropertyListRef
*list
, buffer
[256];
702 *plist
= CFDictionaryGetValue(objects
, (const void *)(intptr_t)startOffset
);
709 databytes
= CFDataGetBytePtr(data
);
710 marker
= *(databytes
+ startOffset
);
711 switch (marker
& 0xf0) {
712 case kCFBinaryPlistMarkerNull
:
714 case kCFBinaryPlistMarkerNull
:
717 case kCFBinaryPlistMarkerFalse
:
718 *plist
= CFRetain(kCFBooleanFalse
);
720 case kCFBinaryPlistMarkerTrue
:
721 *plist
= CFRetain(kCFBooleanTrue
);
725 case kCFBinaryPlistMarkerInt
:
726 if (!_readInt(databytes
+ startOffset
, &bigint
, NULL
)) return false;
727 *plist
= CFNumberCreate(allocator
, kCFNumberSInt64Type
, &bigint
);
728 if (objects
) CFDictionarySetValue(objects
, (const void *)(intptr_t)startOffset
, *plist
);
729 return (*plist
) ? true : false;
730 case kCFBinaryPlistMarkerReal
:
734 _getFloatFromData(databytes
+ startOffset
, &f
);
735 *plist
= CFNumberCreate(allocator
, kCFNumberFloat32Type
, &f
);
736 if (objects
) CFDictionarySetValue(objects
, (const void *)(intptr_t)startOffset
, *plist
);
737 return (*plist
) ? true : false;
738 } else if (3 == cnt
) {
740 _getDoubleFromData(databytes
+ startOffset
, &d
);
741 *plist
= CFNumberCreate(allocator
, kCFNumberFloat64Type
, &d
);
742 if (objects
) CFDictionarySetValue(objects
, (const void *)(intptr_t)startOffset
, *plist
);
743 return (*plist
) ? true : false;
746 case kCFBinaryPlistMarkerDate
& 0xf0: {
747 CFSwappedFloat64 swapped64
;
750 if (3 != cnt
) return false;
751 memmove(&swapped64
, databytes
+ startOffset
+ 1, sizeof(swapped64
));
752 d
= CFConvertFloat64SwappedToHost(swapped64
);
753 *plist
= CFDateCreate(allocator
, d
);
754 if (objects
) CFDictionarySetValue(objects
, (const void *)(intptr_t)startOffset
, *plist
);
755 return (*plist
) ? true : false;
757 case kCFBinaryPlistMarkerData
:
759 bytesptr
= databytes
+ startOffset
+ 1;
761 if (!_readInt(bytesptr
, &bigint
, &bytesptr
)) return false;
762 if (INT_MAX
< bigint
) return false;
763 cnt
= (CFIndex
)bigint
;
765 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
766 *plist
= CFDataCreateMutable(allocator
, 0);
767 CFDataAppendBytes((CFMutableDataRef
)*plist
, bytesptr
, cnt
);
769 *plist
= CFDataCreate(allocator
, bytesptr
, cnt
);
771 if (objects
) CFDictionarySetValue(objects
, (const void *)(intptr_t)startOffset
, *plist
);
772 return (*plist
) ? true : false;
773 case kCFBinaryPlistMarkerASCIIString
:
775 bytesptr
= databytes
+ startOffset
+ 1;
777 if (!_readInt(bytesptr
, &bigint
, &bytesptr
)) return false;
778 if (INT_MAX
< bigint
) return false;
779 cnt
= (CFIndex
)bigint
;
781 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
782 CFStringRef str
= CFStringCreateWithBytes(allocator
, bytesptr
, cnt
, kCFStringEncodingASCII
, false);
783 *plist
= CFStringCreateMutableCopy(allocator
, 0, str
);
786 *plist
= CFStringCreateWithBytes(allocator
, bytesptr
, cnt
, kCFStringEncodingASCII
, false);
788 if (objects
) CFDictionarySetValue(objects
, (const void *)(intptr_t)startOffset
, *plist
);
789 return (*plist
) ? true : false;
790 case kCFBinaryPlistMarkerUnicode16String
:
792 bytesptr
= databytes
+ startOffset
+ 1;
794 if (!_readInt(bytesptr
, &bigint
, &bytesptr
)) return false;
795 if (INT_MAX
< bigint
) return false;
796 cnt
= (CFIndex
)bigint
;
798 chars
= CFAllocatorAllocate(allocator
, cnt
* sizeof(UniChar
), 0);
799 memmove(chars
, bytesptr
, cnt
* sizeof(UniChar
));
800 for (idx
= 0; idx
< cnt
; idx
++) {
801 chars
[idx
] = CFSwapInt16BigToHost(chars
[idx
]);
803 if (mutabilityOption
== kCFPropertyListMutableContainersAndLeaves
) {
804 CFStringRef str
= CFStringCreateWithCharactersNoCopy(allocator
, chars
, cnt
, allocator
);
805 *plist
= CFStringCreateMutableCopy(allocator
, 0, str
);
808 *plist
= CFStringCreateWithCharactersNoCopy(allocator
, chars
, cnt
, allocator
);
810 if (objects
) CFDictionarySetValue(objects
, (const void *)(intptr_t)startOffset
, *plist
);
811 return (*plist
) ? true : false;
812 case kCFBinaryPlistMarkerUID
:
813 cnt
= (marker
& 0x0f) + 1;
814 bytesptr
= databytes
+ startOffset
+ 1;
816 for (idx
= 0; idx
< cnt
; idx
++) {
817 bigint
= (bigint
<< 8) + *bytesptr
++;
819 if (UINT_MAX
< bigint
) return false;
820 *plist
= _CFKeyedArchiverUIDCreate(allocator
, (uint32_t)bigint
);
821 if (objects
) CFDictionarySetValue(objects
, (const void *)(intptr_t)startOffset
, *plist
);
822 return (*plist
) ? true : false;
823 case kCFBinaryPlistMarkerArray
:
825 bytesptr
= databytes
+ startOffset
+ 1;
827 if (!_readInt(bytesptr
, &bigint
, &bytesptr
)) return false;
828 if (INT_MAX
< bigint
) return false;
829 cnt
= (CFIndex
)bigint
;
831 list
= (cnt
<= 256) ? buffer
: CFAllocatorAllocate(kCFAllocatorSystemDefault
, sizeof(CFPropertyListRef
) * cnt
, 0);
832 for (idx
= 0; idx
< cnt
; idx
++) {
833 CFPropertyListRef pl
;
834 off
= _getOffsetOfRefAt(databytes
, bytesptr
, trailer
);
835 if ((uint64_t)CFDataGetLength(data
) <= off
) return false;
836 if (!__CFBinaryPlistCreateObject(data
, off
, trailer
, allocator
, mutabilityOption
, objects
, &pl
)) {
838 CFRelease(list
[idx
]);
840 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
844 bytesptr
+= trailer
->_objectRefSize
;
846 *plist
= _CFArrayCreate_ex(allocator
, (mutabilityOption
!= kCFPropertyListImmutable
), list
, cnt
);
847 if (objects
) CFDictionarySetValue(objects
, (const void *)(intptr_t)startOffset
, *plist
);
848 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
849 return (*plist
) ? true : false;
850 case kCFBinaryPlistMarkerDict
:
852 bytesptr
= databytes
+ startOffset
+ 1;
854 if (!_readInt(bytesptr
, &bigint
, &bytesptr
)) return false;
855 if (INT_MAX
< bigint
) return false;
856 cnt
= (CFIndex
)bigint
;
859 list
= (cnt
<= 256) ? buffer
: CFAllocatorAllocate(kCFAllocatorSystemDefault
, sizeof(CFPropertyListRef
) * cnt
, 0);
860 for (idx
= 0; idx
< cnt
; idx
++) {
861 CFPropertyListRef pl
;
862 off
= _getOffsetOfRefAt(databytes
, bytesptr
, trailer
);
863 if ((uint64_t)CFDataGetLength(data
) <= off
) return false;
864 if (!__CFBinaryPlistCreateObject(data
, off
, trailer
, allocator
, mutabilityOption
, objects
, &pl
)) {
866 CFRelease(list
[idx
]);
868 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
872 bytesptr
+= trailer
->_objectRefSize
;
874 *plist
= _CFDictionaryCreate_ex(allocator
, (mutabilityOption
!= kCFPropertyListImmutable
), list
, list
+ cnt
/ 2, cnt
/ 2);
875 if (objects
) CFDictionarySetValue(objects
, (const void *)(intptr_t)startOffset
, *plist
);
876 if (list
!= buffer
) CFAllocatorDeallocate(kCFAllocatorSystemDefault
, list
);
877 return (*plist
) ? true : false;
882 __private_extern__
bool __CFTryParseBinaryPlist(CFAllocatorRef allocator
, CFDataRef data
, CFOptionFlags option
, CFPropertyListRef
*plist
, CFStringRef
*errorString
) {
884 CFBinaryPlistTrailer trailer
;
886 CFPropertyListRef pl
;
888 if (8 <= CFDataGetLength(data
) && __CFBinaryPlistGetTopLevelInfo(data
, &marker
, &offset
, &trailer
)) {
889 if (__CFBinaryPlistCreateObject(data
, offset
, &trailer
, allocator
, option
, NULL
, &pl
)) {
890 if (plist
) *plist
= pl
;
892 if (plist
) *plist
= NULL
;
893 if (errorString
) *errorString
= CFRetain(CFSTR("binary data is corrupt"));