// SPDX-FileCopyrightText: 2025-2026 Yu-Sheng Lin // SPDX-FileCopyrightText: 2025-2026 Yoda Lee // SPDX-License-Identifier: MIT // Project: libfstwriter // Website: https://github.com/gtkwave/libfstwriter #pragma once // direct include #include "fstcpp/fstcpp.h" // C system headers // C++ standard library headers #include #include #include #include // Other libraries' .h files. // Your project's .h files. #include "fstcpp/fstcpp_assertion.h" #include "fstcpp/fstcpp_stream_write_helper.h" namespace fst { namespace platform { // Can be replaced with std::bit_width when C++20 is available inline uint64_t clog2(uint64_t x) { return 64 - __builtin_clzll(x - 1); } inline constexpr uint32_t gen_mask_safe(unsigned width) { // works even when width == 32 return ((uint32_t(1) << (width - 1)) << 1) - 1; } inline uint32_t read_field(const uint32_t src, unsigned width, unsigned offset) { const uint32_t mask = gen_mask_safe(width); return (src >> offset) & mask; } inline void write_field(uint32_t &dst, const uint32_t src, unsigned width, unsigned offset) { const uint32_t mask = gen_mask_safe(width) << offset; dst = (dst & ~mask) | ((src << offset) & mask); } } // namespace platform class VariableInfo final { static constexpr uint64_t kCapacityBaseShift = 5; static constexpr uint64_t kCapacityBase = 1 << kCapacityBaseShift; // To maximize cache efficiency, we compact the data members into 16 bytes. // We make use of bitfields to store multiple pieces of information in a single integer. // But standard does not guarantee the layout of bitfields (the `int x : N;` syntax), // so we use helper functions to access bitfields. // begin of data members // 1. 8B pointer (assume 64-bit architecture), its size can be: // - 0 if data is nullptr // - `kCapacityBase * pow(2, capacity_log2)` if data is not nullptr // - If we want more bits, we can use the `kCapacityBaseShift` LSB for other purposes. uint8_t *data = nullptr; // 2. 4B size. The same as vector.size(), but we only need 32b. uint32_t size_ = 0; // 3. 4B misc. Highly compacted information for max cache efficiency. // - 6b capacity_log2 // - 2b last_encoding_type // - 23b bitwidth // - 1b is_real // Note: optimization possibility (not implemented) // - real is always 64-bit double, so we can use 24 bits to encode // is_real and bitwidth together, and bitwidth = (1<<24-1) is a special // value to indicate that the variable is a real. // - Currently bitwidth is whatever you pass to Writer::createVar. // - Not implemented since nobody needs 16M-bit over 8M-bit bitwidth IMO. static constexpr uint32_t kIsRealWidth = 1; static constexpr uint32_t kBitwidthWidth = 23; static constexpr uint32_t kLastEncodingTypeWidth = 2; static constexpr uint32_t kCapacityLog2Width = 6; static constexpr uint32_t kIsRealOffset = 0; static constexpr uint32_t kBitwidthOffset = kIsRealOffset + kIsRealWidth; static constexpr uint32_t kLastEncodingTypeOffset = kBitwidthOffset + kBitwidthWidth; static constexpr uint32_t kCapacityLog2Offset = kLastEncodingTypeOffset + kLastEncodingTypeWidth; uint32_t misc = 0; // end of data members void capacity_log2(uint32_t capacity_log2_) { platform::write_field(misc, capacity_log2_, kCapacityLog2Width, kCapacityLog2Offset); } uint32_t capacity() const { if (data == nullptr) { return 0; } return kCapacityBase << platform::read_field(misc, kCapacityLog2Width, kCapacityLog2Offset); } inline bool need_reallocate(uint64_t new_size) const { return capacity() < new_size; } // This function is cold, so we don't inline it void reallocate(uint64_t new_size); inline void size(uint64_t s) { size_ = s; } public: static constexpr uint32_t kMaxSupportedBitwidth = 0x7fffff; inline uint64_t size() const { return size_; } inline uint32_t bitwidth() const { return platform::read_field(misc, kBitwidthWidth, kBitwidthOffset); } inline bool is_real() const { return bool(platform::read_field(misc, kIsRealWidth, kIsRealOffset)); } inline void last_written_encode_type(EncodingType encoding_) { platform::write_field( misc, static_cast(encoding_), kLastEncodingTypeWidth, kLastEncodingTypeOffset ); } inline EncodingType last_written_encode_type() const { return static_cast( platform::read_field(misc, kLastEncodingTypeWidth, kLastEncodingTypeOffset) ); } uint64_t last_written_bytes() const; template auto dispatchHelper(Callable &&callable, Args &&...args) const; VariableInfo(uint32_t bitwidth_, bool is_real_ = false); ~VariableInfo() { if (data_ptr() != nullptr) { // don't delete data directly for better abstraction // we might use the LSB of data in the future as LSB is // always aligned to kCapacityBase delete[] data_ptr(); } } VariableInfo(VariableInfo &&rhs) { data = rhs.data; rhs.data = nullptr; misc = rhs.misc; size_ = rhs.size_; // rhs.misc = 0; } uint32_t emitValueChange(uint64_t current_time_index, const uint64_t val); uint32_t emitValueChange( uint64_t current_time_index, const uint32_t *val, EncodingType encoding ); uint32_t emitValueChange( uint64_t current_time_index, const uint64_t *val, EncodingType encoding ); void keepOnlyTheLatestValue() { const auto last_written_bytes_ = last_written_bytes(); const auto data_ptr_ = data_ptr(); std::copy_n(data_ptr_ + size() - last_written_bytes_, last_written_bytes_, data_ptr_); size(last_written_bytes_); } void dumpInitialBits(std::vector &buf) const; void dumpValueChanges(std::vector &buf) const; // We only need to make this class compatible with vector // delete copy constructor and assignment operator VariableInfo(const VariableInfo &) = delete; VariableInfo &operator=(const VariableInfo &) = delete; VariableInfo &operator=(VariableInfo &&) = delete; void resize(size_t new_size) { if (need_reallocate(new_size)) { reallocate(new_size); } size(new_size); } void add_size(size_t added_size) { resize(size() + added_size); } uint8_t *data_ptr() { return data; } }; static_assert( sizeof(VariableInfo) != 12, "We don't support 32-bit architecture, comment out the assertions and take the risk" ); static_assert(sizeof(VariableInfo) == 16, "VariableInfo should be small"); namespace detail { constexpr size_t kEmitTimeIndexAndEncodingSize = sizeof(uint64_t) + sizeof(fst::EncodingType); // EmitReaderHelper and EmitWriterHelper are very optimized for emit functions // User must ensure the pointer points to the valid memory region struct EmitReaderHelper { const uint8_t *ptr; EmitReaderHelper(const uint8_t *ptr_) : ptr(ptr_) {} std::pair readTimeIndexAndEncoding() { const auto time_index = read(); const auto encoding = read(); return std::make_pair(time_index, encoding); } template T read() { const size_t s = sizeof(T); T u; std::memcpy(&u, ptr, s); ptr += s; return u; } void skip(size_t count) { ptr += count; } template T peek(size_t i = 0) { const size_t s = sizeof(T); T u; std::memcpy(&u, ptr + i * s, s); return u; } }; struct EmitWriterHelper { uint8_t *ptr; EmitWriterHelper(uint8_t *ptr_) : ptr(ptr_) {} EmitWriterHelper &writeTimeIndexAndEncoding(uint64_t time_index, fst::EncodingType encoding) { write(time_index); write(encoding); return *this; } template EmitWriterHelper &write(T u) { const size_t s = sizeof(u); std::memcpy(ptr, &u, s); ptr += s; return *this; } template EmitWriterHelper &fill(T u, size_t count) { for (size_t i = 0; i < count; ++i) { std::memcpy(ptr, &u, sizeof(u)); ptr += sizeof(u); } return *this; } template EmitWriterHelper &write(T *u, size_t size) { for (size_t i = 0; i < size; ++i) { std::memcpy(ptr, u + i, sizeof(T)); ptr += sizeof(T); } return *this; } }; class VariableInfoDouble { VariableInfo &info; public: VariableInfoDouble(VariableInfo &info_) : info(info_) {} public: inline size_t computeBytesNeeded(EncodingType encoding) const { (void)encoding; return kEmitTimeIndexAndEncodingSize + sizeof(double); } inline EmitWriterHelper emitValueChangeCommonPart( uint64_t current_time_index, EncodingType encoding ) { if (current_time_index + 1 == 0) { info.resize(0); } // For Double, value is always 8 bytes (sizeof(double) or uint64_t) const size_t added_size = computeBytesNeeded(encoding); const size_t old_size = info.size(); info.add_size(added_size); EmitWriterHelper wh(info.data_ptr() + old_size); wh.writeTimeIndexAndEncoding(current_time_index, encoding); return wh; } public: void construct() { const size_t needed = computeBytesNeeded(EncodingType::BINARY); info.resize(needed); EmitWriterHelper wh(info.data_ptr()); const double nan_val = std::numeric_limits::quiet_NaN(); const uint64_t nan_val_u64 = *reinterpret_cast(&nan_val); wh.writeTimeIndexAndEncoding(0, EncodingType::BINARY).write(nan_val_u64); } void emitValueChange(uint64_t current_time_index, const uint64_t val) { auto wh = emitValueChangeCommonPart(current_time_index, EncodingType::BINARY); std::cout << current_time_index << ": " << std::hex << val << std::endl; // Note, do not use write here since the uint64_t is // already bit_cast'ed from double wh.write(val); } // Double variables should not use these array-based emitValueChange overloads. // We implement them to satisfy the VairableInfo::dispatchHelper template instantiation. void emitValueChange(uint64_t, const uint32_t *, EncodingType) { throw std::runtime_error("emitValueChange(uint32_t*) not supported for Double"); } void emitValueChange(uint64_t, const uint64_t *, EncodingType) { throw std::runtime_error("emitValueChange(uint64_t*) not supported for Double"); } void dumpInitialBits(std::vector &buf) const { FST_DCHECK_GT(info.size(), kEmitTimeIndexAndEncodingSize); EmitReaderHelper rh(info.data_ptr()); StreamVectorWriteHelper wh(buf); (void)rh.readTimeIndexAndEncoding(); auto v = rh.read(); wh.write(v); } void dumpValueChanges(std::vector &buf) const { StreamVectorWriteHelper wh(buf); EmitReaderHelper rh(info.data_ptr()); const uint8_t *tail = info.data_ptr() + info.size(); bool first = true; uint64_t prev_time_index = 0; while (true) { if (rh.ptr == tail) break; FST_CHECK_GT(tail, rh.ptr); const auto time_index = rh.read(); const auto enc = rh.read(); const auto num_byte = sizeof(double); if (first) { // Note: [0] is initial value, which is already dumped in dumpInitialBits() first = false; } else { FST_CHECK(enc == EncodingType::BINARY); const uint64_t delta_time_index = time_index - prev_time_index; prev_time_index = time_index; // Double shall be treated as non-binary const bool has_non_binary = true; wh // .writeLEB128((delta_time_index << 1) | has_non_binary) .write(rh.peek()); } rh.skip(num_byte); } } }; template class VariableInfoScalarInt { VariableInfo &info; public: VariableInfoScalarInt(VariableInfo &info_) : info(info_) {} public: inline size_t computeBytesNeeded(EncodingType encoding) const { return kEmitTimeIndexAndEncodingSize + sizeof(T) * bitPerEncodedBit(encoding); } // The returning address points to the first byte of the value inline EmitWriterHelper emitValueChangeCommonPart( uint64_t current_time_index, EncodingType encoding ) { if (current_time_index + 1 == 0) { // This is the first value change, we need to remove everything // and then add the new value info.resize(0); } const size_t added_size = computeBytesNeeded(encoding); const size_t old_size = info.size(); info.add_size(added_size); EmitWriterHelper wh(info.data_ptr() + old_size); wh.writeTimeIndexAndEncoding(current_time_index, encoding); return wh; } public: void construct() { info.resize(computeBytesNeeded(EncodingType::VERILOG)); EmitWriterHelper wh(info.data_ptr()); wh.writeTimeIndexAndEncoding(0, EncodingType::VERILOG).write(T(0)).write(T(-1)); } void emitValueChange(uint64_t current_time_index, const uint64_t val) { auto wh = emitValueChangeCommonPart(current_time_index, EncodingType::BINARY); wh.template write(val); } void emitValueChange(uint64_t current_time_index, const uint32_t *val, EncodingType encoding) { auto wh = emitValueChangeCommonPart(current_time_index, encoding); for (unsigned i = 0; i < bitPerEncodedBit(encoding); ++i) { // C++17: replace this with if constexpr if (sizeof(T) == 8) { uint64_t v = val[1]; // high bits v <<= 32; v |= val[0]; // low bits wh.template write(v); val += 2; } else { wh.template write(val[0]); val += 1; } } } void emitValueChange(uint64_t current_time_index, const uint64_t *val, EncodingType encoding) { auto wh = emitValueChangeCommonPart(current_time_index, encoding); for (unsigned i = 0; i < bitPerEncodedBit(encoding); ++i) { wh.template write(val[i]); } } void dumpInitialBits(std::vector &buf) const { // FST requires initial bits present FST_DCHECK_GT(info.size(), kEmitTimeIndexAndEncodingSize); EmitReaderHelper rh(info.data_ptr()); const auto time_index_enc = rh.readTimeIndexAndEncoding(); const auto enc = time_index_enc.second; const auto bitwidth = info.bitwidth(); switch (enc) { case EncodingType::BINARY: { auto v0 = rh.read(); for (unsigned i = bitwidth; i-- > 0;) { const char c = ((v0 >> i) & T(1)) ? '1' : '0'; buf.push_back(c); } break; } case EncodingType::VERILOG: { auto v0 = rh.read(); auto v1 = rh.read(); for (unsigned i = bitwidth; i-- > 0;) { const T b1 = ((v1 >> i) & T(1)); const T b0 = ((v0 >> i) & T(1)); const char c = kEncodedBitToCharTable[(b1 << 1) | b0]; buf.push_back(c); } break; } // Not supporting VHDL now // LCOV_EXCL_START default: case EncodingType::VHDL: { auto v0 = rh.read(); auto v1 = rh.read(); auto v2 = rh.read(); for (unsigned i = bitwidth; i-- > 0;) { const T b2 = ((v2 >> i) & T(1)); const T b1 = ((v1 >> i) & T(1)); const T b0 = ((v0 >> i) & T(1)); const char c = kEncodedBitToCharTable[(b2 << 2) | (b1 << 1) | b0]; buf.push_back(c); } break; } } // LCOV_EXCL_STOP } void dumpValueChanges(std::vector &buf) const { StreamVectorWriteHelper h(buf); EmitReaderHelper rh(info.data_ptr()); const uint8_t *tail = info.data_ptr() + info.size(); const auto bitwidth = info.bitwidth(); bool first = true; uint64_t prev_time_index = 0; if (bitwidth == 1) { while (true) { if (rh.ptr == tail) { break; } FST_DCHECK_GT(tail, rh.ptr); const auto time_index = rh.read(); const auto enc = rh.read(); const auto num_element = bitPerEncodedBit(enc); const auto num_byte = num_element * sizeof(T); if (first) { // Note: [0] is initial value, which is already dumped in dumpInitialBits() first = false; } else { unsigned val = 0; for (unsigned i = 0; i < num_element; ++i) { val |= rh.peek(i); } uint64_t delta_time_index = time_index - prev_time_index; prev_time_index = time_index; switch (val) { // clang-format off case 0: delta_time_index = (delta_time_index<<2) | (0<<1) | 0; break; // '0' case 1: delta_time_index = (delta_time_index<<2) | (1<<1) | 0; break; // '1' case 2: delta_time_index = (delta_time_index<<4) | (0<<1) | 1; break; // 'X' case 3: delta_time_index = (delta_time_index<<4) | (1<<1) | 1; break; // 'Z' // Not supporting VHDL now // LCOV_EXCL_START case 4: delta_time_index = (delta_time_index<<4) | (2<<1) | 1; break; // 'H' case 5: delta_time_index = (delta_time_index<<4) | (3<<1) | 1; break; // 'U' case 6: delta_time_index = (delta_time_index<<4) | (4<<1) | 1; break; // 'W' case 7: delta_time_index = (delta_time_index<<4) | (5<<1) | 1; break; // 'L' case 8: delta_time_index = (delta_time_index<<4) | (6<<1) | 1; break; // '-' case 9: delta_time_index = (delta_time_index<<4) | (7<<1) | 1; break; // '?' default: break; // LCOV_EXCL_STOP // clang-format on } h.writeLEB128(delta_time_index); } rh.skip(num_byte); } } else { while (true) { if (rh.ptr == tail) { break; } FST_CHECK_GT(tail, rh.ptr); const auto time_index = rh.read(); const auto enc = rh.read(); const auto num_element = bitPerEncodedBit(enc); const auto num_byte = num_element * sizeof(T); if (first) { first = false; } else { FST_CHECK(enc == EncodingType::BINARY); // TODO const bool has_non_binary = enc != EncodingType::BINARY; const uint64_t delta_time_index = time_index - prev_time_index; prev_time_index = time_index; h // .writeLEB128((delta_time_index << 1) | has_non_binary) .writeUIntPartialForValueChange(rh.peek(), bitwidth); } rh.skip(num_byte); } } } }; class VariableInfoLongInt { VariableInfo &info; unsigned num_words() const { return (info.bitwidth() + 63) / 64; } public: VariableInfoLongInt(VariableInfo &info_) : info(info_) {} public: inline size_t computeBytesNeeded(EncodingType encoding) const { return ( kEmitTimeIndexAndEncodingSize + num_words() * sizeof(uint64_t) * bitPerEncodedBit(encoding) ); } inline EmitWriterHelper emitValueChangeCommonPart( uint64_t current_time_index, EncodingType encoding ) { if (current_time_index + 1 == 0) { info.resize(0); } const size_t added_size = computeBytesNeeded(encoding); const size_t old_size = info.size(); info.add_size(added_size); EmitWriterHelper wh(info.data_ptr() + old_size); wh.writeTimeIndexAndEncoding(current_time_index, encoding); return wh; } public: void construct() { const size_t nw = num_words(); info.resize(computeBytesNeeded(EncodingType::VERILOG)); EmitWriterHelper wh(info.data_ptr()); wh // .writeTimeIndexAndEncoding(0, EncodingType::VERILOG) .fill(uint64_t(0), nw) .fill(uint64_t(-1), nw); } void emitValueChange(uint64_t current_time_index, const uint64_t val) { const unsigned nw = num_words(); auto wh = emitValueChangeCommonPart(current_time_index, EncodingType::BINARY); wh.write(val).fill(uint64_t(0), nw - 1); } void emitValueChange(uint64_t current_time_index, const uint32_t *val, EncodingType encoding) { const unsigned nw32 = (info.bitwidth() + 31) / 32; const unsigned bpb = bitPerEncodedBit(encoding); auto wh = emitValueChangeCommonPart(current_time_index, encoding); for (unsigned i = 0; i < bpb; ++i) { for (unsigned j = 0; j < nw32 / 2; ++j) { uint64_t v = val[1]; // high bits v <<= 32; v |= val[0]; // low bits wh.write(v); val += 2; } if (nw32 % 2 != 0) { uint64_t v = val[0]; wh.write(v); val += 1; } } } void emitValueChange(uint64_t current_time_index, const uint64_t *val, EncodingType encoding) { const unsigned nw_encoded = num_words() * bitPerEncodedBit(encoding); auto wh = emitValueChangeCommonPart(current_time_index, encoding); wh.write(val, nw_encoded); } void dumpInitialBits(std::vector &buf) const { FST_DCHECK_GT(info.size(), kEmitTimeIndexAndEncodingSize); EmitReaderHelper rh(info.data_ptr()); const auto time_index_enc = rh.readTimeIndexAndEncoding(); const auto enc = time_index_enc.second; const unsigned nw = num_words(); switch (enc) { case EncodingType::BINARY: { for (unsigned word_index = nw; word_index-- > 0;) { const uint64_t v0 = rh.peek(word_index); const unsigned num_bit = (word_index * 64 + 64 > info.bitwidth()) ? (info.bitwidth() % 64) : 64; for (unsigned bit_index = num_bit; bit_index-- > 0;) { const char c = ((v0 >> bit_index) & uint64_t(1)) ? '1' : '0'; buf.push_back(c); } } break; } case EncodingType::VERILOG: { for (unsigned word_index = nw; word_index-- > 0;) { const uint64_t v0 = rh.peek(nw * 0 + word_index); const uint64_t v1 = rh.peek(nw * 1 + word_index); const unsigned num_bit = (word_index * 64 + 64 > info.bitwidth()) ? (info.bitwidth() % 64) : 64; for (unsigned bit_index = num_bit; bit_index-- > 0;) { const bool b0 = ((v0 >> bit_index) & uint64_t(1)); const bool b1 = ((v1 >> bit_index) & uint64_t(1)); const char c = kEncodedBitToCharTable[(b1 << 1) | b0]; buf.push_back(c); } } break; } default: case EncodingType::VHDL: { // Not supporting VHDL now // LCOV_EXCL_START for (unsigned word_index = nw; word_index-- > 0;) { const uint64_t v0 = rh.peek(nw * 0 + word_index); const uint64_t v1 = rh.peek(nw * 1 + word_index); const uint64_t v2 = rh.peek(nw * 2 + word_index); const unsigned num_bit = (word_index * 64 + 64 > info.bitwidth()) ? (info.bitwidth() % 64) : 64; for (unsigned bit_index = num_bit; bit_index-- > 0;) { const bool b0 = ((v0 >> bit_index) & uint64_t(1)); const bool b1 = ((v1 >> bit_index) & uint64_t(1)); const bool b2 = ((v2 >> bit_index) & uint64_t(1)); const char c = kEncodedBitToCharTable[(b2 << 2) | (b1 << 1) | b0]; buf.push_back(c); } } break; // LCOV_EXCL_STOP } rh.skip(sizeof(uint64_t) * nw * bitPerEncodedBit(enc)); } } void dumpValueChanges(std::vector &buf) const { StreamVectorWriteHelper h(buf); EmitReaderHelper rh(info.data_ptr()); const uint8_t *tail = info.data_ptr() + info.size(); const unsigned nw = num_words(); const unsigned bitwidth = info.bitwidth(); // Local copy for lambda capture/usage if needed bool first = true; uint64_t prev_time_index = 0; while (true) { if (rh.ptr == tail) break; FST_DCHECK_GT(tail, rh.ptr); const auto time_index = rh.read(); const auto enc = rh.read(); const auto num_element = bitPerEncodedBit(enc); const auto num_byte = num_element * nw * sizeof(uint64_t); if (first) { // Note: [0] is initial value, which is already dumped in dumpInitialBits() first = false; } else { FST_CHECK(enc == EncodingType::BINARY); // TODO const bool has_non_binary = enc != EncodingType::BINARY; const uint64_t delta_time_index = time_index - prev_time_index; prev_time_index = time_index; h.writeLEB128((delta_time_index << 1) | has_non_binary); if (bitwidth % 64 != 0) { const unsigned remaining = bitwidth % 64; uint64_t hi64 = rh.peek(nw - 1); // write from nw-1 to 1 for (unsigned j = nw - 1; j > 0; --j) { uint64_t lo64 = rh.peek(j - 1); h.writeUIntBE((hi64 << (64 - remaining)) | (lo64 >> remaining)); hi64 = lo64; } // write 0 h.writeUIntPartialForValueChange(hi64, remaining); } else { // write from nw-1 to 0 for (unsigned j = nw; j-- > 0;) { h.writeUIntBE(rh.peek(j)); } } } rh.skip(num_byte); } } }; } // namespace detail template auto VariableInfo::dispatchHelper(Callable &&callable, Args &&...args) const { const auto bitwidth = this->bitwidth(); const auto is_real = this->is_real(); if (not is_real) { // Decision: the branch miss is too expensive for large design, so we only use 3 types of // int if (bitwidth <= 8) { return callable( detail::VariableInfoScalarInt(const_cast(*this)), std::forward(args)... ); // } else if (bitwidth <= 16) { // return // callable(detail::VariableInfoScalarInt(const_cast(*this)), // std::forward(args)...); } else if (bitwidth <= 32) { return // callable(detail::VariableInfoScalarInt(const_cast(*this)), // std::forward(args)...); } else if (bitwidth <= 64) { return callable( detail::VariableInfoScalarInt(const_cast(*this)), std::forward(args)... ); } else { return callable( detail::VariableInfoLongInt(const_cast(*this)), std::forward(args)... ); } } return callable( detail::VariableInfoDouble(const_cast(*this)), std::forward(args)... ); } inline VariableInfo::VariableInfo(uint32_t bitwidth_, bool is_real_) { platform::write_field(misc, bitwidth_, kBitwidthWidth, kBitwidthOffset); platform::write_field(misc, is_real_, kIsRealWidth, kIsRealOffset); dispatchHelper([](auto obj) { obj.construct(); }); last_written_encode_type(EncodingType::BINARY); } inline uint32_t VariableInfo::emitValueChange(uint64_t current_time_index, const uint64_t val) { const auto old_size = size(); dispatchHelper([=](auto obj) { obj.emitValueChange(current_time_index, val); }); last_written_encode_type(EncodingType::BINARY); return size() - old_size; } inline uint32_t VariableInfo::emitValueChange( uint64_t current_time_index, const uint32_t *val, EncodingType encoding ) { const auto old_size = size(); dispatchHelper([=](auto obj) { obj.emitValueChange(current_time_index, val, encoding); }); last_written_encode_type(encoding); return size() - old_size; } inline uint32_t VariableInfo::emitValueChange( uint64_t current_time_index, const uint64_t *val, EncodingType encoding ) { const auto old_size = size(); dispatchHelper([=](auto obj) { obj.emitValueChange(current_time_index, val, encoding); }); last_written_encode_type(encoding); return size() - old_size; } inline void VariableInfo::dumpInitialBits(std::vector &buf) const { dispatchHelper([&](auto obj) { obj.dumpInitialBits(buf); }); } inline void VariableInfo::dumpValueChanges(std::vector &buf) const { dispatchHelper([&](auto obj) { obj.dumpValueChanges(buf); }); } inline uint64_t VariableInfo::last_written_bytes() const { const auto encoding = last_written_encode_type(); return dispatchHelper([encoding](auto obj) { return obj.computeBytesNeeded(encoding); }); } } // namespace fst