/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * 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 "mozilla/Assertions.h" #include #include #include "js/AllocPolicy.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; } } } uint64_t readVariableLength64() { uint64_t val = 0; uint32_t shift = 0; uint8_t byte; while (true) { MOZ_ASSERT(shift < 64); byte = readByte(); val |= (uint64_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(buffer_); } uint32_t readUnsigned() { return readVariableLength(); } uint64_t readUnsigned64() { return readVariableLength64(); } 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; } // Reads a value written by writeUnsigned15Bit. uint32_t readUnsigned15Bit() { uint8_t byte = readByte(); uint32_t val = byte >> 1; if (byte & 1) { val |= uint32_t(readByte()) << 7; } return val; } void* readRawPointer() { uintptr_t ptrWord = 0; for (unsigned i = 0; i < sizeof(uintptr_t); i++) { ptrWord |= static_cast(readByte()) << (i * 8); } return reinterpret_cast(ptrWord); } 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 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); if (!buffer_.append(byte)) { enoughMemory_ = false; } } void writeByteAt(uint32_t pos, uint32_t byte) { MOZ_ASSERT(byte <= 0xFF); if (!oom()) { buffer_[pos] = byte; } } // Writes a variable-length value similar to writeUnsigned, but optimized for // small 15-bit values that fit in one or two variable-length-encoded bytes. // Must be read using readUnsigned15Bit. void writeUnsigned15Bit(uint32_t value) { uint8_t byte1 = ((value & 0x7F) << 1) | (value > 0x7F); writeByte(byte1); value >>= 7; if (value) { MOZ_ASSERT(value <= 0xFF); writeByte(value); } } 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 writeUnsigned64(uint64_t value) { do { uint8_t byte = ((value & 0x7F) << 1) | (value > 0x7F); writeByte(byte); value >>= 7; } while (value); } 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(endPtr)[-1] = value; } void writeRawPointer(const void* ptr) { uintptr_t ptrWord = reinterpret_cast(ptr); for (unsigned i = 0; i < sizeof(uintptr_t); i++) { writeByte((ptrWord >> (i * 8)) & 0xFF); } } 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_; } void propagateOOM(bool success) { enoughMemory_ &= success; } }; CompactBufferReader::CompactBufferReader(const CompactBufferWriter& writer) : buffer_(writer.buffer()), end_(writer.buffer() + writer.length()) {} } // namespace jit } // namespace js #endif /* jit_Compactbuffer_h */