| //------------------------------------------------------------------------------------------------------- |
| // Copyright (C) Microsoft. All rights reserved. |
| // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. |
| //------------------------------------------------------------------------------------------------------- |
| // Buffer builder is used to layout out binary content which contains offsets |
| // from one part of the content to another. |
| // It works in two-pass fashion: |
| // - Pass one fixes the real offset of each element (BufferBuilder) of the |
| // content. |
| // - Pass two writes the actual content including any relative offset values. |
| //---------------------------------------------------------------------------- |
| |
| #pragma once |
| |
| #if _M_IX86 |
| #define serialization_alignment |
| #elif _M_X64 || defined(_M_ARM32_OR_ARM64) |
| #define serialization_alignment __unaligned |
| #else |
| #error Must define alignment capabilities for processor |
| #endif |
| |
| struct _SIMDValue; |
| typedef _SIMDValue SIMDValue; |
| |
| namespace Js |
| { |
| // The base buffer builder class |
| class BufferBuilder |
| { |
| protected: |
| LPCWSTR clue; |
| BufferBuilder(LPCWSTR clue) |
| : clue(clue), offset(0xffffffff) { } |
| public: |
| uint32 offset; |
| virtual uint32 FixOffset(uint32 offset) = 0; |
| virtual void Write(__in_bcount(bufferSize) byte * buffer, __in uint32 bufferSize) const = 0; |
| #if DBG |
| protected: |
| void TraceOutput(byte * buffer, uint32 size) const; |
| #endif |
| }; |
| |
| #if VARIABLE_INT_ENCODING |
| #define VARIABLE_INT_TAGBIT_COUNT (1) |
| #define VARIABLE_INT_BYTE_SHIFT (8 - VARIABLE_INT_TAGBIT_COUNT) |
| #define VARIABLE_INT_BYTE_MAX (1 << VARIABLE_INT_BYTE_SHIFT) |
| #define VARIABLE_INT_BYTE_MASK ~((byte) VARIABLE_INT_BYTE_MAX) |
| #define SENTINEL_BYTE_COUNT 1 |
| |
| #define ONE_BYTE_MAX ((byte) 0xfd) |
| #define TWO_BYTE_MAX ((uint16) 0xffff) |
| |
| #define TWO_BYTE_SENTINEL ONE_BYTE_MAX + 1 |
| #define FOUR_BYTE_SENTINEL ONE_BYTE_MAX + 2 |
| |
| #define MIN_SENTINEL TWO_BYTE_SENTINEL |
| #endif |
| |
| #if INSTRUMENT_BUFFER_INTS |
| static uint Counts[] = { 0, 0, 0, 0 }; |
| #endif |
| |
| // Templatized buffer builder for writing fixed-size content |
| template<typename T, bool useVariableIntEncoding> |
| struct BufferBuilderOf : BufferBuilder |
| { |
| typedef serialization_alignment T value_type; |
| value_type value; |
| |
| BufferBuilderOf(LPCWSTR clue, const T & value) |
| : BufferBuilder(clue), value(value) |
| { } |
| |
| // Assume that the value is 0- for negative values of value, we'll just use the default encoding |
| bool UseOneByte() const |
| { |
| return value >= 0 && value <= ONE_BYTE_MAX; |
| } |
| |
| bool UseTwoBytes() const |
| { |
| return value > ONE_BYTE_MAX && value <= TWO_BYTE_MAX; |
| } |
| |
| uint32 FixOffset(uint32 offset) override |
| { |
| this->offset = offset; |
| |
| if (useVariableIntEncoding) |
| { |
| if (UseOneByte()) |
| { |
| return this->offset + sizeof(serialization_alignment byte); |
| } |
| else if (UseTwoBytes()) |
| { |
| return this->offset + sizeof(serialization_alignment uint16) + SENTINEL_BYTE_COUNT; |
| } |
| |
| return this->offset + sizeof(serialization_alignment T) + SENTINEL_BYTE_COUNT; |
| } |
| else |
| { |
| return this->offset + sizeof(serialization_alignment T); |
| } |
| } |
| |
| void Write(__in_bcount(bufferSize) byte * buffer, __in uint32 bufferSize) const |
| { |
| DebugOnly(uint32 size = sizeof(T)); |
| |
| #if INSTRUMENT_BUFFER_INTS |
| if (value < ((1 << 8))) |
| { |
| Counts[0]++; |
| } |
| else if (value < ((1 << 16))) |
| { |
| Counts[1]++; |
| } |
| else if (value < ((1 << 24))) |
| { |
| Counts[2]++; |
| } |
| else |
| { |
| Counts[3]++; |
| } |
| #endif |
| |
| if (useVariableIntEncoding) |
| { |
| if (UseOneByte()) |
| { |
| if (bufferSize - this->offset<sizeof(serialization_alignment byte)) |
| { |
| Throw::FatalInternalError(); |
| } |
| DebugOnly(size = sizeof(byte)); |
| *(serialization_alignment byte*)(buffer + this->offset) = (byte) value; |
| } |
| else if (UseTwoBytes()) |
| { |
| if (bufferSize - this->offset<sizeof(serialization_alignment uint16)) |
| { |
| Throw::FatalInternalError(); |
| } |
| DebugOnly(size = sizeof(uint16) + 1); |
| *(serialization_alignment byte*)(buffer + this->offset) = TWO_BYTE_SENTINEL; |
| *(serialization_alignment uint16*)(buffer + this->offset + SENTINEL_BYTE_COUNT) = (uint16) this->value; |
| } |
| else |
| { |
| if (bufferSize - this->offset<sizeof(serialization_alignment T)) |
| { |
| Throw::FatalInternalError(); |
| } |
| *(serialization_alignment byte*)(buffer + this->offset) = FOUR_BYTE_SENTINEL; |
| *(serialization_alignment T*)(buffer + this->offset + SENTINEL_BYTE_COUNT) = this->value; |
| DebugOnly(size = sizeof(T) + 1); |
| #if INSTRUMENT_BUFFER_INTS |
| printf("[BCGENSTATS] %d, %d\n", value, sizeof(T)); |
| #endif |
| } |
| } |
| else |
| { |
| if (bufferSize - this->offset<sizeof(serialization_alignment T)) |
| { |
| Throw::FatalInternalError(); |
| } |
| *(serialization_alignment T*)(buffer + this->offset) = value; |
| } |
| DebugOnly(TraceOutput(buffer, size)); |
| } |
| }; |
| |
| template<typename T> |
| struct BufferBuilderOf<T, false>: BufferBuilder |
| { |
| typedef serialization_alignment T value_type; |
| value_type value; |
| |
| BufferBuilderOf(LPCWSTR clue, const T & value) |
| : BufferBuilder(clue), value(value) |
| { } |
| |
| uint32 FixOffset(uint32 offset) override |
| { |
| this->offset = offset; |
| |
| return this->offset + sizeof(serialization_alignment T); |
| } |
| |
| void Write(__in_bcount(bufferSize) byte * buffer, __in uint32 bufferSize) const |
| { |
| if (bufferSize - this->offset<sizeof(serialization_alignment T)) |
| { |
| Throw::FatalInternalError(); |
| } |
| |
| *(serialization_alignment T*)(buffer + this->offset) = value; |
| DebugOnly(TraceOutput(buffer, sizeof(T))); |
| } |
| }; |
| |
| |
| template <typename T> |
| struct ConstantSizedBufferBuilderOf : BufferBuilderOf<T, false> |
| { |
| ConstantSizedBufferBuilderOf(LPCWSTR clue, const T & value) |
| : BufferBuilderOf<T, false>(clue, value) |
| { } |
| }; |
| |
| #if VARIABLE_INT_ENCODING |
| typedef BufferBuilderOf<int16, true> BufferBuilderInt16; |
| typedef BufferBuilderOf<int, true> BufferBuilderInt32; |
| typedef ConstantSizedBufferBuilderOf<byte> BufferBuilderByte; |
| typedef ConstantSizedBufferBuilderOf<float> BufferBuilderFloat; |
| typedef ConstantSizedBufferBuilderOf<double> BufferBuilderDouble; |
| typedef ConstantSizedBufferBuilderOf<SIMDValue> BufferBuilderSIMD; |
| #else |
| typedef ConstantSizedBufferBuilderOf<int16> BufferBuilderInt16; |
| typedef ConstantSizedBufferBuilderOf<int> BufferBuilderInt32; |
| typedef ConstantSizedBufferBuilderOf<byte> BufferBuilderByte; |
| typedef ConstantSizedBufferBuilderOf<float> BufferBuilderFloat; |
| typedef ConstantSizedBufferBuilderOf<double> BufferBuilderDouble; |
| typedef ConstantSizedBufferBuilderOf<SIMDValue> BufferBuilderSIMD; |
| #endif |
| |
| // A buffer builder which contains a list of buffer builders |
| struct BufferBuilderList : BufferBuilder |
| { |
| regex::ImmutableList<BufferBuilder*> * list; |
| BufferBuilderList(LPCWSTR clue) |
| : BufferBuilder(clue), list(nullptr) |
| { } |
| |
| uint32 FixOffset(uint32 offset) override |
| { |
| this->offset = offset; |
| return list->Accumulate(offset,[](uint32 size, BufferBuilder * builder)->uint32 { |
| return builder->FixOffset(size); |
| }); |
| } |
| |
| void Write(__in_bcount(bufferSize) byte * buffer, __in uint32 bufferSize) const |
| { |
| return list->Iterate([&](BufferBuilder * builder) { |
| builder->Write(buffer, bufferSize); |
| }); |
| } |
| |
| }; |
| |
| // A buffer builder which points to another buffer builder. |
| // At write time, it will write the offset from the start of the raw buffer to |
| // the pointed-to location. |
| struct BufferBuilderRelativeOffset : BufferBuilder |
| { |
| BufferBuilder * pointsTo; |
| uint32 additionalOffset; |
| BufferBuilderRelativeOffset(LPCWSTR clue, BufferBuilder * pointsTo, uint32 additionalOffset) |
| : BufferBuilder(clue), pointsTo(pointsTo), additionalOffset(additionalOffset) |
| { } |
| BufferBuilderRelativeOffset(LPCWSTR clue, BufferBuilder * pointsTo) |
| : BufferBuilder(clue), pointsTo(pointsTo), additionalOffset(0) |
| { } |
| |
| uint32 FixOffset(uint32 offset) override |
| { |
| this->offset = offset; |
| return this->offset + sizeof(int); |
| } |
| |
| void Write(__in_bcount(bufferSize) byte * buffer, __in uint32 bufferSize) const |
| { |
| if (bufferSize - this->offset<sizeof(int)) |
| { |
| Throw::FatalInternalError(); |
| } |
| |
| int offsetOfPointedTo = pointsTo->offset; |
| *(int*)(buffer + this->offset) = offsetOfPointedTo + additionalOffset; |
| DebugOnly(TraceOutput(buffer, sizeof(int))); |
| } |
| }; |
| |
| // A buffer builder which holds a raw byte buffer |
| struct BufferBuilderRaw : BufferBuilder |
| { |
| uint32 size; |
| const byte * raw; |
| BufferBuilderRaw(LPCWSTR clue, __in uint32 size, __in_bcount(size) const byte * raw) |
| : BufferBuilder(clue), size(size), raw(raw) |
| { } |
| |
| uint32 FixOffset(uint32 offset) override |
| { |
| this->offset = offset; |
| return this->offset + size; |
| } |
| |
| void Write(__in_bcount(bufferSize) byte * buffer, __in uint32 bufferSize) const |
| { |
| if (bufferSize - this->offset<size) |
| { |
| Throw::FatalInternalError(); |
| } |
| |
| js_memcpy_s(buffer + this->offset, bufferSize-this->offset, raw, size); |
| DebugOnly(TraceOutput(buffer, size)); |
| } |
| }; |
| |
| // A buffer builder which aligns its contents to the specified alignment |
| struct BufferBuilderAligned : BufferBuilder |
| { |
| BufferBuilder * content; |
| uint32 alignment; |
| uint32 padding; |
| |
| BufferBuilderAligned(LPCWSTR clue, BufferBuilder * content, uint32 alignment) |
| : BufferBuilder(clue), content(content), alignment(alignment), padding(0) |
| { } |
| |
| uint32 FixOffset(uint32 offset) override |
| { |
| this->offset = offset; |
| |
| // Calculate padding |
| offset = ::Math::Align(this->offset, this->alignment); |
| this->padding = offset - this->offset; |
| Assert(this->padding < this->alignment); |
| |
| return content->FixOffset(offset); |
| } |
| |
| void Write(__in_bcount(bufferSize) byte * buffer, __in uint32 bufferSize) const |
| { |
| if (bufferSize - this->offset < this->padding) |
| { |
| Throw::FatalInternalError(); |
| } |
| |
| for (uint32 i = 0; i < this->padding; i++) |
| { |
| buffer[this->offset + i] = 0; |
| } |
| |
| this->content->Write(buffer, bufferSize); |
| } |
| }; |
| |
| } |