]> git.saurik.com Git - apple/cf.git/blob - Parsing.subproj/CFBinaryPList.c
CF-299.tar.gz
[apple/cf.git] / Parsing.subproj / CFBinaryPList.c
1 /*
2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
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
13 * file.
14 *
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.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /* CFBinaryPList.c
26 Copyright 2000-2002, Apple, Inc. All rights reserved.
27 Responsibility: Christopher Kane
28 */
29
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"
42 #include <stdio.h>
43 #include <limits.h>
44 #include <string.h>
45 #include "CFInternal.h"
46
47
48 struct __CFKeyedArchiverUID {
49 CFRuntimeBase _base;
50 uint32_t _value;
51 };
52
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);
56 }
57
58 static CFStringRef __CFKeyedArchiverUIDCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
59 CFKeyedArchiverUIDRef uid = (CFKeyedArchiverUIDRef)cf;
60 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("@%u@"), uid->_value);
61 }
62
63 static CFTypeID __kCFKeyedArchiverUIDTypeID = _kCFRuntimeNotATypeID;
64
65 static const CFRuntimeClass __CFKeyedArchiverUIDClass = {
66 0,
67 "CFKeyedArchiverUID",
68 NULL, // init
69 NULL, // copy
70 NULL, // finalize
71 NULL, // equal -- pointer equality only
72 NULL, // hash -- pointer hashing only
73 __CFKeyedArchiverUIDCopyFormattingDescription,
74 __CFKeyedArchiverUIDCopyDescription
75 };
76
77 __private_extern__ void __CFKeyedArchiverUIDInitialize(void) {
78 __kCFKeyedArchiverUIDTypeID = _CFRuntimeRegisterClass(&__CFKeyedArchiverUIDClass);
79 }
80
81 CFTypeID _CFKeyedArchiverUIDGetTypeID(void) {
82 return __kCFKeyedArchiverUIDTypeID;
83 }
84
85 CFKeyedArchiverUIDRef _CFKeyedArchiverUIDCreate(CFAllocatorRef allocator, uint32_t value) {
86 CFKeyedArchiverUIDRef uid;
87 uid = (CFKeyedArchiverUIDRef)_CFRuntimeCreateInstance(allocator, __kCFKeyedArchiverUIDTypeID, sizeof(struct __CFKeyedArchiverUID) - sizeof(CFRuntimeBase), NULL);
88 if (NULL == uid) {
89 return NULL;
90 }
91 ((struct __CFKeyedArchiverUID *)uid)->_value = value;
92 return uid;
93 }
94
95
96 uint32_t _CFKeyedArchiverUIDGetValue(CFKeyedArchiverUIDRef uid) {
97 return uid->_value;
98 }
99
100
101 typedef struct {
102 CFTypeRef stream;
103 bool streamIsData;
104 uint64_t written;
105 int32_t used;
106 uint8_t buffer[8192 - 16];
107 } __CFBinaryPlistWriteBuffer;
108
109 static void bufferWrite(__CFBinaryPlistWriteBuffer *buf, const uint8_t *buffer, CFIndex count) {
110 CFIndex copyLen;
111 if ((CFIndex)sizeof(buf->buffer) <= count) {
112 if (buf->streamIsData) {
113 CFDataAppendBytes((CFMutableDataRef)buf->stream, buf->buffer, buf->used);
114 } else {
115 }
116 buf->written += buf->used;
117 buf->used = 0;
118 if (buf->streamIsData) {
119 CFDataAppendBytes((CFMutableDataRef)buf->stream, buffer, count);
120 } else {
121 }
122 buf->written += count;
123 return;
124 }
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));
131 } else {
132 }
133 buf->written += sizeof(buf->buffer);
134 memmove(buf->buffer, buffer + copyLen, count - copyLen);
135 buf->used = count - copyLen;
136 }
137 }
138
139 static void bufferFlush(__CFBinaryPlistWriteBuffer *buf) {
140 if (buf->streamIsData) {
141 CFDataAppendBytes((CFMutableDataRef)buf->stream, buf->buffer, buf->used);
142 } else {
143 }
144 buf->written += buf->used;
145 buf->used = 0;
146 }
147
148 /*
149 HEADER
150 magic number ("bplist")
151 file format version
152
153 OBJECT TABLE
154 variable-sized objects
155
156 Object Formats (marker byte followed by additional info in some cases)
157 null 0000 0000
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
167 0111 xxxx // unused
168 uid 1000 nnnn ... // nnnn+1 is # of bytes
169 1001 xxxx // unused
170 array 1010 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows
171 1011 xxxx // unused
172 1100 xxxx // unused
173 dict 1101 nnnn [int] keyref* objref* // nnnn is count, unless '1111', then int count follows
174 1110 xxxx // unused
175 1111 xxxx // unused
176
177 OFFSET TABLE
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
181
182 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
187
188 */
189
190
191 static CFTypeID stringtype = -1, datatype = -1, numbertype = -1, booltype = -1;
192 static CFTypeID datetype = -1, dicttype = -1, arraytype = -1;
193
194 static void _appendInt(__CFBinaryPlistWriteBuffer *buf, uint64_t bigint) {
195 uint8_t marker;
196 uint8_t *bytes;
197 CFIndex nbytes;
198 if (bigint <= (uint64_t)0xff) {
199 nbytes = 1;
200 marker = kCFBinaryPlistMarkerInt | 0;
201 } else if (bigint <= (uint64_t)0xffff) {
202 nbytes = 2;
203 marker = kCFBinaryPlistMarkerInt | 1;
204 } else if (bigint <= (uint64_t)0xffffffff) {
205 nbytes = 4;
206 marker = kCFBinaryPlistMarkerInt | 2;
207 } else {
208 nbytes = 8;
209 marker = kCFBinaryPlistMarkerInt | 3;
210 }
211 bigint = CFSwapInt64HostToBig(bigint);
212 bytes = (uint8_t *)&bigint + sizeof(bigint) - nbytes;
213 bufferWrite(buf, &marker, 1);
214 bufferWrite(buf, bytes, nbytes);
215 }
216
217 static void _appendUID(__CFBinaryPlistWriteBuffer *buf, CFKeyedArchiverUIDRef uid) {
218 uint8_t marker;
219 uint8_t *bytes;
220 CFIndex nbytes;
221 uint64_t bigint = _CFKeyedArchiverUIDGetValue(uid);
222 if (bigint <= (uint64_t)0xff) {
223 nbytes = 1;
224 } else if (bigint <= (uint64_t)0xffff) {
225 nbytes = 2;
226 } else if (bigint <= (uint64_t)0xffffffff) {
227 nbytes = 4;
228 } else {
229 nbytes = 8;
230 }
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);
236 }
237
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);
246 }
247 return CFEqual(cf1, cf2);
248 }
249
250 static void _flattenPlist(CFPropertyListRef plist, CFMutableArrayRef objlist, CFMutableDictionaryRef objtable, CFMutableSetRef uniquingset) {
251 CFPropertyListRef unique;
252 uint32_t refnum;
253 CFTypeID type = CFGetTypeID(plist);
254 CFIndex idx, before, after;
255 CFPropertyListRef *list, buffer[256];
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);
270 }
271 return;
272 }
273 }
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);
283 }
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);
291 }
292 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
293 }
294 }
295
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;
307
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();
316 }
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);
324
325 _flattenPlist(plist, objlist, objtable, uniquingset);
326
327 CFRelease(uniquingset);
328
329 cnt = CFArrayGetCount(objlist);
330 offsets = CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(*offsets), 0);
331
332 buf = CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(__CFBinaryPlistWriteBuffer), 0);
333 buf->stream = stream;
334 buf->streamIsData = (CFGetTypeID(stream) == CFDataGetTypeID());
335 buf->written = 0;
336 buf->used = 0;
337 bufferWrite(buf, "bplist00", 8); // header
338
339 memset(&trailer, 0, sizeof(trailer));
340 trailer._numObjects = CFSwapInt64HostToBig(cnt);
341 trailer._topObject = 0; // true for this implementation
342 mask = ~(uint64_t)0;
343 while (cnt & mask) {
344 trailer._objectRefSize++;
345 mask = mask << 8;
346 }
347
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);
354 CFIndex needed;
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);
360 if (ret == count) {
361 uint8_t marker = kCFBinaryPlistMarkerASCIIString | (needed < 15 ? needed : 0xf);
362 bufferWrite(buf, &marker, 1);
363 if (15 <= needed) {
364 _appendInt(buf, (uint64_t)needed);
365 }
366 bufferWrite(buf, bytes, needed);
367 } else {
368 UniChar *chars;
369 uint8_t marker = kCFBinaryPlistMarkerUnicode16String | (count < 15 ? count : 0xf);
370 bufferWrite(buf, &marker, 1);
371 if (15 <= count) {
372 _appendInt(buf, (uint64_t)count);
373 }
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]);
378 }
379 bufferWrite(buf, (uint8_t *)chars, count * sizeof(UniChar));
380 CFAllocatorDeallocate(kCFAllocatorDefault, chars);
381 }
382 if (bytes != buffer) CFAllocatorDeallocate(kCFAllocatorDefault, bytes);
383 } else if (numbertype == type) {
384 uint8_t marker;
385 CFSwappedFloat64 swapped64;
386 CFSwappedFloat32 swapped32;
387 uint64_t bigint;
388 uint8_t *bytes;
389 CFIndex nbytes;
390 if (CFNumberIsFloatType(obj)) {
391 if (CFNumberGetByteSize(obj) <= (CFIndex)sizeof(float)) {
392 float v;
393 CFNumberGetValue(obj, kCFNumberFloat32Type, &v);
394 swapped32 = CFConvertFloat32HostToSwapped(v);
395 bytes = (uint8_t *)&swapped32;
396 nbytes = sizeof(float);
397 marker = kCFBinaryPlistMarkerReal | 2;
398 } else {
399 double v;
400 CFNumberGetValue(obj, kCFNumberFloat64Type, &v);
401 swapped64 = CFConvertFloat64HostToSwapped(v);
402 bytes = (uint8_t *)&swapped64;
403 nbytes = sizeof(double);
404 marker = kCFBinaryPlistMarkerReal | 3;
405 }
406 bufferWrite(buf, &marker, 1);
407 bufferWrite(buf, bytes, nbytes);
408 } else {
409 CFNumberGetValue(obj, kCFNumberSInt64Type, &bigint);
410 _appendInt(buf, bigint);
411 }
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);
421 if (15 <= count) {
422 _appendInt(buf, (uint64_t)count);
423 }
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);
436 if (15 <= count) {
437 _appendInt(buf, (uint64_t)count);
438 }
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);
448 }
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);
455 if (15 <= count) {
456 _appendInt(buf, (uint64_t)count);
457 }
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);
467 }
468 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
469 } else {
470 CFRelease(objtable);
471 CFRelease(objlist);
472 CFAllocatorDeallocate(kCFAllocatorSystemDefault, offsets);
473 return 0;
474 }
475 }
476 CFRelease(objtable);
477 CFRelease(objlist);
478
479 length_so_far = buf->written + buf->used;
480 trailer._offsetTableOffset = CFSwapInt64HostToBig(length_so_far);
481 trailer._offsetIntSize = 0;
482 mask = ~(uint64_t)0;
483 while (length_so_far & mask) {
484 trailer._offsetIntSize++;
485 mask = mask << 8;
486 }
487
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);
492 }
493 length_so_far += cnt * trailer._offsetIntSize;
494
495 bufferWrite(buf, (uint8_t *)&trailer, sizeof(trailer));
496 bufferFlush(buf);
497 length_so_far += sizeof(trailer);
498 CFAllocatorDeallocate(kCFAllocatorSystemDefault, buf);
499 CFAllocatorDeallocate(kCFAllocatorSystemDefault, offsets);
500 return (CFIndex)length_so_far;
501 }
502
503 bool __CFBinaryPlistGetTopLevelInfo(CFDataRef data, uint8_t *marker, uint64_t *offset, CFBinaryPlistTrailer *trailer) {
504 const uint8_t *databytes, *bytesptr;
505 uint64_t datalen;
506 CFBinaryPlistTrailer trail;
507 uint64_t off;
508 CFIndex idx;
509
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();
518 }
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;
530 off = 0;
531 for (idx = 0; idx < trail._offsetIntSize; idx++) {
532 off = (off << 8) + bytesptr[idx];
533 }
534 if (trail._offsetTableOffset <= off) return false;
535 if (trailer) *trailer = trail;
536 if (offset) *offset = off;
537 if (marker) *marker = *(databytes + off);
538 return true;
539 }
540
541 static bool _readInt(const uint8_t *ptr, uint64_t *bigint, const uint8_t **newptr) {
542 uint8_t marker;
543 CFIndex idx, cnt;
544 marker = *ptr++;
545 if ((marker & 0xf0) != kCFBinaryPlistMarkerInt) return false;
546 cnt = 1 << (marker & 0xf);
547 *bigint = 0;
548 for (idx = 0; idx < cnt; idx++) {
549 *bigint = (*bigint << 8) + *ptr++;
550 }
551 if (newptr) *newptr = ptr;
552 return true;
553 }
554
555 static uint64_t _getOffsetOfRefAt(const uint8_t *databytes, const uint8_t *bytesptr, const CFBinaryPlistTrailer *trailer) {
556 uint64_t ref = 0, off = 0;
557 CFIndex idx;
558 for (idx = 0; idx < trailer->_objectRefSize; idx++) {
559 ref = (ref << 8) + bytesptr[idx];
560 }
561 bytesptr = databytes + trailer->_offsetTableOffset + ref * trailer->_offsetIntSize;
562 for (idx = 0; idx < trailer->_offsetIntSize; idx++) {
563 off = (off << 8) + bytesptr[idx];
564 }
565 return off;
566 }
567
568 bool __CFBinaryPlistGetOffsetForValueFromArray(CFDataRef data, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFIndex idx, uint64_t *offset) {
569 const uint8_t *databytes, *bytesptr;
570 uint8_t marker;
571 CFIndex cnt;
572 uint64_t off;
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;
579 if (0xf == cnt) {
580 uint64_t bigint;
581 if (!_readInt(bytesptr, &bigint, &bytesptr)) return false;
582 if (INT_MAX < bigint) return false;
583 cnt = (CFIndex)bigint;
584 }
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;
589 return true;
590 }
591
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;
594 uint64_t off;
595 uint8_t marker;
596 CFTypeID keytype = CFGetTypeID(key);
597 CFIndex idx, keyn, cnt, cnt2;
598
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;
604 if (0xf == cnt) {
605 uint64_t bigint;
606 if (!_readInt(refsptr, &bigint, &refsptr)) return false;
607 if (INT_MAX < bigint) return false;
608 cnt = (CFIndex)bigint;
609 }
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;
619 UniChar uchar;
620 if (keytype != stringtype) goto miss;
621 if (0xf == cnt2 && CFStringGetLength(key) < 15) goto miss;
622 bytesptr++;
623 if (0xf == cnt2) {
624 uint64_t bigint;
625 if (!_readInt(bytesptr, &bigint, &bytesptr)) return false;
626 if (INT_MAX < bigint) return false;
627 cnt2 = (CFIndex)bigint;
628 }
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;
638 }
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;
643 return true;
644 } else {
645 //#warning the other primitive types should be allowed as keys in a binary plist dictionary, I think
646 return false;
647 }
648 miss: ;
649 }
650 return false;
651 }
652
653 extern CFArrayRef _CFArrayCreate_ex(CFAllocatorRef allocator, bool mutable, const void **values, CFIndex numValues);
654
655 extern CFDictionaryRef _CFDictionaryCreate_ex(CFAllocatorRef allocator, bool mutable, const void **keys, const void **values, CFIndex numValues);
656
657 #if 0
658 static bool _getUIDFromData(const uint8_t *datap, uint64_t *vp) {
659 int32_t idx, cnt;
660 uint8_t marker = *datap;
661 uint64_t bigint;
662 if ((marker & 0xf0) != kCFBinaryPlistMarkerUID) return false;
663 cnt = (marker & 0x0f) + 1;
664 datap++;
665 bigint = 0;
666 for (idx = 0; idx < cnt; idx++) {
667 bigint = (bigint << 8) + *datap++;
668 }
669 *vp = bigint;
670 return true;
671 }
672 #endif
673
674 static bool _getFloatFromData(const uint8_t *datap, float *vp) {
675 CFSwappedFloat32 swapped32;
676 if (*datap != (kCFBinaryPlistMarkerReal | 2)) return false;
677 datap++;
678 memmove(&swapped32, datap, sizeof(swapped32));
679 *vp = CFConvertFloat32SwappedToHost(swapped32);
680 return true;
681 }
682
683 static bool _getDoubleFromData(const uint8_t *datap, double *vp) {
684 CFSwappedFloat64 swapped64;
685 if (*datap != (kCFBinaryPlistMarkerReal | 3)) return false;
686 datap++;
687 memmove(&swapped64, datap, sizeof(swapped64));
688 *vp = CFConvertFloat64SwappedToHost(swapped64);
689 return true;
690 }
691
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;
694 uint64_t off;
695 uint8_t marker;
696 CFIndex idx, cnt;
697 uint64_t bigint;
698 UniChar *chars;
699 CFPropertyListRef *list, buffer[256];
700
701 if (objects) {
702 *plist = CFDictionaryGetValue(objects, (const void *)(intptr_t)startOffset);
703 if (*plist) {
704 CFRetain(*plist);
705 return true;
706 }
707 }
708
709 databytes = CFDataGetBytePtr(data);
710 marker = *(databytes + startOffset);
711 switch (marker & 0xf0) {
712 case kCFBinaryPlistMarkerNull:
713 switch (marker) {
714 case kCFBinaryPlistMarkerNull:
715 *plist = NULL;
716 return true;
717 case kCFBinaryPlistMarkerFalse:
718 *plist = CFRetain(kCFBooleanFalse);
719 return true;
720 case kCFBinaryPlistMarkerTrue:
721 *plist = CFRetain(kCFBooleanTrue);
722 return true;
723 }
724 return false;
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:
731 cnt = marker & 0x0f;
732 if (2 == cnt) {
733 float f;
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) {
739 double d;
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;
744 }
745 return false;
746 case kCFBinaryPlistMarkerDate & 0xf0: {
747 CFSwappedFloat64 swapped64;
748 double d;
749 cnt = marker & 0x0f;
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;
756 }
757 case kCFBinaryPlistMarkerData:
758 cnt = marker & 0x0f;
759 bytesptr = databytes + startOffset + 1;
760 if (0xf == cnt) {
761 if (!_readInt(bytesptr, &bigint, &bytesptr)) return false;
762 if (INT_MAX < bigint) return false;
763 cnt = (CFIndex)bigint;
764 }
765 if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) {
766 *plist = CFDataCreateMutable(allocator, 0);
767 CFDataAppendBytes((CFMutableDataRef)*plist, bytesptr, cnt);
768 } else {
769 *plist = CFDataCreate(allocator, bytesptr, cnt);
770 }
771 if (objects) CFDictionarySetValue(objects, (const void *)(intptr_t)startOffset, *plist);
772 return (*plist) ? true : false;
773 case kCFBinaryPlistMarkerASCIIString:
774 cnt = marker & 0x0f;
775 bytesptr = databytes + startOffset + 1;
776 if (0xf == cnt) {
777 if (!_readInt(bytesptr, &bigint, &bytesptr)) return false;
778 if (INT_MAX < bigint) return false;
779 cnt = (CFIndex)bigint;
780 }
781 if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) {
782 CFStringRef str = CFStringCreateWithBytes(allocator, bytesptr, cnt, kCFStringEncodingASCII, false);
783 *plist = CFStringCreateMutableCopy(allocator, 0, str);
784 CFRelease(str);
785 } else {
786 *plist = CFStringCreateWithBytes(allocator, bytesptr, cnt, kCFStringEncodingASCII, false);
787 }
788 if (objects) CFDictionarySetValue(objects, (const void *)(intptr_t)startOffset, *plist);
789 return (*plist) ? true : false;
790 case kCFBinaryPlistMarkerUnicode16String:
791 cnt = marker & 0x0f;
792 bytesptr = databytes + startOffset + 1;
793 if (0xf == cnt) {
794 if (!_readInt(bytesptr, &bigint, &bytesptr)) return false;
795 if (INT_MAX < bigint) return false;
796 cnt = (CFIndex)bigint;
797 }
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]);
802 }
803 if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) {
804 CFStringRef str = CFStringCreateWithCharactersNoCopy(allocator, chars, cnt, allocator);
805 *plist = CFStringCreateMutableCopy(allocator, 0, str);
806 CFRelease(str);
807 } else {
808 *plist = CFStringCreateWithCharactersNoCopy(allocator, chars, cnt, allocator);
809 }
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;
815 bigint = 0;
816 for (idx = 0; idx < cnt; idx++) {
817 bigint = (bigint << 8) + *bytesptr++;
818 }
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:
824 cnt = marker & 0x0f;
825 bytesptr = databytes + startOffset + 1;
826 if (0xf == cnt) {
827 if (!_readInt(bytesptr, &bigint, &bytesptr)) return false;
828 if (INT_MAX < bigint) return false;
829 cnt = (CFIndex)bigint;
830 }
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)) {
837 while (idx--) {
838 CFRelease(list[idx]);
839 }
840 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
841 return false;
842 }
843 list[idx] = pl;
844 bytesptr += trailer->_objectRefSize;
845 }
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:
851 cnt = marker & 0x0f;
852 bytesptr = databytes + startOffset + 1;
853 if (0xf == cnt) {
854 if (!_readInt(bytesptr, &bigint, &bytesptr)) return false;
855 if (INT_MAX < bigint) return false;
856 cnt = (CFIndex)bigint;
857 }
858 cnt *= 2;
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)) {
865 while (idx--) {
866 CFRelease(list[idx]);
867 }
868 if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
869 return false;
870 }
871 list[idx] = pl;
872 bytesptr += trailer->_objectRefSize;
873 }
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;
878 }
879 return false;
880 }
881
882 __private_extern__ bool __CFTryParseBinaryPlist(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags option, CFPropertyListRef *plist, CFStringRef *errorString) {
883 uint8_t marker;
884 CFBinaryPlistTrailer trailer;
885 uint64_t offset;
886 CFPropertyListRef pl;
887
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;
891 } else {
892 if (plist) *plist = NULL;
893 if (errorString) *errorString = CFRetain(CFSTR("binary data is corrupt"));
894 }
895 return true;
896 }
897 return false;
898 }
899