| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| #ifndef RUNTIME_VM_DATASTREAM_H_ | |
| #define RUNTIME_VM_DATASTREAM_H_ | |
| #include "platform/assert.h" | |
| #include "platform/utils.h" | |
| #include "vm/allocation.h" | |
| #include "vm/exceptions.h" | |
| #include "vm/globals.h" | |
| #include "vm/os.h" | |
| #include "vm/zone.h" | |
| namespace dart { | |
| static constexpr int8_t kDataBitsPerByte = 7; | |
| static constexpr int8_t kByteMask = (1 << kDataBitsPerByte) - 1; | |
| static constexpr int8_t kMaxUnsignedDataPerByte = kByteMask; | |
| static constexpr int8_t kMinDataPerByte = -(1 << (kDataBitsPerByte - 1)); | |
| static constexpr int8_t kMaxDataPerByte = | |
| (~kMinDataPerByte & kByteMask); // NOLINT | |
| static constexpr uint8_t kEndByteMarker = (255 - kMaxDataPerByte); | |
| static constexpr uint8_t kEndUnsignedByteMarker = | |
| (255 - kMaxUnsignedDataPerByte); | |
| struct LEB128Constants : AllStatic { | |
| // Convenience template for ensuring non-signed types trigger SFINAE. | |
| template <typename T, typename S> | |
| using only_if_signed = | |
| typename std::enable_if<std::is_signed<T>::value, S>::type; | |
| // Convenience template for ensuring signed types trigger SFINAE. | |
| template <typename T, typename S> | |
| using only_if_unsigned = | |
| typename std::enable_if<std::is_unsigned<T>::value, S>::type; | |
| // (S)LEB128 encodes 7 bits of data per byte (hence 128). | |
| static constexpr uint8_t kDataBitsPerByte = 7; | |
| static constexpr uint8_t kDataByteMask = (1 << kDataBitsPerByte) - 1; | |
| // If more data follows a given data byte, the high bit is set. | |
| static constexpr uint8_t kMoreDataMask = (1 << kDataBitsPerByte); | |
| // For SLEB128, the high bit in the data of the last byte is the sign bit. | |
| static constexpr uint8_t kSignMask = (1 << (kDataBitsPerByte - 1)); | |
| }; | |
| class NonStreamingWriteStream; | |
| namespace module_snapshot { | |
| class Deserializer; | |
| } | |
| // Stream for reading various types from a buffer. | |
| class ReadStream : public ValueObject { | |
| public: | |
| ReadStream(const uint8_t* buffer, intptr_t size) | |
| : buffer_(buffer), current_(buffer), end_(buffer + size) {} | |
| // Creates a ReadStream that starts at a given position in the buffer. | |
| ReadStream(const uint8_t* buffer, intptr_t size, intptr_t pos) | |
| : ReadStream(buffer, size) { | |
| SetPosition(pos); | |
| } | |
| template <int N, typename T> | |
| class Raw {}; | |
| template <typename T> | |
| class Raw<1, T> { | |
| public: | |
| static T Read(ReadStream* st) { return bit_cast<T>(st->ReadByte()); } | |
| }; | |
| template <typename T> | |
| class Raw<2, T> { | |
| public: | |
| static T Read(ReadStream* st) { return bit_cast<T>(st->Read16()); } | |
| }; | |
| template <typename T> | |
| class Raw<4, T> { | |
| public: | |
| static T Read(ReadStream* st) { return bit_cast<T>(st->Read32()); } | |
| }; | |
| template <typename T> | |
| class Raw<8, T> { | |
| public: | |
| static T Read(ReadStream* st) { return bit_cast<T>(st->Read64()); } | |
| }; | |
| // Reads 'len' bytes from the stream. | |
| void ReadBytes(void* addr, intptr_t len) { | |
| ASSERT((end_ - current_) >= len); | |
| if (len != 0) { | |
| memmove(addr, current_, len); | |
| } | |
| current_ += len; | |
| } | |
| template <typename T = intptr_t> | |
| T ReadUnsigned() { | |
| return Read<T>(kEndUnsignedByteMarker); | |
| } | |
| intptr_t ReadRefId() { | |
| const int8_t* cursor = reinterpret_cast<const int8_t*>(current_); | |
| intptr_t result = 0; | |
| intptr_t byte; | |
| // clang-format off | |
| #define STAGE \ | |
| byte = *cursor++; /* ldrsb byte, [result], 1 */ \ | |
| result = byte + (result << 7); /* add result, byte, result lsl 7 */ \ | |
| if (byte < 0) goto done; /* tbnz byte, 63, done */ | |
| STAGE // 0-7 | |
| STAGE // 8-14 | |
| STAGE // 15-21 | |
| STAGE // 22-28 | |
| #undef STAGE | |
| ASSERT(byte < 0); // 256MB is enough for anyone... | |
| // clang-format on | |
| done: | |
| current_ = reinterpret_cast<const uint8_t*>(cursor); | |
| // With big-endian order and the has-more marker being 0, the correction | |
| // factor to remove the last-byte marker is a constant, which can be folded | |
| // into subsequent load offsets. | |
| return result + 128; | |
| } | |
| intptr_t Position() const { return current_ - buffer_; } | |
| void SetPosition(intptr_t value) { | |
| ASSERT((end_ - buffer_) >= value); | |
| current_ = buffer_ + value; | |
| } | |
| void Align(intptr_t alignment, intptr_t offset = 0) { | |
| intptr_t position_before = Position(); | |
| intptr_t position_after = | |
| Utils::RoundUp(position_before, alignment, offset); | |
| Advance(position_after - position_before); | |
| } | |
| const uint8_t* AddressOfCurrentPosition() const { return current_; } | |
| void Advance(intptr_t value) { | |
| ASSERT((end_ - current_) >= value); | |
| current_ = current_ + value; | |
| } | |
| intptr_t PendingBytes() const { | |
| ASSERT(end_ >= current_); | |
| return (end_ - current_); | |
| } | |
| template <typename T> | |
| T Read() { | |
| return Read<T>(kEndByteMarker); | |
| } | |
| uword ReadWordWith32BitReads() { | |
| constexpr intptr_t kNumRead32PerWord = kBitsPerWord / kBitsPerInt32; | |
| uword value = 0; | |
| for (intptr_t j = 0; j < kNumRead32PerWord; j++) { | |
| const auto partial_value = Raw<kInt32Size, uint32_t>::Read(this); | |
| value |= (static_cast<uword>(partial_value) << (j * kBitsPerInt32)); | |
| } | |
| return value; | |
| } | |
| private: | |
| using C = LEB128Constants; | |
| public: | |
| template <typename T = uintptr_t> | |
| C::only_if_unsigned<T, T> ReadLEB128() { | |
| constexpr intptr_t kBitsPerT = kBitsPerByte * sizeof(T); | |
| T r = 0; | |
| uint8_t s = 0; | |
| uint8_t b; | |
| do { | |
| ASSERT(s < kBitsPerT); | |
| b = ReadByte(); | |
| r |= static_cast<T>(b & C::kDataByteMask) << s; | |
| s += C::kDataBitsPerByte; | |
| } while ((b & C::kMoreDataMask) != 0); | |
| ASSERT(s < C::kDataBitsPerByte + kBitsPerT); | |
| return r; | |
| } | |
| template <typename T> | |
| C::only_if_signed<T, T> ReadLEB128() { | |
| return bit_cast<T>(ReadLEB128<typename std::make_unsigned<T>::type>()); | |
| } | |
| template <typename T> | |
| C::only_if_unsigned<T, T> ReadSLEB128() { | |
| constexpr intptr_t kBitsPerT = kBitsPerByte * sizeof(T); | |
| T r = 0; | |
| uint8_t s = 0; | |
| uint8_t b; | |
| do { | |
| ASSERT(s < kBitsPerT); | |
| b = ReadByte(); | |
| r |= static_cast<T>(b & C::kDataByteMask) << s; | |
| s += C::kDataBitsPerByte; | |
| } while ((b & C::kMoreDataMask) != 0); | |
| ASSERT(s < C::kDataBitsPerByte + kBitsPerT); | |
| // At this point, [s] contains how many data bits have made it into the | |
| // value. If the value is negative and the count of data bits is less than | |
| // the size of the value, then we need to extend the sign by setting the | |
| // remaining (unset) most significant bits (MSBs). | |
| T sign_bits = 0; | |
| if ((b & C::kSignMask) != 0 && s < kBitsPerT) { | |
| // Create a bitmask for the current data bits and invert it. | |
| sign_bits = ~((static_cast<T>(1) << s) - 1); | |
| } | |
| return r | sign_bits; | |
| } | |
| template <typename T = intptr_t> | |
| C::only_if_signed<T, T> ReadSLEB128() { | |
| return bit_cast<T>(ReadSLEB128<typename std::make_unsigned<T>::type>()); | |
| } | |
| private: | |
| uint16_t Read16() { return Read16(kEndByteMarker); } | |
| uint32_t Read32() { return Read32(kEndByteMarker); } | |
| uint64_t Read64() { return Read64(kEndByteMarker); } | |
| template <typename T> | |
| T Read(uint8_t end_byte_marker) { | |
| using Unsigned = typename std::make_unsigned<T>::type; | |
| Unsigned b = ReadByte(); | |
| if (b > kMaxUnsignedDataPerByte) { | |
| return b - end_byte_marker; | |
| } | |
| T r = 0; | |
| uint8_t s = 0; | |
| do { | |
| r |= static_cast<Unsigned>(b) << s; | |
| s += kDataBitsPerByte; | |
| b = ReadByte(); | |
| } while (b <= kMaxUnsignedDataPerByte); | |
| return r | (static_cast<Unsigned>(b - end_byte_marker) << s); | |
| } | |
| // Setting up needed variables for the unrolled loop sections below. | |
| #define UNROLLED_INIT() \ | |
| using Unsigned = typename std::make_unsigned<T>::type; \ | |
| Unsigned b = ReadByte(); \ | |
| if (b > kMaxUnsignedDataPerByte) { \ | |
| return b - end_byte_marker; \ | |
| } \ | |
| T r = b; | |
| // Part of the unrolled loop where the loop may stop, having read the last part, | |
| // or continue reading. | |
| #define UNROLLED_BODY(bit_start) \ | |
| static_assert(bit_start % kDataBitsPerByte == 0, \ | |
| "Bit start must be a multiple of the data bits per byte"); \ | |
| static_assert(bit_start >= 0 && bit_start < kBitsPerByte * sizeof(T), \ | |
| "Starting unrolled body at invalid bit position"); \ | |
| static_assert(bit_start + kDataBitsPerByte < kBitsPerByte * sizeof(T), \ | |
| "Unrolled body should not contain final bits in value"); \ | |
| b = ReadByte(); \ | |
| if (b > kMaxUnsignedDataPerByte) { \ | |
| return r | (static_cast<T>(b - end_byte_marker) << bit_start); \ | |
| } \ | |
| r |= b << bit_start; | |
| // The end of the unrolled loop. | |
| #define UNROLLED_END(bit_start) \ | |
| static_assert(bit_start % kDataBitsPerByte == 0, \ | |
| "Bit start must be a multiple of the data bits per byte"); \ | |
| static_assert(bit_start >= 0 && bit_start < kBitsPerByte * sizeof(T), \ | |
| "Starting unrolled end at invalid bit position"); \ | |
| static_assert(bit_start + kDataBitsPerByte >= kBitsPerByte * sizeof(T), \ | |
| "Unrolled end does not contain final bits in value"); \ | |
| b = ReadByte(); \ | |
| ASSERT(b > kMaxUnsignedDataPerByte); \ | |
| return r | (static_cast<T>(b - end_byte_marker) << bit_start); | |
| uint16_t Read16(uint8_t end_byte_marker) { | |
| using T = uint16_t; | |
| UNROLLED_INIT(); | |
| UNROLLED_BODY(7); | |
| UNROLLED_END(14); | |
| } | |
| uint32_t Read32(uint8_t end_byte_marker) { | |
| using T = uint32_t; | |
| UNROLLED_INIT(); | |
| UNROLLED_BODY(7); | |
| UNROLLED_BODY(14); | |
| UNROLLED_BODY(21); | |
| UNROLLED_END(28); | |
| } | |
| uint64_t Read64(uint8_t end_byte_marker) { | |
| using T = uint64_t; | |
| UNROLLED_INIT(); | |
| UNROLLED_BODY(7); | |
| UNROLLED_BODY(14); | |
| UNROLLED_BODY(21); | |
| UNROLLED_BODY(28); | |
| UNROLLED_BODY(35); | |
| UNROLLED_BODY(42); | |
| UNROLLED_BODY(49); | |
| UNROLLED_BODY(56); | |
| UNROLLED_END(63); | |
| } | |
| DART_FORCE_INLINE uint8_t ReadByte() { | |
| ASSERT(current_ < end_); | |
| return *current_++; | |
| } | |
| private: | |
| ReadStream(const uint8_t* buffer, const uint8_t* current, const uint8_t* end) | |
| : buffer_(buffer), current_(current), end_(end) {} | |
| const uint8_t* buffer_; | |
| const uint8_t* current_; | |
| const uint8_t* end_; | |
| friend class Deserializer; | |
| friend class module_snapshot::Deserializer; | |
| DISALLOW_COPY_AND_ASSIGN(ReadStream); | |
| }; | |
| // Base class for streams that write bytes to some backing store. Generally, | |
| // BaseWriteStream is a more appropriate superclass of new WriteStreams since | |
| // it offers more functionality (e.g., Printf). | |
| struct AbstractWriteStream : public ValueObject { | |
| AbstractWriteStream() {} | |
| virtual ~AbstractWriteStream() {} | |
| virtual intptr_t Position() const = 0; | |
| virtual intptr_t Align(intptr_t alignment, | |
| intptr_t offset = 0, | |
| uint8_t fill_byte = 0) = 0; | |
| virtual void WriteBytes(const void* addr, intptr_t len) = 0; | |
| virtual void WriteByte(uint8_t value) = 0; | |
| private: | |
| using C = LEB128Constants; | |
| public: | |
| template <typename T> | |
| C::only_if_unsigned<T, void> WriteLEB128(T value) { | |
| T remainder = value; | |
| bool is_last_part; | |
| do { | |
| uint8_t part = static_cast<uint8_t>(remainder & C::kDataByteMask); | |
| remainder >>= C::kDataBitsPerByte; | |
| // For unsigned types, we're done when the remainder has no bits set. | |
| is_last_part = remainder == static_cast<T>(0); | |
| if (!is_last_part) { | |
| // Mark this part as a non-final part for this value. | |
| part |= C::kMoreDataMask; | |
| } | |
| WriteByte(part); | |
| } while (!is_last_part); | |
| } | |
| template <typename T> | |
| C::only_if_signed<T, void> WriteLEB128(T value) { | |
| // If we're trying to LEB128 encode a negative value, chances are we should | |
| // be using SLEB128 instead. | |
| ASSERT(value >= 0); | |
| return WriteLEB128(bit_cast<typename std::make_unsigned<T>::type>(value)); | |
| } | |
| void WriteFixed5LEB128(intptr_t value) { | |
| ASSERT(value >= 0); | |
| WriteByte((value & C::kDataByteMask) | C::kMoreDataMask); | |
| value >>= C::kDataBitsPerByte; | |
| WriteByte((value & C::kDataByteMask) | C::kMoreDataMask); | |
| value >>= C::kDataBitsPerByte; | |
| WriteByte((value & C::kDataByteMask) | C::kMoreDataMask); | |
| value >>= C::kDataBitsPerByte; | |
| WriteByte((value & C::kDataByteMask) | C::kMoreDataMask); | |
| value >>= C::kDataBitsPerByte; | |
| WriteByte((value & C::kDataByteMask)); | |
| value >>= C::kDataBitsPerByte; | |
| ASSERT(value == 0); | |
| } | |
| template <typename T> | |
| C::only_if_signed<T, void> WriteSLEB128(T value) { | |
| constexpr intptr_t kBitsPerT = kBitsPerByte * sizeof(T); | |
| using Unsigned = typename std::make_unsigned<T>::type; | |
| // Record whether the original value was negative. | |
| const bool is_negative = value < 0; | |
| T remainder = value; | |
| bool is_last_part; | |
| do { | |
| uint8_t part = static_cast<uint8_t>(remainder & C::kDataByteMask); | |
| remainder >>= C::kDataBitsPerByte; | |
| // For signed types, we're done when either: | |
| // - the remainder has all bits set and the part's sign bit is set | |
| // for negative values, or | |
| // - the remainder has no bits set and the part's sign bit is unset for | |
| // non-negative values. | |
| // If the remainder matches but the sign bit does not, we need one more | |
| // part to set the sign bit correctly when decoding. | |
| if (is_negative) { | |
| // Right shifts of negative values in C are not guaranteed to be | |
| // arithmetic. For negative values, set the [kDataBitsPerByte] most | |
| // significant bits after shifting to ensure the value stays negative. | |
| constexpr intptr_t preserved_bits = kBitsPerT - C::kDataBitsPerByte; | |
| // The sign extension mask is the inverse of the preserved bits mask. | |
| constexpr T sign_extend = | |
| ~static_cast<T>((static_cast<Unsigned>(1) << preserved_bits) - 1); | |
| // Sign extend for negative values just in case a non-arithmetic right | |
| // shift is used by the compiler. | |
| remainder |= sign_extend; | |
| ASSERT(remainder < 0); // Remainder should still be negative. | |
| is_last_part = | |
| remainder == ~static_cast<T>(0) && (part & C::kSignMask) != 0; | |
| } else { | |
| ASSERT(remainder >= 0); // Remainder should still be non-negative. | |
| is_last_part = | |
| (remainder == static_cast<T>(0) && (part & C::kSignMask) == 0); | |
| } | |
| if (!is_last_part) { | |
| // Mark this part as a non-final part for this value. | |
| part |= C::kMoreDataMask; | |
| } | |
| WriteByte(part); | |
| } while (!is_last_part); | |
| } | |
| template <typename T> | |
| C::only_if_unsigned<T, void> WriteSLEB128(T value) { | |
| return WriteSLEB128(bit_cast<typename std::make_signed<T>::type>(value)); | |
| } | |
| }; | |
| // Base class for streams that writing various types into a buffer, possibly | |
| // flushing data out periodically to a more permanent store. | |
| class BaseWriteStream : public AbstractWriteStream { | |
| public: | |
| explicit BaseWriteStream(intptr_t initial_size) | |
| : initial_size_(Utils::RoundUpToPowerOfTwo(initial_size)) {} | |
| DART_FORCE_INLINE intptr_t bytes_written() const { return Position(); } | |
| virtual intptr_t Position() const { return current_ - buffer_; } | |
| intptr_t initial_size() const { return initial_size_; } | |
| virtual intptr_t Align(intptr_t alignment, | |
| intptr_t offset = 0, | |
| uint8_t fill_byte = 0) { | |
| const intptr_t position_before = Position(); | |
| const intptr_t position_after = | |
| Utils::RoundUp(position_before, alignment, offset); | |
| const intptr_t length = position_after - position_before; | |
| if (length != 0) { | |
| EnsureSpace(length); | |
| memset(current_, fill_byte, length); | |
| SetPosition(position_after); | |
| } | |
| return length; | |
| } | |
| template <int N, typename T> | |
| class Raw {}; | |
| template <typename T> | |
| class Raw<1, T> { | |
| public: | |
| static void Write(BaseWriteStream* st, T value) { | |
| st->WriteByte(bit_cast<uint8_t>(value)); | |
| } | |
| }; | |
| template <typename T> | |
| class Raw<2, T> { | |
| public: | |
| static void Write(BaseWriteStream* st, T value) { | |
| st->Write<int16_t>(bit_cast<int16_t>(value)); | |
| } | |
| }; | |
| template <typename T> | |
| class Raw<4, T> { | |
| public: | |
| static void Write(BaseWriteStream* st, T value) { | |
| st->Write<int32_t>(bit_cast<int32_t>(value)); | |
| } | |
| }; | |
| template <typename T> | |
| class Raw<8, T> { | |
| public: | |
| static void Write(BaseWriteStream* st, T value) { | |
| st->Write<int64_t>(bit_cast<int64_t>(value)); | |
| } | |
| }; | |
| void WriteWordWith32BitWrites(uword value) { | |
| constexpr intptr_t kNumWrite32PerWord = kBitsPerWord / kBitsPerInt32; | |
| const uint32_t mask = Utils::NBitMask(kBitsPerInt32); | |
| for (intptr_t j = 0; j < kNumWrite32PerWord; j++) { | |
| const uint32_t shifted_value = (value >> (j * kBitsPerInt32)); | |
| Raw<kInt32Size, uint32_t>::Write(this, shifted_value & mask); | |
| } | |
| } | |
| template <typename T> | |
| void WriteUnsigned(T value) { | |
| ASSERT(value >= 0); | |
| while (value > kMaxUnsignedDataPerByte) { | |
| WriteByte(static_cast<uint8_t>(value & kByteMask)); | |
| value = value >> kDataBitsPerByte; | |
| } | |
| WriteByte(static_cast<uint8_t>(value + kEndUnsignedByteMarker)); | |
| } | |
| void WriteRefId(intptr_t value) { | |
| ASSERT(Utils::IsUint(28, value)); // 256MB is enough for anyone... | |
| EnsureSpace(4); | |
| if ((value >> 21) != 0) { | |
| *current_++ = (value >> 21) & 127; | |
| } | |
| if ((value >> 14) != 0) { | |
| *current_++ = (value >> 14) & 127; | |
| } | |
| if ((value >> 7) != 0) { | |
| *current_++ = (value >> 7) & 127; | |
| } | |
| *current_++ = ((value >> 0) & 127) | 128; | |
| } | |
| void WriteBytes(const void* addr, intptr_t len) { | |
| if (len != 0) { | |
| EnsureSpace(len); | |
| memmove(current_, addr, len); | |
| current_ += len; | |
| } | |
| } | |
| void WriteWord(uword value) { WriteFixed(value); } | |
| void WriteTargetWord(word value); | |
| void Printf(const char* format, ...) PRINTF_ATTRIBUTE(2, 3) { | |
| va_list args; | |
| va_start(args, format); | |
| VPrintf(format, args); | |
| va_end(args); | |
| } | |
| void VPrintf(const char* format, va_list args) { | |
| // Measure. | |
| va_list measure_args; | |
| va_copy(measure_args, args); | |
| intptr_t len = Utils::VSNPrint(nullptr, 0, format, measure_args); | |
| va_end(measure_args); | |
| // Alloc. | |
| EnsureSpace(len + 1); | |
| // Print. | |
| va_list print_args; | |
| va_copy(print_args, args); | |
| Utils::VSNPrint(reinterpret_cast<char*>(current_), len + 1, format, | |
| print_args); | |
| va_end(print_args); | |
| current_ += len; // Not len + 1 to swallow the terminating NUL. | |
| } | |
| template <typename T> | |
| void Write(T value) { | |
| T v = value; | |
| while (v < kMinDataPerByte || v > kMaxDataPerByte) { | |
| WriteByte(static_cast<uint8_t>(v & kByteMask)); | |
| v = v >> kDataBitsPerByte; | |
| } | |
| WriteByte(static_cast<uint8_t>(v + kEndByteMarker)); | |
| } | |
| template <typename T> | |
| void WriteFixed(T value) { | |
| WriteBytes(&value, sizeof(value)); | |
| } | |
| DART_FORCE_INLINE virtual void WriteByte(uint8_t value) { | |
| EnsureSpace(1); | |
| *current_++ = value; | |
| } | |
| void WriteString(const char* cstr) { WriteBytes(cstr, strlen(cstr)); } | |
| protected: | |
| void EnsureSpace(intptr_t size_needed) { | |
| if (Remaining() >= size_needed) return; | |
| intptr_t increment_size = capacity_; | |
| if (size_needed > increment_size) { | |
| increment_size = Utils::RoundUp(size_needed, initial_size_); | |
| } | |
| intptr_t new_size = capacity_ + increment_size; | |
| ASSERT(new_size > capacity_); | |
| Realloc(new_size); | |
| if (buffer_ == nullptr) { | |
| Exceptions::ThrowOOM(); | |
| } | |
| ASSERT(Remaining() >= size_needed); | |
| } | |
| virtual void SetPosition(intptr_t value) { | |
| EnsureSpace(value - BaseWriteStream::Position()); | |
| current_ = buffer_ + value; | |
| } | |
| DART_FORCE_INLINE intptr_t Remaining() const { | |
| return capacity_ - BaseWriteStream::Position(); | |
| } | |
| // Resizes the internal buffer to the requested new capacity. Should set | |
| // buffer_, capacity_, and current_ appropriately. | |
| // | |
| // Instead of templating over an Allocator (which would then cause users | |
| // of the templated class to need to be templated, etc.), we just add an | |
| // Realloc method to override appropriately in subclasses. Less flexible, | |
| // but requires less changes throughout the codebase. | |
| virtual void Realloc(intptr_t new_capacity) = 0; | |
| const intptr_t initial_size_; | |
| uint8_t* buffer_ = nullptr; | |
| uint8_t* current_ = nullptr; | |
| intptr_t capacity_ = 0; | |
| DISALLOW_COPY_AND_ASSIGN(BaseWriteStream); | |
| }; | |
| // A base class for non-streaming write streams. Since these streams are | |
| // not flushed periodically, the internal buffer contains all written data | |
| // and can be retrieved via buffer(). NonStreamingWriteStream also provides | |
| // SetPosition as part of its public API for non-sequential writing. | |
| class NonStreamingWriteStream : public BaseWriteStream { | |
| public: | |
| explicit NonStreamingWriteStream(intptr_t initial_size) | |
| : BaseWriteStream(initial_size) {} | |
| public: | |
| uint8_t* buffer() const { return buffer_; } | |
| // Sets the position of the buffer | |
| DART_FORCE_INLINE void SetPosition(intptr_t value) { | |
| BaseWriteStream::SetPosition(value); | |
| } | |
| }; | |
| // A non-streaming write stream that uses realloc for reallocation, and frees | |
| // the buffer when destructed unless ownership is transferred using Steal(). | |
| class MallocWriteStream : public NonStreamingWriteStream { | |
| public: | |
| explicit MallocWriteStream(intptr_t initial_size) | |
| : NonStreamingWriteStream(initial_size) {} | |
| ~MallocWriteStream(); | |
| // Resets the stream and returns the original buffer, which is now considered | |
| // owned by the caller. Sets [*length] to the length of the returned buffer. | |
| uint8_t* Steal(intptr_t* length) { | |
| ASSERT(length != nullptr); | |
| *length = bytes_written(); | |
| uint8_t* const old_buffer = buffer_; | |
| // We don't immediately reallocate a new space just in case this steal | |
| // is the last use of this stream. | |
| current_ = buffer_ = nullptr; | |
| capacity_ = 0; | |
| return old_buffer; | |
| } | |
| private: | |
| virtual void Realloc(intptr_t new_size); | |
| DISALLOW_COPY_AND_ASSIGN(MallocWriteStream); | |
| }; | |
| // A non-streaming write stream that uses a zone for reallocation. | |
| class ZoneWriteStream : public NonStreamingWriteStream { | |
| public: | |
| ZoneWriteStream(Zone* zone, intptr_t initial_size) | |
| : NonStreamingWriteStream(initial_size), zone_(zone) {} | |
| private: | |
| virtual void Realloc(intptr_t new_size); | |
| Zone* const zone_; | |
| DISALLOW_COPY_AND_ASSIGN(ZoneWriteStream); | |
| }; | |
| // A streaming write stream that uses the internal buffer only for non-flushed | |
| // data. Like MallocWriteStream, uses realloc for reallocation, and flushes and | |
| // frees the internal buffer when destructed. Since part or all of the written | |
| // data may be flushed and no longer in the internal buffer, it does not provide | |
| // a way to retrieve the written contents. | |
| class StreamingWriteStream : public BaseWriteStream { | |
| public: | |
| explicit StreamingWriteStream(intptr_t initial_capacity, | |
| Dart_StreamingWriteCallback callback, | |
| void* callback_data) | |
| : BaseWriteStream(initial_capacity), | |
| callback_(callback), | |
| callback_data_(callback_data) {} | |
| ~StreamingWriteStream(); | |
| private: | |
| // Flushes any unflushed data to callback_data and resets the internal | |
| // buffer. Changes current_ and flushed_size_ accordingly. | |
| virtual void Flush(); | |
| virtual void Realloc(intptr_t new_size); | |
| virtual intptr_t Position() const { | |
| return flushed_size_ + BaseWriteStream::Position(); | |
| } | |
| virtual void SetPosition(intptr_t value) { | |
| // Make sure we're not trying to set the position to already-flushed data. | |
| ASSERT(value >= flushed_size_); | |
| BaseWriteStream::SetPosition(value - flushed_size_); | |
| } | |
| const Dart_StreamingWriteCallback callback_; | |
| void* const callback_data_; | |
| intptr_t flushed_size_ = 0; | |
| DISALLOW_COPY_AND_ASSIGN(StreamingWriteStream); | |
| }; | |
| } // namespace dart | |
| #endif // RUNTIME_VM_DATASTREAM_H_ |