| /* |
| * Copyright (C) 2009 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "BinaryPropertyList.h" |
| |
| #include <limits> |
| #include <wtf/HashMap.h> |
| #include <wtf/text/StringHash.h> |
| |
| static const size_t headerSize = 8; |
| static const size_t trailerSize = 32; |
| |
| static const UInt8 booleanTrueMarkerByte = 0x09; |
| static const UInt8 oneByteIntegerMarkerByte = 0x10; |
| static const UInt8 twoByteIntegerMarkerByte = 0x11; |
| static const UInt8 fourByteIntegerMarkerByte = 0x12; |
| #ifdef __LP64__ |
| static const UInt8 eightByteIntegerMarkerByte = 0x13; |
| #endif |
| |
| static const UInt8 asciiStringMarkerByte = 0x50; |
| static const UInt8 asciiStringWithSeparateLengthMarkerByte = 0x5F; |
| static const UInt8 unicodeStringMarkerByte = 0x60; |
| static const UInt8 unicodeStringWithSeparateLengthMarkerByte = 0x6F; |
| static const UInt8 arrayMarkerByte = 0xA0; |
| static const UInt8 arrayWithSeparateLengthMarkerByte = 0xAF; |
| static const UInt8 dictionaryMarkerByte = 0xD0; |
| static const UInt8 dictionaryWithSeparateLengthMarkerByte = 0xDF; |
| static const size_t maxLengthInMarkerByte = 0xE; |
| |
| class IntegerArray { |
| public: |
| IntegerArray() : m_integers(0), m_size(0) { } |
| IntegerArray(const int* integers, size_t size) : m_integers(integers), m_size(size) { ASSERT(integers); ASSERT(size); } |
| |
| void markDeleted() { m_integers = 0; m_size = deletedValueSize(); } |
| bool isDeletedValue() const { return m_size == deletedValueSize(); } |
| |
| const int* integers() const { ASSERT(!isDeletedValue()); return m_integers; } |
| size_t size() const { ASSERT(!isDeletedValue()); return m_size; } |
| |
| private: |
| static size_t deletedValueSize() { return std::numeric_limits<size_t>::max(); } |
| |
| friend bool operator==(const IntegerArray&, const IntegerArray&); |
| |
| const int* m_integers; |
| size_t m_size; |
| }; |
| |
| inline bool operator==(const IntegerArray& a, const IntegerArray& b) |
| { |
| return a.m_integers == b.m_integers && a.m_size == b.m_size; |
| } |
| |
| struct IntegerArrayHashTraits : WTF::GenericHashTraits<IntegerArray> { |
| static const bool needsDestruction = false; |
| static void constructDeletedValue(IntegerArray& slot) { slot.markDeleted(); } |
| static bool isDeletedValue(const IntegerArray& array) { return array.isDeletedValue(); } |
| }; |
| |
| struct IntegerArrayHash { |
| static unsigned hash(const IntegerArray&); |
| static bool equal(const IntegerArray&, const IntegerArray&); |
| static const bool safeToCompareToEmptyOrDeleted = true; |
| }; |
| |
| unsigned IntegerArrayHash::hash(const IntegerArray& array) |
| { |
| return StringHasher::hashMemory(array.integers(), array.size() * sizeof(int)); |
| } |
| |
| bool IntegerArrayHash::equal(const IntegerArray& a, const IntegerArray& b) |
| { |
| if (a.isDeletedValue() || b.isDeletedValue()) |
| return a.isDeletedValue() == b.isDeletedValue(); |
| if (a.size() != b.size()) |
| return false; |
| for (size_t i = 0; i < a.size(); ++i) { |
| if (a.integers()[i] != b.integers()[i]) |
| return false; |
| } |
| return true; |
| } |
| |
| typedef size_t ObjectReference; |
| |
| class BinaryPropertyListPlan : private BinaryPropertyListObjectStream { |
| public: |
| BinaryPropertyListPlan(BinaryPropertyListWriter&); |
| |
| ObjectReference booleanTrueObjectReference() const; |
| ObjectReference integerObjectReference(int) const; |
| ObjectReference stringObjectReference(const String&) const; |
| ObjectReference integerArrayObjectReference(const int*, size_t) const; |
| |
| ObjectReference objectCount() const { return m_currentObjectReference; } |
| |
| ObjectReference byteCount() const { return m_byteCount; } |
| ObjectReference objectReferenceCount() const { return m_objectReferenceCount; } |
| |
| private: |
| virtual void writeBooleanTrue(); |
| virtual void writeInteger(int); |
| virtual void writeString(const String&); |
| virtual void writeIntegerArray(const int*, size_t); |
| virtual void writeUniqueString(const String&); |
| virtual void writeUniqueString(const char*); |
| virtual size_t writeArrayStart(); |
| virtual void writeArrayEnd(size_t); |
| virtual size_t writeDictionaryStart(); |
| virtual void writeDictionaryEnd(size_t); |
| |
| void writeArrayObject(size_t); |
| void writeDictionaryObject(size_t); |
| void writeStringObject(const String&); |
| void writeStringObject(const char*); |
| |
| static ObjectReference invalidObjectReference() { return std::numeric_limits<ObjectReference>::max(); } |
| |
| typedef HashMap<IntegerArray, ObjectReference, IntegerArrayHash, IntegerArrayHashTraits> IntegerArrayMap; |
| |
| ObjectReference m_booleanTrueObjectReference; |
| ObjectReference m_integerZeroObjectReference; |
| HashMap<int, ObjectReference> m_integers; |
| HashMap<String, ObjectReference> m_strings; |
| IntegerArrayMap m_integerArrays; |
| |
| ObjectReference m_currentObjectReference; |
| |
| size_t m_currentAggregateSize; |
| |
| size_t m_byteCount; |
| size_t m_objectReferenceCount; |
| }; |
| |
| BinaryPropertyListPlan::BinaryPropertyListPlan(BinaryPropertyListWriter& client) |
| : m_booleanTrueObjectReference(invalidObjectReference()) |
| , m_integerZeroObjectReference(invalidObjectReference()) |
| , m_currentObjectReference(0) |
| , m_currentAggregateSize(0) |
| , m_byteCount(0) |
| , m_objectReferenceCount(0) |
| { |
| client.writeObjects(*this); |
| ASSERT(m_currentAggregateSize == 1); |
| } |
| |
| void BinaryPropertyListPlan::writeBooleanTrue() |
| { |
| ++m_currentAggregateSize; |
| if (m_booleanTrueObjectReference != invalidObjectReference()) |
| return; |
| m_booleanTrueObjectReference = m_currentObjectReference++; |
| ++m_byteCount; |
| } |
| |
| static inline int integerByteCount(size_t integer) |
| { |
| if (integer <= 0xFF) |
| return 2; |
| if (integer <= 0xFFFF) |
| return 3; |
| #ifdef __LP64__ |
| if (integer <= 0xFFFFFFFFULL) |
| return 5; |
| return 9; |
| #else |
| return 5; |
| #endif |
| } |
| |
| void BinaryPropertyListPlan::writeInteger(int integer) |
| { |
| ASSERT(integer >= 0); |
| ++m_currentAggregateSize; |
| if (!integer) { |
| if (m_integerZeroObjectReference != invalidObjectReference()) |
| return; |
| m_integerZeroObjectReference = m_currentObjectReference; |
| } else { |
| if (!m_integers.add(integer, m_currentObjectReference).isNewEntry) |
| return; |
| } |
| ++m_currentObjectReference; |
| m_byteCount += integerByteCount(integer); |
| } |
| |
| void BinaryPropertyListPlan::writeString(const String& string) |
| { |
| ++m_currentAggregateSize; |
| if (!m_strings.add(string, m_currentObjectReference).isNewEntry) |
| return; |
| ++m_currentObjectReference; |
| writeStringObject(string); |
| } |
| |
| void BinaryPropertyListPlan::writeIntegerArray(const int* integers, size_t size) |
| { |
| size_t savedAggregateSize = ++m_currentAggregateSize; |
| ASSERT(size); |
| IntegerArrayMap::AddResult addResult = m_integerArrays.add(IntegerArray(integers, size), 0); |
| if (!addResult.isNewEntry) |
| return; |
| for (size_t i = 0; i < size; ++i) |
| writeInteger(integers[i]); |
| addResult.iterator->value = m_currentObjectReference++; |
| writeArrayObject(size); |
| m_currentAggregateSize = savedAggregateSize; |
| } |
| |
| void BinaryPropertyListPlan::writeUniqueString(const String& string) |
| { |
| ++m_currentAggregateSize; |
| ++m_currentObjectReference; |
| writeStringObject(string); |
| } |
| |
| void BinaryPropertyListPlan::writeUniqueString(const char* string) |
| { |
| ++m_currentAggregateSize; |
| ++m_currentObjectReference; |
| writeStringObject(string); |
| } |
| |
| size_t BinaryPropertyListPlan::writeArrayStart() |
| { |
| size_t savedAggregateSize = m_currentAggregateSize; |
| m_currentAggregateSize = 0; |
| return savedAggregateSize; |
| } |
| |
| void BinaryPropertyListPlan::writeArrayEnd(size_t savedAggregateSize) |
| { |
| ++m_currentObjectReference; |
| writeArrayObject(m_currentAggregateSize); |
| m_currentAggregateSize = savedAggregateSize + 1; |
| } |
| |
| size_t BinaryPropertyListPlan::writeDictionaryStart() |
| { |
| size_t savedAggregateSize = m_currentAggregateSize; |
| m_currentAggregateSize = 0; |
| return savedAggregateSize; |
| } |
| |
| void BinaryPropertyListPlan::writeDictionaryEnd(size_t savedAggregateSize) |
| { |
| ++m_currentObjectReference; |
| writeDictionaryObject(m_currentAggregateSize); |
| m_currentAggregateSize = savedAggregateSize + 1; |
| } |
| |
| static size_t markerPlusLengthByteCount(size_t length) |
| { |
| if (length <= maxLengthInMarkerByte) |
| return 1; |
| return 1 + integerByteCount(length); |
| } |
| |
| void BinaryPropertyListPlan::writeStringObject(const String& string) |
| { |
| unsigned length = string.length(); |
| m_byteCount += markerPlusLengthByteCount(length) + length; |
| if (!string.isAllASCII()) |
| m_byteCount += length; |
| } |
| |
| void BinaryPropertyListPlan::writeStringObject(const char* string) |
| { |
| unsigned length = strlen(string); |
| m_byteCount += markerPlusLengthByteCount(length) + length; |
| } |
| |
| void BinaryPropertyListPlan::writeArrayObject(size_t size) |
| { |
| ASSERT(size); |
| m_byteCount += markerPlusLengthByteCount(size); |
| m_objectReferenceCount += size; |
| } |
| |
| void BinaryPropertyListPlan::writeDictionaryObject(size_t size) |
| { |
| ASSERT(size); |
| ASSERT(!(size & 1)); |
| m_byteCount += markerPlusLengthByteCount(size / 2); |
| m_objectReferenceCount += size; |
| } |
| |
| ObjectReference BinaryPropertyListPlan::booleanTrueObjectReference() const |
| { |
| ASSERT(m_booleanTrueObjectReference != invalidObjectReference()); |
| return m_booleanTrueObjectReference; |
| } |
| |
| ObjectReference BinaryPropertyListPlan::integerObjectReference(int integer) const |
| { |
| ASSERT(integer >= 0); |
| if (!integer) { |
| ASSERT(m_integerZeroObjectReference != invalidObjectReference()); |
| return m_integerZeroObjectReference; |
| } |
| ASSERT(m_integers.contains(integer)); |
| return m_integers.get(integer); |
| } |
| |
| ObjectReference BinaryPropertyListPlan::stringObjectReference(const String& string) const |
| { |
| ASSERT(m_strings.contains(string)); |
| return m_strings.get(string); |
| } |
| |
| ObjectReference BinaryPropertyListPlan::integerArrayObjectReference(const int* integers, size_t size) const |
| { |
| ASSERT(m_integerArrays.contains(IntegerArray(integers, size))); |
| return m_integerArrays.get(IntegerArray(integers, size)); |
| } |
| |
| class BinaryPropertyListSerializer : private BinaryPropertyListObjectStream { |
| public: |
| BinaryPropertyListSerializer(BinaryPropertyListWriter&); |
| |
| private: |
| virtual void writeBooleanTrue(); |
| virtual void writeInteger(int); |
| virtual void writeString(const String&); |
| virtual void writeIntegerArray(const int*, size_t); |
| virtual void writeUniqueString(const String&); |
| virtual void writeUniqueString(const char*); |
| virtual size_t writeArrayStart(); |
| virtual void writeArrayEnd(size_t); |
| virtual size_t writeDictionaryStart(); |
| virtual void writeDictionaryEnd(size_t); |
| |
| ObjectReference writeIntegerWithoutAddingAggregateObjectReference(int); |
| |
| void appendIntegerObject(int); |
| void appendStringObject(const String&); |
| void appendStringObject(const char*); |
| void appendIntegerArrayObject(const int*, size_t); |
| |
| void appendByte(unsigned char); |
| void appendByte(unsigned); |
| void appendByte(unsigned long); |
| void appendByte(int); |
| |
| void appendInteger(size_t); |
| |
| void appendObjectReference(ObjectReference); |
| |
| void addAggregateObjectReference(ObjectReference); |
| |
| void startObject(); |
| |
| const BinaryPropertyListPlan m_plan; |
| const int m_objectReferenceSize; |
| const size_t m_offsetTableStart; |
| const int m_offsetSize; |
| const size_t m_bufferSize; |
| UInt8* const m_buffer; |
| |
| UInt8* m_currentByte; |
| ObjectReference m_currentObjectReference; |
| UInt8* m_currentAggregateBufferByte; |
| }; |
| |
| inline void BinaryPropertyListSerializer::appendByte(unsigned char byte) |
| { |
| *m_currentByte++ = byte; |
| ASSERT(m_currentByte <= m_currentAggregateBufferByte); |
| } |
| |
| inline void BinaryPropertyListSerializer::appendByte(unsigned byte) |
| { |
| *m_currentByte++ = byte; |
| ASSERT(m_currentByte <= m_currentAggregateBufferByte); |
| } |
| |
| inline void BinaryPropertyListSerializer::appendByte(unsigned long byte) |
| { |
| *m_currentByte++ = byte; |
| ASSERT(m_currentByte <= m_currentAggregateBufferByte); |
| } |
| |
| inline void BinaryPropertyListSerializer::appendByte(int byte) |
| { |
| *m_currentByte++ = byte; |
| ASSERT(m_currentByte <= m_currentAggregateBufferByte); |
| } |
| |
| static int bytesNeeded(size_t count) |
| { |
| ASSERT(count); |
| int bytesNeeded = 1; |
| for (size_t mask = std::numeric_limits<size_t>::max() << 8; count & mask; mask <<= 8) |
| ++bytesNeeded; |
| return bytesNeeded; |
| } |
| |
| static inline void storeLength(UInt8* destination, size_t length) |
| { |
| #ifdef __LP64__ |
| destination[0] = length >> 56; |
| destination[1] = length >> 48; |
| destination[2] = length >> 40; |
| destination[3] = length >> 32; |
| #else |
| destination[0] = 0; |
| destination[1] = 0; |
| destination[2] = 0; |
| destination[3] = 0; |
| #endif |
| destination[4] = length >> 24; |
| destination[5] = length >> 16; |
| destination[6] = length >> 8; |
| destination[7] = length; |
| } |
| |
| // Like memmove, but reverses the bytes. |
| static void moveAndReverseBytes(UInt8* destination, const UInt8* source, size_t length) |
| { |
| ASSERT(length); |
| memmove(destination, source, length); |
| UInt8* start = destination; |
| UInt8* end = destination + length; |
| while (end - start > 1) |
| std::swap(*start++, *--end); |
| } |
| |
| // The serializer uses a single buffer for the property list. |
| // The buffer contains: |
| // |
| // 8-byte header |
| // object data |
| // offset table |
| // 32-byte trailer |
| // |
| // While serializing object, the offset table entry for each object is written just before |
| // the object data for that object is written. Aggregates, arrays and dictionaries, are a |
| // special case. The objects that go into an aggregate are written before the aggregate is. |
| // As each object is written, the object reference is put in the aggregate buffer. Then, |
| // when the aggregate is written, the aggregate buffer is copied into place in the object |
| // data. Finally, the header and trailer are written. |
| // |
| // The aggregate buffer shares space with the object data, like this: |
| // |
| // 8-byte header |
| // object data |
| // >>> aggregate buffer <<< |
| // offset table |
| // 32-byte trailer |
| // |
| // To make it easy to build it incrementally, the buffer starts at the end of the object |
| // data space, and grows backwards. We're guaranteed the aggregate buffer will never collide |
| // with the object data pointer because we know that the object data is correctly sized |
| // based on our plan, and all the data in the aggregate buffer will be used to create the |
| // actual aggregate objects; in the worst case the aggregate buffer will already be in |
| // exactly the right place, but backwards. |
| |
| BinaryPropertyListSerializer::BinaryPropertyListSerializer(BinaryPropertyListWriter& client) |
| : m_plan(client) |
| , m_objectReferenceSize(bytesNeeded(m_plan.objectCount())) |
| , m_offsetTableStart(headerSize + m_plan.byteCount() + m_plan.objectReferenceCount() * m_objectReferenceSize) |
| , m_offsetSize(bytesNeeded(m_offsetTableStart)) |
| , m_bufferSize(m_offsetTableStart + m_plan.objectCount() * m_offsetSize + trailerSize) |
| , m_buffer(client.buffer(m_bufferSize)) |
| , m_currentObjectReference(0) |
| { |
| ASSERT(m_objectReferenceSize > 0); |
| ASSERT(m_offsetSize > 0); |
| |
| #ifdef __LP64__ |
| ASSERT(m_objectReferenceSize <= 8); |
| ASSERT(m_offsetSize <= 8); |
| #else |
| ASSERT(m_objectReferenceSize <= 4); |
| ASSERT(m_offsetSize <= 4); |
| #endif |
| |
| if (!m_buffer) |
| return; |
| |
| // Write objects and offset table. |
| m_currentByte = m_buffer + headerSize; |
| m_currentAggregateBufferByte = m_buffer + m_offsetTableStart; |
| client.writeObjects(*this); |
| ASSERT(m_currentObjectReference == m_plan.objectCount()); |
| ASSERT(m_currentAggregateBufferByte == m_buffer + m_offsetTableStart); |
| ASSERT(m_currentByte == m_buffer + m_offsetTableStart); |
| |
| // Write header. |
| memcpy(m_buffer, "bplist00", headerSize); |
| |
| // Write trailer. |
| UInt8* trailer = m_buffer + m_bufferSize - trailerSize; |
| memset(trailer, 0, 6); |
| trailer[6] = m_offsetSize; |
| trailer[7] = m_objectReferenceSize; |
| storeLength(trailer + 8, m_plan.objectCount()); |
| storeLength(trailer + 16, m_plan.objectCount() - 1); |
| storeLength(trailer + 24, m_offsetTableStart); |
| } |
| |
| void BinaryPropertyListSerializer::writeBooleanTrue() |
| { |
| ObjectReference reference = m_plan.booleanTrueObjectReference(); |
| if (m_currentObjectReference != reference) |
| ASSERT(reference < m_currentObjectReference); |
| else { |
| startObject(); |
| appendByte(booleanTrueMarkerByte); |
| } |
| addAggregateObjectReference(reference); |
| } |
| |
| inline ObjectReference BinaryPropertyListSerializer::writeIntegerWithoutAddingAggregateObjectReference(int integer) |
| { |
| ObjectReference reference = m_plan.integerObjectReference(integer); |
| if (m_currentObjectReference != reference) |
| ASSERT(reference < m_currentObjectReference); |
| else |
| appendIntegerObject(integer); |
| return reference; |
| } |
| |
| void BinaryPropertyListSerializer::writeInteger(int integer) |
| { |
| addAggregateObjectReference(writeIntegerWithoutAddingAggregateObjectReference(integer)); |
| } |
| |
| void BinaryPropertyListSerializer::writeString(const String& string) |
| { |
| ObjectReference reference = m_plan.stringObjectReference(string); |
| if (m_currentObjectReference != reference) |
| ASSERT(reference < m_currentObjectReference); |
| else |
| appendStringObject(string); |
| addAggregateObjectReference(reference); |
| } |
| |
| void BinaryPropertyListSerializer::writeIntegerArray(const int* integers, size_t size) |
| { |
| ObjectReference reference = m_plan.integerArrayObjectReference(integers, size); |
| for (size_t i = 0; i < size; ++i) |
| writeIntegerWithoutAddingAggregateObjectReference(integers[i]); |
| if (m_currentObjectReference != reference) |
| ASSERT(reference < m_currentObjectReference); |
| else |
| appendIntegerArrayObject(integers, size); |
| addAggregateObjectReference(reference); |
| } |
| |
| void BinaryPropertyListSerializer::writeUniqueString(const char* string) |
| { |
| addAggregateObjectReference(m_currentObjectReference); |
| appendStringObject(string); |
| } |
| |
| void BinaryPropertyListSerializer::writeUniqueString(const String& string) |
| { |
| addAggregateObjectReference(m_currentObjectReference); |
| appendStringObject(string); |
| } |
| |
| size_t BinaryPropertyListSerializer::writeArrayStart() |
| { |
| return m_currentAggregateBufferByte - m_buffer; |
| } |
| |
| void BinaryPropertyListSerializer::writeArrayEnd(size_t savedAggregateBufferOffset) |
| { |
| ObjectReference reference = m_currentObjectReference; |
| startObject(); |
| size_t aggregateBufferByteCount = savedAggregateBufferOffset - (m_currentAggregateBufferByte - m_buffer); |
| ASSERT(aggregateBufferByteCount); |
| ASSERT(!(aggregateBufferByteCount % m_objectReferenceSize)); |
| size_t size = aggregateBufferByteCount / m_objectReferenceSize; |
| if (size <= maxLengthInMarkerByte) |
| appendByte(arrayMarkerByte | size); |
| else { |
| appendByte(arrayWithSeparateLengthMarkerByte); |
| appendInteger(size); |
| } |
| m_currentAggregateBufferByte = m_buffer + savedAggregateBufferOffset; |
| ASSERT(m_currentByte <= m_currentAggregateBufferByte); |
| moveAndReverseBytes(m_currentByte, m_currentAggregateBufferByte - aggregateBufferByteCount, aggregateBufferByteCount); |
| m_currentByte += aggregateBufferByteCount; |
| ASSERT(m_currentByte <= m_currentAggregateBufferByte); |
| if (m_currentObjectReference < m_plan.objectCount()) |
| addAggregateObjectReference(reference); |
| else |
| ASSERT(m_currentObjectReference == m_plan.objectCount()); |
| } |
| |
| size_t BinaryPropertyListSerializer::writeDictionaryStart() |
| { |
| return m_currentAggregateBufferByte - m_buffer; |
| } |
| |
| void BinaryPropertyListSerializer::writeDictionaryEnd(size_t savedAggregateBufferOffset) |
| { |
| ObjectReference reference = m_currentObjectReference; |
| startObject(); |
| size_t aggregateBufferByteCount = savedAggregateBufferOffset - (m_currentAggregateBufferByte - m_buffer); |
| ASSERT(aggregateBufferByteCount); |
| ASSERT(!(aggregateBufferByteCount % (m_objectReferenceSize * 2))); |
| size_t size = aggregateBufferByteCount / (m_objectReferenceSize * 2); |
| if (size <= maxLengthInMarkerByte) |
| appendByte(dictionaryMarkerByte | size); |
| else { |
| appendByte(dictionaryWithSeparateLengthMarkerByte); |
| appendInteger(size); |
| } |
| m_currentAggregateBufferByte = m_buffer + savedAggregateBufferOffset; |
| ASSERT(m_currentByte <= m_currentAggregateBufferByte); |
| moveAndReverseBytes(m_currentByte, m_currentAggregateBufferByte - aggregateBufferByteCount, aggregateBufferByteCount); |
| m_currentByte += aggregateBufferByteCount; |
| ASSERT(m_currentByte <= m_currentAggregateBufferByte); |
| if (m_currentObjectReference != m_plan.objectCount()) |
| addAggregateObjectReference(reference); |
| else |
| ASSERT(m_currentObjectReference == m_plan.objectCount()); |
| } |
| |
| void BinaryPropertyListSerializer::appendIntegerObject(int integer) |
| { |
| startObject(); |
| ASSERT(integer >= 0); |
| appendInteger(integer); |
| } |
| |
| void BinaryPropertyListSerializer::appendInteger(size_t integer) |
| { |
| if (integer <= 0xFF) { |
| appendByte(oneByteIntegerMarkerByte); |
| appendByte(integer); |
| return; |
| } |
| if (integer <= 0xFFFF) { |
| appendByte(twoByteIntegerMarkerByte); |
| appendByte(integer >> 8); |
| appendByte(integer); |
| return; |
| } |
| #ifdef __LP64__ |
| if (integer <= 0xFFFFFFFFULL) { |
| #endif |
| appendByte(fourByteIntegerMarkerByte); |
| appendByte(integer >> 24); |
| appendByte(integer >> 16); |
| appendByte(integer >> 8); |
| appendByte(integer); |
| #ifdef __LP64__ |
| return; |
| } |
| appendByte(eightByteIntegerMarkerByte); |
| appendByte(integer >> 56); |
| appendByte(integer >> 48); |
| appendByte(integer >> 40); |
| appendByte(integer >> 32); |
| appendByte(integer >> 24); |
| appendByte(integer >> 16); |
| appendByte(integer >> 8); |
| appendByte(integer); |
| #endif |
| } |
| |
| void BinaryPropertyListSerializer::appendStringObject(const String& string) |
| { |
| startObject(); |
| unsigned length = string.length(); |
| if (string.isAllASCII()) { |
| if (length <= maxLengthInMarkerByte) |
| appendByte(static_cast<unsigned char>(asciiStringMarkerByte | length)); |
| else { |
| appendByte(asciiStringWithSeparateLengthMarkerByte); |
| appendInteger(length); |
| } |
| for (unsigned i = 0; i < length; ++i) |
| appendByte(string[i]); |
| } else { |
| if (length <= maxLengthInMarkerByte) |
| appendByte(static_cast<unsigned char>(unicodeStringMarkerByte | length)); |
| else { |
| appendByte(unicodeStringWithSeparateLengthMarkerByte); |
| appendInteger(length); |
| } |
| for (unsigned i = 0; i < length; ++i) { |
| appendByte(string[i] >> 8); |
| appendByte(string[i]); |
| } |
| } |
| } |
| |
| void BinaryPropertyListSerializer::appendStringObject(const char* string) |
| { |
| startObject(); |
| unsigned length = strlen(string); |
| if (length <= maxLengthInMarkerByte) |
| appendByte(static_cast<unsigned char>(asciiStringMarkerByte | length)); |
| else { |
| appendByte(asciiStringWithSeparateLengthMarkerByte); |
| appendInteger(length); |
| } |
| for (unsigned i = 0; i < length; ++i) |
| appendByte(string[i]); |
| } |
| |
| void BinaryPropertyListSerializer::appendIntegerArrayObject(const int* integers, size_t size) |
| { |
| startObject(); |
| if (size <= maxLengthInMarkerByte) |
| appendByte(arrayMarkerByte | size); |
| else { |
| appendByte(arrayWithSeparateLengthMarkerByte); |
| appendInteger(size); |
| } |
| for (unsigned i = 0; i < size; ++i) |
| appendObjectReference(m_plan.integerObjectReference(integers[i])); |
| } |
| |
| void BinaryPropertyListSerializer::appendObjectReference(ObjectReference reference) |
| { |
| switch (m_objectReferenceSize) { |
| #ifdef __LP64__ |
| case 8: |
| appendByte(reference >> 56); |
| FALLTHROUGH; |
| case 7: |
| appendByte(reference >> 48); |
| FALLTHROUGH; |
| case 6: |
| appendByte(reference >> 40); |
| FALLTHROUGH; |
| case 5: |
| appendByte(reference >> 32); |
| FALLTHROUGH; |
| #endif |
| case 4: |
| appendByte(reference >> 24); |
| FALLTHROUGH; |
| case 3: |
| appendByte(reference >> 16); |
| FALLTHROUGH; |
| case 2: |
| appendByte(reference >> 8); |
| FALLTHROUGH; |
| case 1: |
| appendByte(reference); |
| } |
| } |
| |
| void BinaryPropertyListSerializer::startObject() |
| { |
| ObjectReference reference = m_currentObjectReference++; |
| |
| size_t offset = m_currentByte - m_buffer; |
| |
| UInt8* offsetTableEntry = m_buffer + m_offsetTableStart + reference * m_offsetSize + m_offsetSize; |
| switch (m_offsetSize) { |
| #ifdef __LP64__ |
| case 8: |
| offsetTableEntry[-8] = offset >> 56; |
| FALLTHROUGH; |
| case 7: |
| offsetTableEntry[-7] = offset >> 48; |
| FALLTHROUGH; |
| case 6: |
| offsetTableEntry[-6] = offset >> 40; |
| FALLTHROUGH; |
| case 5: |
| offsetTableEntry[-5] = offset >> 32; |
| FALLTHROUGH; |
| #endif |
| case 4: |
| offsetTableEntry[-4] = offset >> 24; |
| FALLTHROUGH; |
| case 3: |
| offsetTableEntry[-3] = offset >> 16; |
| FALLTHROUGH; |
| case 2: |
| offsetTableEntry[-2] = offset >> 8; |
| FALLTHROUGH; |
| case 1: |
| offsetTableEntry[-1] = offset; |
| } |
| } |
| |
| void BinaryPropertyListSerializer::addAggregateObjectReference(ObjectReference reference) |
| { |
| switch (m_objectReferenceSize) { |
| #ifdef __LP64__ |
| case 8: |
| *--m_currentAggregateBufferByte = reference >> 56; |
| FALLTHROUGH; |
| case 7: |
| *--m_currentAggregateBufferByte = reference >> 48; |
| FALLTHROUGH; |
| case 6: |
| *--m_currentAggregateBufferByte = reference >> 40; |
| FALLTHROUGH; |
| case 5: |
| *--m_currentAggregateBufferByte = reference >> 32; |
| FALLTHROUGH; |
| #endif |
| case 4: |
| *--m_currentAggregateBufferByte = reference >> 24; |
| FALLTHROUGH; |
| case 3: |
| *--m_currentAggregateBufferByte = reference >> 16; |
| FALLTHROUGH; |
| case 2: |
| *--m_currentAggregateBufferByte = reference >> 8; |
| FALLTHROUGH; |
| case 1: |
| *--m_currentAggregateBufferByte = reference; |
| } |
| ASSERT(m_currentByte <= m_currentAggregateBufferByte); |
| } |
| |
| void BinaryPropertyListWriter::writePropertyList() |
| { |
| BinaryPropertyListSerializer serializer(*this); |
| } |