| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #ifndef jit_Compactbuffer_h |
| #define jit_Compactbuffer_h |
| |
| #include "jsalloc.h" |
| |
| #include "jit/IonTypes.h" |
| #include "js/Vector.h" |
| |
| namespace js { |
| namespace jit { |
| |
| class CompactBufferWriter; |
| |
| // CompactBuffers are byte streams designed for compressable integers. It has |
| // helper functions for writing bytes, fixed-size integers, and variable-sized |
| // integers. Variable sized integers are encoded in 1-5 bytes, each byte |
| // containing 7 bits of the integer and a bit which specifies whether the next |
| // byte is also part of the integer. |
| // |
| // Fixed-width integers are also available, in case the actual value will not |
| // be known until later. |
| |
| class CompactBufferReader |
| { |
| const uint8_t* buffer_; |
| const uint8_t* end_; |
| |
| uint32_t readVariableLength() { |
| uint32_t val = 0; |
| uint32_t shift = 0; |
| uint8_t byte; |
| while (true) { |
| MOZ_ASSERT(shift < 32); |
| byte = readByte(); |
| val |= (uint32_t(byte) >> 1) << shift; |
| shift += 7; |
| if (!(byte & 1)) |
| return val; |
| } |
| } |
| |
| public: |
| CompactBufferReader(const uint8_t* start, const uint8_t* end) |
| : buffer_(start), |
| end_(end) |
| { } |
| inline explicit CompactBufferReader(const CompactBufferWriter& writer); |
| uint8_t readByte() { |
| MOZ_ASSERT(buffer_ < end_); |
| return *buffer_++; |
| } |
| uint32_t readFixedUint32_t() { |
| uint32_t b0 = readByte(); |
| uint32_t b1 = readByte(); |
| uint32_t b2 = readByte(); |
| uint32_t b3 = readByte(); |
| return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24); |
| } |
| uint16_t readFixedUint16_t() { |
| uint32_t b0 = readByte(); |
| uint32_t b1 = readByte(); |
| return b0 | (b1 << 8); |
| } |
| uint32_t readNativeEndianUint32_t() { |
| // Must be at 4-byte boundary |
| MOZ_ASSERT(uintptr_t(buffer_) % sizeof(uint32_t) == 0); |
| return *reinterpret_cast<const uint32_t*>(buffer_); |
| } |
| uint32_t readUnsigned() { |
| return readVariableLength(); |
| } |
| int32_t readSigned() { |
| uint8_t b = readByte(); |
| bool isNegative = !!(b & (1 << 0)); |
| bool more = !!(b & (1 << 1)); |
| int32_t result = b >> 2; |
| if (more) |
| result |= readUnsigned() << 6; |
| if (isNegative) |
| return -result; |
| return result; |
| } |
| |
| bool more() const { |
| MOZ_ASSERT(buffer_ <= end_); |
| return buffer_ < end_; |
| } |
| |
| void seek(const uint8_t* start, uint32_t offset) { |
| buffer_ = start + offset; |
| MOZ_ASSERT(start < end_); |
| MOZ_ASSERT(buffer_ < end_); |
| } |
| |
| const uint8_t* currentPosition() const { |
| return buffer_; |
| } |
| }; |
| |
| class CompactBufferWriter |
| { |
| js::Vector<uint8_t, 32, SystemAllocPolicy> buffer_; |
| bool enoughMemory_; |
| |
| public: |
| CompactBufferWriter() |
| : enoughMemory_(true) |
| { } |
| |
| void setOOM() { |
| enoughMemory_ = false; |
| } |
| |
| // Note: writeByte() takes uint32 to catch implicit casts with a runtime |
| // assert. |
| void writeByte(uint32_t byte) { |
| MOZ_ASSERT(byte <= 0xFF); |
| enoughMemory_ &= buffer_.append(byte); |
| } |
| void writeByteAt(uint32_t pos, uint32_t byte) { |
| MOZ_ASSERT(byte <= 0xFF); |
| if (!oom()) |
| buffer_[pos] = byte; |
| } |
| void writeUnsigned(uint32_t value) { |
| do { |
| uint8_t byte = ((value & 0x7F) << 1) | (value > 0x7F); |
| writeByte(byte); |
| value >>= 7; |
| } while (value); |
| } |
| void writeUnsignedAt(uint32_t pos, uint32_t value, uint32_t original) { |
| MOZ_ASSERT(value <= original); |
| do { |
| uint8_t byte = ((value & 0x7F) << 1) | (original > 0x7F); |
| writeByteAt(pos++, byte); |
| value >>= 7; |
| original >>= 7; |
| } while (original); |
| } |
| void writeSigned(int32_t v) { |
| bool isNegative = v < 0; |
| uint32_t value = isNegative ? -v : v; |
| uint8_t byte = ((value & 0x3F) << 2) | ((value > 0x3F) << 1) | uint32_t(isNegative); |
| writeByte(byte); |
| |
| // Write out the rest of the bytes, if needed. |
| value >>= 6; |
| if (value == 0) |
| return; |
| writeUnsigned(value); |
| } |
| void writeFixedUint32_t(uint32_t value) { |
| writeByte(value & 0xFF); |
| writeByte((value >> 8) & 0xFF); |
| writeByte((value >> 16) & 0xFF); |
| writeByte((value >> 24) & 0xFF); |
| } |
| void writeFixedUint16_t(uint16_t value) { |
| writeByte(value & 0xFF); |
| writeByte(value >> 8); |
| } |
| void writeNativeEndianUint32_t(uint32_t value) { |
| // Must be at 4-byte boundary |
| MOZ_ASSERT_IF(!oom(), length() % sizeof(uint32_t) == 0); |
| writeFixedUint32_t(0); |
| if (oom()) |
| return; |
| uint8_t* endPtr = buffer() + length(); |
| reinterpret_cast<uint32_t*>(endPtr)[-1] = value; |
| } |
| size_t length() const { |
| return buffer_.length(); |
| } |
| uint8_t* buffer() { |
| MOZ_ASSERT(!oom()); |
| return &buffer_[0]; |
| } |
| const uint8_t* buffer() const { |
| MOZ_ASSERT(!oom()); |
| return &buffer_[0]; |
| } |
| bool oom() const { |
| return !enoughMemory_; |
| } |
| }; |
| |
| CompactBufferReader::CompactBufferReader(const CompactBufferWriter& writer) |
| : buffer_(writer.buffer()), |
| end_(writer.buffer() + writer.length()) |
| { |
| } |
| |
| } // namespace jit |
| } // namespace js |
| |
| #endif /* jit_Compactbuffer_h */ |