#include <CoreFoundation/CFBase.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFNumber.h>
#include <CoreFoundation/CFDate.h>
#include <CoreFoundation/CFData.h>
#include <CoreFoundation/CFArray.h>
#include <CoreFoundation/CFDictionary.h>
#include <CoreFoundation/CFSet.h>
#include <CoreFoundation/CFPropertyList.h>
#include <CoreFoundation/CFByteOrder.h>
#include <CoreFoundation/CFRuntime.h>
#include <CoreFoundation/CFStream.h>
#include <stdio.h>
#include <limits.h>
#include <string.h>
#include "CFInternal.h"
CF_INLINE CFTypeID __CFGenericTypeID_genericobj_inline(const void *cf) {
CFTypeID typeID = __CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_info, 15, 8);
return CF_IS_OBJC(typeID, cf) ? CFGetTypeID(cf) : typeID;
}
struct __CFKeyedArchiverUID {
CFRuntimeBase _base;
uint32_t _value;
};
static CFStringRef __CFKeyedArchiverUIDCopyDescription(CFTypeRef cf) {
CFKeyedArchiverUIDRef uid = (CFKeyedArchiverUIDRef)cf;
return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<CFKeyedArchiverUID %p [%p]>{value = %u}"), cf, CFGetAllocator(cf), uid->_value);
}
static CFStringRef __CFKeyedArchiverUIDCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
CFKeyedArchiverUIDRef uid = (CFKeyedArchiverUIDRef)cf;
return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("@%u@"), uid->_value);
}
static CFTypeID __kCFKeyedArchiverUIDTypeID = _kCFRuntimeNotATypeID;
static const CFRuntimeClass __CFKeyedArchiverUIDClass = {
0,
"CFKeyedArchiverUID",
NULL, NULL, NULL, NULL, NULL, __CFKeyedArchiverUIDCopyFormattingDescription,
__CFKeyedArchiverUIDCopyDescription
};
__private_extern__ void __CFKeyedArchiverUIDInitialize(void) {
__kCFKeyedArchiverUIDTypeID = _CFRuntimeRegisterClass(&__CFKeyedArchiverUIDClass);
}
CFTypeID _CFKeyedArchiverUIDGetTypeID(void) {
return __kCFKeyedArchiverUIDTypeID;
}
CFKeyedArchiverUIDRef _CFKeyedArchiverUIDCreate(CFAllocatorRef allocator, uint32_t value) {
CFKeyedArchiverUIDRef uid;
uid = (CFKeyedArchiverUIDRef)_CFRuntimeCreateInstance(allocator, __kCFKeyedArchiverUIDTypeID, sizeof(struct __CFKeyedArchiverUID) - sizeof(CFRuntimeBase), NULL);
if (NULL == uid) {
return NULL;
}
((struct __CFKeyedArchiverUID *)uid)->_value = value;
return uid;
}
uint32_t _CFKeyedArchiverUIDGetValue(CFKeyedArchiverUIDRef uid) {
return uid->_value;
}
typedef struct {
CFTypeRef stream;
bool streamIsData;
uint64_t written;
int32_t used;
uint8_t buffer[8192 - 16];
} __CFBinaryPlistWriteBuffer;
CF_INLINE void writeBytes(__CFBinaryPlistWriteBuffer *buf, const UInt8 *bytes, CFIndex length) {
if (buf->streamIsData) {
CFDataAppendBytes((CFMutableDataRef)buf->stream, bytes, length);
} else {
CFWriteStreamWrite((CFWriteStreamRef)buf->stream, bytes, length);
}
}
static void bufferWrite(__CFBinaryPlistWriteBuffer *buf, const uint8_t *buffer, CFIndex count) {
CFIndex copyLen;
if ((CFIndex)sizeof(buf->buffer) <= count) {
writeBytes(buf, buf->buffer, buf->used);
buf->written += buf->used;
buf->used = 0;
writeBytes(buf, buffer, count);
buf->written += count;
return;
}
copyLen = __CFMin(count, (CFIndex)sizeof(buf->buffer) - buf->used);
memmove(buf->buffer + buf->used, buffer, copyLen);
buf->used += copyLen;
if (sizeof(buf->buffer) == buf->used) {
writeBytes(buf, buf->buffer, sizeof(buf->buffer));
buf->written += sizeof(buf->buffer);
memmove(buf->buffer, buffer + copyLen, count - copyLen);
buf->used = count - copyLen;
}
}
static void bufferFlush(__CFBinaryPlistWriteBuffer *buf) {
writeBytes(buf, buf->buffer, buf->used);
buf->written += buf->used;
buf->used = 0;
}
static CFTypeID stringtype = -1, datatype = -1, numbertype = -1, datetype = -1;
static CFTypeID booltype = -1, dicttype = -1, arraytype = -1;
static void _appendInt(__CFBinaryPlistWriteBuffer *buf, uint64_t bigint) {
uint8_t marker;
uint8_t *bytes;
CFIndex nbytes;
if (bigint <= (uint64_t)0xff) {
nbytes = 1;
marker = kCFBinaryPlistMarkerInt | 0;
} else if (bigint <= (uint64_t)0xffff) {
nbytes = 2;
marker = kCFBinaryPlistMarkerInt | 1;
} else if (bigint <= (uint64_t)0xffffffff) {
nbytes = 4;
marker = kCFBinaryPlistMarkerInt | 2;
} else {
nbytes = 8;
marker = kCFBinaryPlistMarkerInt | 3;
}
bigint = CFSwapInt64HostToBig(bigint);
bytes = (uint8_t *)&bigint + sizeof(bigint) - nbytes;
bufferWrite(buf, &marker, 1);
bufferWrite(buf, bytes, nbytes);
}
static void _appendUID(__CFBinaryPlistWriteBuffer *buf, CFKeyedArchiverUIDRef uid) {
uint8_t marker;
uint8_t *bytes;
CFIndex nbytes;
uint64_t bigint = _CFKeyedArchiverUIDGetValue(uid);
if (bigint <= (uint64_t)0xff) {
nbytes = 1;
} else if (bigint <= (uint64_t)0xffff) {
nbytes = 2;
} else if (bigint <= (uint64_t)0xffffffff) {
nbytes = 4;
} else {
nbytes = 8;
}
marker = kCFBinaryPlistMarkerUID | (nbytes - 1);
bigint = CFSwapInt64HostToBig(bigint);
bytes = (uint8_t *)&bigint + sizeof(bigint) - nbytes;
bufferWrite(buf, &marker, 1);
bufferWrite(buf, bytes, nbytes);
}
static Boolean __plistUniquingEqual(CFTypeRef cf1, CFTypeRef cf2) {
if (__CFGenericTypeID_genericobj_inline(cf1) != __CFGenericTypeID_genericobj_inline(cf2)) return false;
if (__CFGenericTypeID_genericobj_inline(cf1) == numbertype) {
if (CFNumberIsFloatType(cf1) != CFNumberIsFloatType(cf2)) return false;
return CFEqual(cf1, cf2);
}
return CFEqual(cf1, cf2);
}
static void _flattenPlist(CFPropertyListRef plist, CFMutableArrayRef objlist, CFMutableDictionaryRef objtable, CFMutableSetRef uniquingsets[]) {
CFPropertyListRef unique;
uint32_t refnum;
CFTypeID type = __CFGenericTypeID_genericobj_inline(plist);
CFIndex idx;
CFPropertyListRef *list, buffer[256];
int which = -1;
if (stringtype == type) {
which = 0;
} else if (numbertype == type) {
which = 1;
} else if (datatype == type) {
which = 2;
} else if (datetype == type) {
which = 3;
}
if (1 && -1 != which) {
CFMutableSetRef uniquingset = uniquingsets[which];
CFIndex before = CFSetGetCount(uniquingset);
CFSetAddValue(uniquingset, plist);
CFIndex after = CFSetGetCount(uniquingset);
if (after == before) { unique = CFSetGetValue(uniquingset, plist);
if (unique != plist) {
refnum = (uint32_t)CFDictionaryGetValue(objtable, unique);
CFDictionaryAddValue(objtable, plist, (const void *)refnum);
}
return;
}
}
refnum = CFArrayGetCount(objlist);
CFArrayAppendValue(objlist, plist);
CFDictionaryAddValue(objtable, plist, (const void *)refnum);
if (dicttype == type) {
CFIndex count = CFDictionaryGetCount(plist);
list = (count <= 128) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, 2 * count * sizeof(CFTypeRef), 0);
CFDictionaryGetKeysAndValues(plist, list, list + count);
for (idx = 0; idx < 2 * count; idx++) {
_flattenPlist(list[idx], objlist, objtable, uniquingsets);
}
if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
} else if (arraytype == type) {
CFIndex count = CFArrayGetCount(plist);
list = (count <= 256) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, count * sizeof(CFTypeRef), 0);
CFArrayGetValues(plist, CFRangeMake(0, count), list);
for (idx = 0; idx < count; idx++) {
_flattenPlist(list[idx], objlist, objtable, uniquingsets);
}
if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
}
}
CFIndex __CFBinaryPlistWriteToStream(CFPropertyListRef plist, CFTypeRef stream) {
CFMutableDictionaryRef objtable;
CFMutableArrayRef objlist;
CFBinaryPlistTrailer trailer;
uint64_t *offsets, length_so_far;
uint64_t mask, refnum;
int64_t idx, idx2, cnt;
__CFBinaryPlistWriteBuffer *buf;
CFSetCallBacks cb = kCFTypeSetCallBacks;
if ((CFTypeID)-1 == stringtype) {
stringtype = CFStringGetTypeID();
datatype = CFDataGetTypeID();
numbertype = CFNumberGetTypeID();
booltype = CFBooleanGetTypeID();
datetype = CFDateGetTypeID();
dicttype = CFDictionaryGetTypeID();
arraytype = CFArrayGetTypeID();
}
objtable = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL);
_CFDictionarySetCapacity(objtable, 640);
objlist = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, NULL);
_CFArraySetCapacity(objlist, 640);
cb.equal = __plistUniquingEqual;
CFMutableSetRef uniquingsets[4];
uniquingsets[0] = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &cb);
_CFSetSetCapacity(uniquingsets[0], 1000);
uniquingsets[1] = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &cb);
_CFSetSetCapacity(uniquingsets[1], 500);
uniquingsets[2] = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &cb);
_CFSetSetCapacity(uniquingsets[2], 250);
uniquingsets[3] = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &cb);
_CFSetSetCapacity(uniquingsets[3], 250);
_flattenPlist(plist, objlist, objtable, uniquingsets);
CFRelease(uniquingsets[0]);
CFRelease(uniquingsets[1]);
CFRelease(uniquingsets[2]);
CFRelease(uniquingsets[3]);
cnt = CFArrayGetCount(objlist);
offsets = CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(*offsets), 0);
buf = CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(__CFBinaryPlistWriteBuffer), 0);
buf->stream = stream;
buf->streamIsData = (CFGetTypeID(stream) == CFDataGetTypeID());
buf->written = 0;
buf->used = 0;
bufferWrite(buf, "bplist00", 8);
memset(&trailer, 0, sizeof(trailer));
trailer._numObjects = CFSwapInt64HostToBig(cnt);
trailer._topObject = 0; mask = ~(uint64_t)0;
while (cnt & mask) {
trailer._objectRefSize++;
mask = mask << 8;
}
for (idx = 0; idx < cnt; idx++) {
CFPropertyListRef obj = CFArrayGetValueAtIndex(objlist, idx);
CFTypeID type = CFGetTypeID(obj);
offsets[idx] = buf->written + buf->used;
if (stringtype == type) {
CFIndex ret, count = CFStringGetLength(obj);
CFIndex needed;
uint8_t *bytes, buffer[1024];
bytes = (count <= 1024) ? buffer : CFAllocatorAllocate(kCFAllocatorDefault, count, 0);
ret = CFStringGetBytes(obj, CFRangeMake(0, count), kCFStringEncodingASCII, 0, false, bytes, count, &needed);
if (ret == count) {
uint8_t marker = kCFBinaryPlistMarkerASCIIString | (needed < 15 ? needed : 0xf);
bufferWrite(buf, &marker, 1);
if (15 <= needed) {
_appendInt(buf, (uint64_t)needed);
}
bufferWrite(buf, bytes, needed);
} else {
UniChar *chars;
uint8_t marker = kCFBinaryPlistMarkerUnicode16String | (count < 15 ? count : 0xf);
bufferWrite(buf, &marker, 1);
if (15 <= count) {
_appendInt(buf, (uint64_t)count);
}
chars = CFAllocatorAllocate(kCFAllocatorDefault, count * sizeof(UniChar), 0);
CFStringGetCharacters(obj, CFRangeMake(0, count), chars);
for (idx2 = 0; idx2 < count; idx2++) {
chars[idx2] = CFSwapInt16HostToBig(chars[idx2]);
}
bufferWrite(buf, (uint8_t *)chars, count * sizeof(UniChar));
CFAllocatorDeallocate(kCFAllocatorDefault, chars);
}
if (bytes != buffer) CFAllocatorDeallocate(kCFAllocatorDefault, bytes);
} else if (numbertype == type) {
uint8_t marker;
CFSwappedFloat64 swapped64;
CFSwappedFloat32 swapped32;
uint64_t bigint;
uint8_t *bytes;
CFIndex nbytes;
if (CFNumberIsFloatType(obj)) {
if (CFNumberGetByteSize(obj) <= (CFIndex)sizeof(float)) {
float v;
CFNumberGetValue(obj, kCFNumberFloat32Type, &v);
swapped32 = CFConvertFloat32HostToSwapped(v);
bytes = (uint8_t *)&swapped32;
nbytes = sizeof(float);
marker = kCFBinaryPlistMarkerReal | 2;
} else {
double v;
CFNumberGetValue(obj, kCFNumberFloat64Type, &v);
swapped64 = CFConvertFloat64HostToSwapped(v);
bytes = (uint8_t *)&swapped64;
nbytes = sizeof(double);
marker = kCFBinaryPlistMarkerReal | 3;
}
bufferWrite(buf, &marker, 1);
bufferWrite(buf, bytes, nbytes);
} else {
CFNumberGetValue(obj, kCFNumberSInt64Type, &bigint);
_appendInt(buf, bigint);
}
} else if (_CFKeyedArchiverUIDGetTypeID() == type) {
_appendUID(buf, (CFKeyedArchiverUIDRef)obj);
} else if (booltype == type) {
uint8_t marker = CFBooleanGetValue(obj) ? kCFBinaryPlistMarkerTrue : kCFBinaryPlistMarkerFalse;
bufferWrite(buf, &marker, 1);
} else if (datatype == type) {
CFIndex count = CFDataGetLength(obj);
uint8_t marker = kCFBinaryPlistMarkerData | (count < 15 ? count : 0xf);
bufferWrite(buf, &marker, 1);
if (15 <= count) {
_appendInt(buf, (uint64_t)count);
}
bufferWrite(buf, CFDataGetBytePtr(obj), count);
} else if (datetype == type) {
CFSwappedFloat64 swapped;
uint8_t marker = kCFBinaryPlistMarkerDate;
bufferWrite(buf, &marker, 1);
swapped = CFConvertFloat64HostToSwapped(CFDateGetAbsoluteTime(obj));
bufferWrite(buf, (uint8_t *)&swapped, sizeof(swapped));
} else if (dicttype == type) {
CFIndex count = CFDictionaryGetCount(obj);
CFPropertyListRef *list, buffer[512];
uint8_t marker = kCFBinaryPlistMarkerDict | (count < 15 ? count : 0xf);
bufferWrite(buf, &marker, 1);
if (15 <= count) {
_appendInt(buf, (uint64_t)count);
}
list = (count <= 256) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, 2 * count * sizeof(CFTypeRef), 0);
CFDictionaryGetKeysAndValues(obj, list, list + count);
for (idx2 = 0; idx2 < 2 * count; idx2++) {
CFPropertyListRef value = list[idx2];
uint32_t swapped = 0;
uint8_t *source = (uint8_t *)&swapped;
refnum = (uint32_t)CFDictionaryGetValue(objtable, value);
swapped = CFSwapInt32HostToBig(refnum);
bufferWrite(buf, source + sizeof(swapped) - trailer._objectRefSize, trailer._objectRefSize);
}
if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
} else if (arraytype == type) {
CFIndex count = CFArrayGetCount(obj);
CFPropertyListRef *list, buffer[256];
uint8_t marker = kCFBinaryPlistMarkerArray | (count < 15 ? count : 0xf);
bufferWrite(buf, &marker, 1);
if (15 <= count) {
_appendInt(buf, (uint64_t)count);
}
list = (count <= 256) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, count * sizeof(CFTypeRef), 0);
CFArrayGetValues(obj, CFRangeMake(0, count), list);
for (idx2 = 0; idx2 < count; idx2++) {
CFPropertyListRef value = list[idx2];
uint32_t swapped = 0;
uint8_t *source = (uint8_t *)&swapped;
refnum = (uint32_t)CFDictionaryGetValue(objtable, value);
swapped = CFSwapInt32HostToBig(refnum);
bufferWrite(buf, source + sizeof(swapped) - trailer._objectRefSize, trailer._objectRefSize);
}
if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
} else {
CFRelease(objtable);
CFRelease(objlist);
CFAllocatorDeallocate(kCFAllocatorSystemDefault, buf);
CFAllocatorDeallocate(kCFAllocatorSystemDefault, offsets);
return 0;
}
}
CFRelease(objtable);
CFRelease(objlist);
length_so_far = buf->written + buf->used;
trailer._offsetTableOffset = CFSwapInt64HostToBig(length_so_far);
trailer._offsetIntSize = 0;
mask = ~(uint64_t)0;
while (length_so_far & mask) {
trailer._offsetIntSize++;
mask = mask << 8;
}
for (idx = 0; idx < cnt; idx++) {
uint64_t swapped = CFSwapInt64HostToBig(offsets[idx]);
uint8_t *source = (uint8_t *)&swapped;
bufferWrite(buf, source + sizeof(*offsets) - trailer._offsetIntSize, trailer._offsetIntSize);
}
length_so_far += cnt * trailer._offsetIntSize;
bufferWrite(buf, (uint8_t *)&trailer, sizeof(trailer));
bufferFlush(buf);
length_so_far += sizeof(trailer);
CFAllocatorDeallocate(kCFAllocatorSystemDefault, buf);
CFAllocatorDeallocate(kCFAllocatorSystemDefault, offsets);
return (CFIndex)length_so_far;
}
bool __CFBinaryPlistGetTopLevelInfo(const uint8_t *databytes, uint64_t datalen, uint8_t *marker, uint64_t *offset, CFBinaryPlistTrailer *trailer) {
const uint8_t *bytesptr;
CFBinaryPlistTrailer trail;
uint64_t off;
CFIndex idx;
if ((CFTypeID)-1 == stringtype) {
stringtype = CFStringGetTypeID();
datatype = CFDataGetTypeID();
numbertype = CFNumberGetTypeID();
booltype = CFBooleanGetTypeID();
datetype = CFDateGetTypeID();
dicttype = CFDictionaryGetTypeID();
arraytype = CFArrayGetTypeID();
}
if (!databytes || datalen < 8 || 0 != memcmp("bplist00", databytes, 8)) return false;
if (datalen < sizeof(trail) + 8 + 1) return false;
memmove(&trail, databytes + datalen - sizeof(trail), sizeof(trail));
trail._numObjects = CFSwapInt64BigToHost(trail._numObjects);
trail._topObject = CFSwapInt64BigToHost(trail._topObject);
if (trail._numObjects < trail._topObject) return false;
trail._offsetTableOffset = CFSwapInt64BigToHost(trail._offsetTableOffset);
if (datalen < trail._offsetTableOffset + trail._numObjects * trail._offsetIntSize + sizeof(trail)) return false;
bytesptr = databytes + trail._offsetTableOffset + trail._topObject * trail._offsetIntSize;
off = 0;
for (idx = 0; idx < trail._offsetIntSize; idx++) {
off = (off << 8) + bytesptr[idx];
}
if (trail._offsetTableOffset <= off) return false;
if (trailer) *trailer = trail;
if (offset) *offset = off;
if (marker) *marker = *(databytes + off);
return true;
}
static bool _readInt(const uint8_t *ptr, uint64_t *bigint, const uint8_t **newptr) {
uint8_t marker;
CFIndex idx, cnt;
marker = *ptr++;
if ((marker & 0xf0) != kCFBinaryPlistMarkerInt) return false;
cnt = 1 << (marker & 0xf);
*bigint = 0;
for (idx = 0; idx < cnt; idx++) {
*bigint = (*bigint << 8) + *ptr++;
}
if (newptr) *newptr = ptr;
return true;
}
static uint64_t _getOffsetOfRefAt(const uint8_t *databytes, const uint8_t *bytesptr, const CFBinaryPlistTrailer *trailer) {
uint64_t ref = 0, off = 0;
CFIndex idx;
for (idx = 0; idx < trailer->_objectRefSize; idx++) {
ref = (ref << 8) + bytesptr[idx];
}
bytesptr = databytes + trailer->_offsetTableOffset + ref * trailer->_offsetIntSize;
for (idx = 0; idx < trailer->_offsetIntSize; idx++) {
off = (off << 8) + bytesptr[idx];
}
return off;
}
bool __CFBinaryPlistGetOffsetForValueFromArray(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFIndex idx, uint64_t *offset) {
const uint8_t *bytesptr;
uint8_t marker;
CFIndex cnt;
uint64_t off;
marker = *(databytes + startOffset);
if ((marker & 0xf0) != kCFBinaryPlistMarkerArray) return false;
cnt = (marker & 0x0f);
if (cnt < 15 && cnt <= idx) return false;
bytesptr = databytes + startOffset + 1;
if (0xf == cnt) {
uint64_t bigint;
if (!_readInt(bytesptr, &bigint, &bytesptr)) return false;
if (INT_MAX < bigint) return false;
cnt = (CFIndex)bigint;
}
if (cnt <= idx) return false;
off = _getOffsetOfRefAt(databytes, bytesptr + idx * trailer->_objectRefSize, trailer);
if (datalen <= off) return false;
if (offset) *offset = off;
return true;
}
bool __CFBinaryPlistGetOffsetForValueFromDictionary(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFTypeRef key, uint64_t *koffset, uint64_t *voffset) {
const uint8_t *refsptr, *bytesptr;
uint64_t off;
uint8_t marker;
CFTypeID keytype = CFGetTypeID(key);
CFIndex idx, keyn, cnt, cnt2;
marker = *(databytes + startOffset);
if ((marker & 0xf0) != kCFBinaryPlistMarkerDict) return false;
cnt = (marker & 0x0f);
refsptr = databytes + startOffset + 1 + 0;
if (0xf == cnt) {
uint64_t bigint;
if (!_readInt(refsptr, &bigint, &refsptr)) return false;
if (INT_MAX < bigint) return false;
cnt = (CFIndex)bigint;
}
for (keyn = 0; keyn < cnt; keyn++) {
off = _getOffsetOfRefAt(databytes, refsptr, trailer);
if (datalen <= off) return false;
refsptr += trailer->_objectRefSize;
bytesptr = databytes + off;
marker = *bytesptr & 0xf0;
cnt2 = *bytesptr & 0x0f;
if (kCFBinaryPlistMarkerASCIIString == marker || kCFBinaryPlistMarkerUnicode16String == marker) {
CFStringInlineBuffer strbuf;
UniChar uchar;
if (keytype != stringtype) goto miss;
if (0xf == cnt2 && CFStringGetLength(key) < 15) goto miss;
bytesptr++;
if (0xf == cnt2) {
uint64_t bigint;
if (!_readInt(bytesptr, &bigint, &bytesptr)) return false;
if (INT_MAX < bigint) return false;
cnt2 = (CFIndex)bigint;
}
if (cnt2 != CFStringGetLength(key)) goto miss;
uchar = (kCFBinaryPlistMarkerASCIIString == marker) ? (UniChar)bytesptr[0] : (UniChar)(bytesptr[0] * 256 + bytesptr[1]);
if (uchar != CFStringGetCharacterAtIndex(key, 0)) goto miss;
bytesptr += (kCFBinaryPlistMarkerASCIIString == marker) ? 1 : 2;
CFStringInitInlineBuffer(key, &strbuf, CFRangeMake(0, cnt2));
for (idx = 1; idx < cnt2; idx++) {
uchar = (kCFBinaryPlistMarkerASCIIString == marker) ? (UniChar)bytesptr[0] : (UniChar)(bytesptr[0] * 256 + bytesptr[1]);
if (uchar != __CFStringGetCharacterFromInlineBufferQuick(&strbuf, idx)) goto miss;
bytesptr += (kCFBinaryPlistMarkerASCIIString == marker) ? 1 : 2;
}
if (koffset) *koffset = off;
off = _getOffsetOfRefAt(databytes, refsptr + (cnt - 1) * trailer->_objectRefSize, trailer);
if (datalen <= off) return false;
if (voffset) *voffset = off;
return true;
} else {
return false;
}
miss: ;
}
return false;
}
extern CFArrayRef _CFArrayCreate_ex(CFAllocatorRef allocator, bool mutable, const void **values, CFIndex numValues);
extern CFDictionaryRef _CFDictionaryCreate_ex(CFAllocatorRef allocator, bool mutable, const void **keys, const void **values, CFIndex numValues);
#if 0
static bool _getUIDFromData(const uint8_t *datap, uint64_t *vp) {
int32_t idx, cnt;
uint8_t marker = *datap;
uint64_t bigint;
if ((marker & 0xf0) != kCFBinaryPlistMarkerUID) return false;
cnt = (marker & 0x0f) + 1;
datap++;
bigint = 0;
for (idx = 0; idx < cnt; idx++) {
bigint = (bigint << 8) + *datap++;
}
*vp = bigint;
return true;
}
#endif
static bool _getFloatFromData(const uint8_t *datap, float *vp) {
CFSwappedFloat32 swapped32;
if (*datap != (kCFBinaryPlistMarkerReal | 2)) return false;
datap++;
memmove(&swapped32, datap, sizeof(swapped32));
*vp = CFConvertFloat32SwappedToHost(swapped32);
return true;
}
static bool _getDoubleFromData(const uint8_t *datap, double *vp) {
CFSwappedFloat64 swapped64;
if (*datap != (kCFBinaryPlistMarkerReal | 3)) return false;
datap++;
memmove(&swapped64, datap, sizeof(swapped64));
*vp = CFConvertFloat64SwappedToHost(swapped64);
return true;
}
bool __CFBinaryPlistCreateObject(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFAllocatorRef allocator, CFOptionFlags mutabilityOption, CFMutableDictionaryRef objects, CFPropertyListRef *plist) {
const uint8_t *bytesptr;
uint64_t off;
uint8_t marker;
CFIndex idx, cnt;
uint64_t bigint;
UniChar *chars;
CFPropertyListRef *list, buffer[256];
CFAllocatorRef listAllocator;
if (objects) {
*plist = CFDictionaryGetValue(objects, (const void *)(intptr_t)startOffset);
if (*plist) {
CFRetain(*plist);
return true;
}
}
marker = *(databytes + startOffset);
switch (marker & 0xf0) {
case kCFBinaryPlistMarkerNull:
switch (marker) {
case kCFBinaryPlistMarkerNull:
*plist = NULL;
return true;
case kCFBinaryPlistMarkerFalse:
*plist = CFRetain(kCFBooleanFalse);
return true;
case kCFBinaryPlistMarkerTrue:
*plist = CFRetain(kCFBooleanTrue);
return true;
}
return false;
case kCFBinaryPlistMarkerInt:
if (!_readInt(databytes + startOffset, &bigint, NULL)) return false;
*plist = CFNumberCreate(allocator, kCFNumberSInt64Type, &bigint);
if (objects) CFDictionarySetValue(objects, (const void *)(intptr_t)startOffset, *plist);
return (*plist) ? true : false;
case kCFBinaryPlistMarkerReal:
cnt = marker & 0x0f;
if (2 == cnt) {
float f;
_getFloatFromData(databytes + startOffset, &f);
*plist = CFNumberCreate(allocator, kCFNumberFloat32Type, &f);
if (objects) CFDictionarySetValue(objects, (const void *)(intptr_t)startOffset, *plist);
return (*plist) ? true : false;
} else if (3 == cnt) {
double d;
_getDoubleFromData(databytes + startOffset, &d);
*plist = CFNumberCreate(allocator, kCFNumberFloat64Type, &d);
if (objects) CFDictionarySetValue(objects, (const void *)(intptr_t)startOffset, *plist);
return (*plist) ? true : false;
}
return false;
case kCFBinaryPlistMarkerDate & 0xf0: {
CFSwappedFloat64 swapped64;
double d;
cnt = marker & 0x0f;
if (3 != cnt) return false;
memmove(&swapped64, databytes + startOffset + 1, sizeof(swapped64));
d = CFConvertFloat64SwappedToHost(swapped64);
*plist = CFDateCreate(allocator, d);
if (objects) CFDictionarySetValue(objects, (const void *)(intptr_t)startOffset, *plist);
return (*plist) ? true : false;
}
case kCFBinaryPlistMarkerData:
cnt = marker & 0x0f;
bytesptr = databytes + startOffset + 1;
if (0xf == cnt) {
if (!_readInt(bytesptr, &bigint, &bytesptr)) return false;
if (INT_MAX < bigint) return false;
cnt = (CFIndex)bigint;
}
if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) {
*plist = CFDataCreateMutable(allocator, 0);
CFDataAppendBytes((CFMutableDataRef)*plist, bytesptr, cnt);
} else {
*plist = CFDataCreate(allocator, bytesptr, cnt);
}
if (objects) CFDictionarySetValue(objects, (const void *)(intptr_t)startOffset, *plist);
return (*plist) ? true : false;
case kCFBinaryPlistMarkerASCIIString:
cnt = marker & 0x0f;
bytesptr = databytes + startOffset + 1;
if (0xf == cnt) {
if (!_readInt(bytesptr, &bigint, &bytesptr)) return false;
if (INT_MAX < bigint) return false;
cnt = (CFIndex)bigint;
}
if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) {
CFStringRef str = CFStringCreateWithBytes(allocator, bytesptr, cnt, kCFStringEncodingASCII, false);
*plist = CFStringCreateMutableCopy(allocator, 0, str);
CFRelease(str);
} else {
*plist = CFStringCreateWithBytes(allocator, bytesptr, cnt, kCFStringEncodingASCII, false);
}
if (objects) CFDictionarySetValue(objects, (const void *)(intptr_t)startOffset, *plist);
return (*plist) ? true : false;
case kCFBinaryPlistMarkerUnicode16String:
cnt = marker & 0x0f;
bytesptr = databytes + startOffset + 1;
if (0xf == cnt) {
if (!_readInt(bytesptr, &bigint, &bytesptr)) return false;
if (INT_MAX < bigint) return false;
cnt = (CFIndex)bigint;
}
chars = CFAllocatorAllocate(allocator, cnt * sizeof(UniChar), 0);
memmove(chars, bytesptr, cnt * sizeof(UniChar));
for (idx = 0; idx < cnt; idx++) {
chars[idx] = CFSwapInt16BigToHost(chars[idx]);
}
if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) {
CFStringRef str = CFStringCreateWithCharactersNoCopy(allocator, chars, cnt, allocator);
*plist = CFStringCreateMutableCopy(allocator, 0, str);
CFRelease(str);
} else {
*plist = CFStringCreateWithCharactersNoCopy(allocator, chars, cnt, allocator);
}
if (objects) CFDictionarySetValue(objects, (const void *)(intptr_t)startOffset, *plist);
return (*plist) ? true : false;
case kCFBinaryPlistMarkerUID:
cnt = (marker & 0x0f) + 1;
bytesptr = databytes + startOffset + 1;
bigint = 0;
for (idx = 0; idx < cnt; idx++) {
bigint = (bigint << 8) + *bytesptr++;
}
if (UINT_MAX < bigint) return false;
*plist = _CFKeyedArchiverUIDCreate(allocator, (uint32_t)bigint);
if (objects) CFDictionarySetValue(objects, (const void *)(intptr_t)startOffset, *plist);
return (*plist) ? true : false;
case kCFBinaryPlistMarkerArray:
cnt = marker & 0x0f;
bytesptr = databytes + startOffset + 1;
if (0xf == cnt) {
if (!_readInt(bytesptr, &bigint, &bytesptr)) return false;
if (INT_MAX < bigint) return false;
cnt = (CFIndex)bigint;
}
list = (cnt <= 256) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(CFPropertyListRef) * cnt, 0);
listAllocator = (list == buffer ? kCFAllocatorNull : kCFAllocatorSystemDefault);
for (idx = 0; idx < cnt; idx++) {
CFPropertyListRef pl;
off = _getOffsetOfRefAt(databytes, bytesptr, trailer);
if (datalen <= off) return false;
if (!__CFBinaryPlistCreateObject(databytes, datalen, off, trailer, allocator, mutabilityOption, objects, &pl)) {
if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator)) {
while (idx--) {
CFRelease(list[idx]);
}
}
if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
return false;
}
if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) {
CF_WRITE_BARRIER_BASE_ASSIGN(listAllocator, list, list[idx], CFMakeCollectable(pl));
} else {
list[idx] = pl;
}
bytesptr += trailer->_objectRefSize;
}
*plist = _CFArrayCreate_ex(allocator, (mutabilityOption != kCFPropertyListImmutable), list, cnt);
if (objects) CFDictionarySetValue(objects, (const void *)(intptr_t)startOffset, *plist);
if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
return (*plist) ? true : false;
case kCFBinaryPlistMarkerDict:
cnt = marker & 0x0f;
bytesptr = databytes + startOffset + 1;
if (0xf == cnt) {
if (!_readInt(bytesptr, &bigint, &bytesptr)) return false;
if (INT_MAX < bigint) return false;
cnt = (CFIndex)bigint;
}
cnt *= 2;
list = (cnt <= 256) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(CFPropertyListRef) * cnt, 0);
listAllocator = (list == buffer ? kCFAllocatorNull : kCFAllocatorSystemDefault);
for (idx = 0; idx < cnt; idx++) {
CFPropertyListRef pl;
off = _getOffsetOfRefAt(databytes, bytesptr, trailer);
if (datalen <= off) return false;
if (!__CFBinaryPlistCreateObject(databytes, datalen, off, trailer, allocator, mutabilityOption, objects, &pl)) {
if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator)) {
while (idx--) {
CFRelease(list[idx]);
}
}
if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
return false;
}
if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) {
CF_WRITE_BARRIER_BASE_ASSIGN(listAllocator, list, list[idx], CFMakeCollectable(pl));
} else {
list[idx] = pl;
}
bytesptr += trailer->_objectRefSize;
}
*plist = _CFDictionaryCreate_ex(allocator, (mutabilityOption != kCFPropertyListImmutable), list, list + cnt / 2, cnt / 2);
if (objects) CFDictionarySetValue(objects, (const void *)(intptr_t)startOffset, *plist);
if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
return (*plist) ? true : false;
}
return false;
}
__private_extern__ bool __CFTryParseBinaryPlist(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags option, CFPropertyListRef *plist, CFStringRef *errorString) {
uint8_t marker;
CFBinaryPlistTrailer trailer;
uint64_t offset;
CFPropertyListRef pl;
const uint8_t *databytes = CFDataGetBytePtr(data);
uint64_t datalen = CFDataGetLength(data);
if (8 <= datalen && __CFBinaryPlistGetTopLevelInfo(databytes, datalen, &marker, &offset, &trailer)) {
if (__CFBinaryPlistCreateObject(databytes, datalen, offset, &trailer, allocator, option, NULL, &pl)) {
if (plist) *plist = pl;
} else {
if (plist) *plist = NULL;
if (errorString) *errorString = CFRetain(CFSTR("binary data is corrupt"));
}
return true;
}
return false;
}