Added bit stream utils
Also added generalized zigzag coding.
- Due to signed integers being mostly non-negative, improved zigzag coding
to favor positive values.
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index 2214fee..61671e3 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -190,6 +190,7 @@
${spirv-tools_SOURCE_DIR}/include/spirv-tools/libspirv.h
${CMAKE_CURRENT_SOURCE_DIR}/util/bitutils.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/util/bit_stream.h
${CMAKE_CURRENT_SOURCE_DIR}/util/hex_float.h
${CMAKE_CURRENT_SOURCE_DIR}/util/parse_number.h
${CMAKE_CURRENT_SOURCE_DIR}/util/string_utils.h
@@ -218,6 +219,7 @@
${CMAKE_CURRENT_SOURCE_DIR}/text_handler.h
${CMAKE_CURRENT_SOURCE_DIR}/validate.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/util/bit_stream.cpp
${CMAKE_CURRENT_SOURCE_DIR}/util/parse_number.cpp
${CMAKE_CURRENT_SOURCE_DIR}/util/string_utils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/assembly_grammar.cpp
diff --git a/source/util/bit_stream.cpp b/source/util/bit_stream.cpp
new file mode 100644
index 0000000..5dac563
--- /dev/null
+++ b/source/util/bit_stream.cpp
@@ -0,0 +1,387 @@
+// Copyright (c) 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+#include <sstream>
+#include <type_traits>
+
+#include "util/bit_stream.h"
+
+namespace spvutils {
+
+namespace {
+
+// Returns if the system is little-endian. Unfortunately only works during
+// runtime.
+bool IsLittleEndian() {
+ // This constant value allows the detection of the host machine's endianness.
+ // Accessing it as an array of bytes is valid due to C++11 section 3.10
+ // paragraph 10.
+ static const uint16_t kFF00 = 0xff00;
+ return reinterpret_cast<const unsigned char*>(&kFF00)[0] == 0;
+}
+
+// Copies uint8_t buffer to a uint64_t buffer.
+// Motivation: casting uint64_t* to uint8_t* is ok. Casting in the other
+// direction is only advisable if uint8_t* is aligned to 64-bit word boundary.
+std::vector<uint64_t> ToBuffer64(const std::vector<uint8_t>& in) {
+ std::vector<uint64_t> out;
+ out.resize((in.size() + 7) / 8, 0);
+ memcpy(out.data(), in.data(), in.size());
+ return out;
+}
+
+// Returns uint64_t containing the same bits as |val|.
+// Type size must be less than 8 bytes.
+template <typename T>
+uint64_t ToU64(T val) {
+ static_assert(sizeof(T) <= 8, "Type size too big");
+ uint64_t val64 = 0;
+ std::memcpy(&val64, &val, sizeof(T));
+ return val64;
+}
+
+// Returns value of type T containing the same bits as |val64|.
+// Type size must be less than 8 bytes. Upper (unused) bits of |val64| must be
+// zero (irrelevant, but is checked with assertion).
+template <typename T>
+T FromU64(uint64_t val64) {
+ assert(sizeof(T) == 8 || (val64 >> (sizeof(T) * 8)) == 0);
+ static_assert(sizeof(T) <= 8, "Type size too big");
+ T val = 0;
+ std::memcpy(&val, &val64, sizeof(T));
+ return val;
+}
+
+// Writes bits from |val| to |writer| in chunks of size |chunk_length|.
+// Signal bit is used to signal if the reader should expect another chunk:
+// 0 - no more chunks to follow
+// 1 - more chunks to follow
+// If number of written bits reaches |max_payload| last chunk is truncated.
+void WriteVariableWidthInternal(BitWriterInterface* writer, uint64_t val,
+ size_t chunk_length, size_t max_payload) {
+ assert(chunk_length > 0);
+ assert(chunk_length < max_payload);
+ assert(max_payload == 64 || (val >> max_payload) == 0);
+
+ if (val == 0) {
+ writer->WriteBits(0, chunk_length + 1);
+ return;
+ }
+
+ size_t payload_written = 0;
+
+ while (val) {
+ if (payload_written + chunk_length >= max_payload) {
+ // This has to be the last chunk.
+ // There is no need for the signal bit and the chunk can be truncated.
+ const size_t left_to_write = max_payload - payload_written;
+ assert((val >> left_to_write) == 0);
+ writer->WriteBits(val, left_to_write);
+ break;
+ }
+
+ writer->WriteBits(val, chunk_length);
+ payload_written += chunk_length;
+ val = val >> chunk_length;
+
+ // Write a single bit to signal if there is more to come.
+ writer->WriteBits(val ? 1 : 0, 1);
+ }
+}
+
+// Reads data written with WriteVariableWidthInternal. |chunk_length| and
+// |max_payload| should be identical to those used to write the data.
+// Returns false if the stream ends prematurely.
+bool ReadVariableWidthInternal(BitReaderInterface* reader, uint64_t* val,
+ size_t chunk_length, size_t max_payload) {
+ assert(chunk_length > 0);
+ assert(chunk_length <= max_payload);
+ size_t payload_read = 0;
+
+ while (payload_read + chunk_length < max_payload) {
+ uint64_t bits = 0;
+ if (reader->ReadBits(&bits, chunk_length) != chunk_length)
+ return false;
+
+ *val |= bits << payload_read;
+ payload_read += chunk_length;
+
+ uint64_t more_to_come = 0;
+ if (reader->ReadBits(&more_to_come, 1) != 1)
+ return false;
+
+ if (!more_to_come) {
+ return true;
+ }
+ }
+
+ // Need to read the last chunk which may be truncated. No signal bit follows.
+ uint64_t bits = 0;
+ const size_t left_to_read = max_payload - payload_read;
+ if (reader->ReadBits(&bits, left_to_read) != left_to_read)
+ return false;
+
+ *val |= bits << payload_read;
+ return true;
+}
+
+// Calls WriteVariableWidthInternal with the right max_payload argument.
+template <typename T>
+void WriteVariableWidthUnsigned(BitWriterInterface* writer, T val,
+ size_t chunk_length) {
+ static_assert(std::is_unsigned<T>::value, "Type must be unsigned");
+ static_assert(std::is_integral<T>::value, "Type must be integral");
+ WriteVariableWidthInternal(writer, val, chunk_length, sizeof(T) * 8);
+}
+
+// Calls ReadVariableWidthInternal with the right max_payload argument.
+template <typename T>
+bool ReadVariableWidthUnsigned(BitReaderInterface* reader, T* val,
+ size_t chunk_length) {
+ static_assert(std::is_unsigned<T>::value, "Type must be unsigned");
+ static_assert(std::is_integral<T>::value, "Type must be integral");
+ uint64_t val64 = 0;
+ if (!ReadVariableWidthInternal(reader, &val64, chunk_length, sizeof(T) * 8))
+ return false;
+ *val = static_cast<T>(val64);
+ assert(*val == val64);
+ return true;
+}
+
+// Encodes signed |val| to an unsigned value and calls
+// WriteVariableWidthInternal with the right max_payload argument.
+template <typename T>
+void WriteVariableWidthSigned(BitWriterInterface* writer, T val,
+ size_t chunk_length, size_t zigzag_exponent) {
+ static_assert(std::is_signed<T>::value, "Type must be signed");
+ static_assert(std::is_integral<T>::value, "Type must be integral");
+ WriteVariableWidthInternal(writer, EncodeZigZag(val, zigzag_exponent),
+ chunk_length, sizeof(T) * 8);
+}
+
+// Calls ReadVariableWidthInternal with the right max_payload argument
+// and decodes the value.
+template <typename T>
+bool ReadVariableWidthSigned(BitReaderInterface* reader, T* val,
+ size_t chunk_length, size_t zigzag_exponent) {
+ static_assert(std::is_signed<T>::value, "Type must be signed");
+ static_assert(std::is_integral<T>::value, "Type must be integral");
+ uint64_t encoded = 0;
+ if (!ReadVariableWidthInternal(reader, &encoded, chunk_length, sizeof(T) * 8))
+ return false;
+
+ const int64_t decoded = DecodeZigZag(encoded, zigzag_exponent);
+
+ *val = static_cast<T>(decoded);
+ assert(*val == decoded);
+ return true;
+}
+
+} // namespace
+
+void BitWriterInterface::WriteVariableWidthU64(uint64_t val,
+ size_t chunk_length) {
+ WriteVariableWidthUnsigned(this, val, chunk_length);
+}
+
+void BitWriterInterface::WriteVariableWidthU32(uint32_t val,
+ size_t chunk_length) {
+ WriteVariableWidthUnsigned(this, val, chunk_length);
+}
+
+void BitWriterInterface::WriteVariableWidthU16(uint16_t val,
+ size_t chunk_length) {
+ WriteVariableWidthUnsigned(this, val, chunk_length);
+}
+
+void BitWriterInterface::WriteVariableWidthU8(uint8_t val,
+ size_t chunk_length) {
+ WriteVariableWidthUnsigned(this, val, chunk_length);
+}
+
+void BitWriterInterface::WriteVariableWidthS64(int64_t val,
+ size_t chunk_length,
+ size_t zigzag_exponent) {
+ WriteVariableWidthSigned(this, val, chunk_length, zigzag_exponent);
+}
+
+void BitWriterInterface::WriteVariableWidthS32(int32_t val,
+ size_t chunk_length,
+ size_t zigzag_exponent) {
+ WriteVariableWidthSigned(this, val, chunk_length, zigzag_exponent);
+}
+
+void BitWriterInterface::WriteVariableWidthS16(int16_t val,
+ size_t chunk_length,
+ size_t zigzag_exponent) {
+ WriteVariableWidthSigned(this, val, chunk_length, zigzag_exponent);
+}
+
+void BitWriterInterface::WriteVariableWidthS8(int8_t val,
+ size_t chunk_length,
+ size_t zigzag_exponent) {
+ WriteVariableWidthSigned(this, val, chunk_length, zigzag_exponent);
+}
+
+BitWriterWord64::BitWriterWord64(size_t reserve_bits) : end_(0) {
+ buffer_.reserve(NumBitsToNumWords<64>(reserve_bits));
+}
+
+void BitWriterWord64::WriteBits(uint64_t bits, size_t num_bits) {
+ // Check that |bits| and |num_bits| are valid and consistent.
+ assert(num_bits <= 64);
+ const bool is_little_endian = IsLittleEndian();
+ assert(is_little_endian && "Big-endian architecture support not implemented");
+ if (!is_little_endian) return;
+
+ bits = GetLowerBits(bits, num_bits);
+
+ // Offset from the start of the current word.
+ const size_t offset = end_ % 64;
+
+ if (offset == 0) {
+ // If no offset, simply add |bits| as a new word to the buffer_.
+ buffer_.push_back(bits);
+ } else {
+ // Shift bits and add them to the current word after offset.
+ const uint64_t first_word = bits << offset;
+ buffer_.back() |= first_word;
+
+ // If we don't overflow to the next word, there is nothing more to do.
+
+ if (offset + num_bits > 64) {
+ // We overflow to the next word.
+ const uint64_t second_word = bits >> (64 - offset);
+ // Add remaining bits as a new word to buffer_.
+ buffer_.push_back(second_word);
+ }
+ }
+
+ // Move end_ into position for next write.
+ end_ += num_bits;
+ assert(buffer_.size() * 64 >= end_);
+}
+
+bool BitReaderInterface::ReadVariableWidthU64(uint64_t* val,
+ size_t chunk_length) {
+ return ReadVariableWidthUnsigned(this, val, chunk_length);
+}
+
+bool BitReaderInterface::ReadVariableWidthU32(uint32_t* val,
+ size_t chunk_length) {
+ return ReadVariableWidthUnsigned(this, val, chunk_length);
+}
+
+bool BitReaderInterface::ReadVariableWidthU16(uint16_t* val,
+ size_t chunk_length) {
+ return ReadVariableWidthUnsigned(this, val, chunk_length);
+}
+
+bool BitReaderInterface::ReadVariableWidthU8(uint8_t* val,
+ size_t chunk_length) {
+ return ReadVariableWidthUnsigned(this, val, chunk_length);
+}
+
+bool BitReaderInterface::ReadVariableWidthS64(int64_t* val,
+ size_t chunk_length,
+ size_t zigzag_exponent) {
+ return ReadVariableWidthSigned(this, val, chunk_length, zigzag_exponent);
+}
+
+bool BitReaderInterface::ReadVariableWidthS32(int32_t* val,
+ size_t chunk_length,
+ size_t zigzag_exponent) {
+ return ReadVariableWidthSigned(this, val, chunk_length, zigzag_exponent);
+}
+
+bool BitReaderInterface::ReadVariableWidthS16(int16_t* val,
+ size_t chunk_length,
+ size_t zigzag_exponent) {
+ return ReadVariableWidthSigned(this, val, chunk_length, zigzag_exponent);
+}
+
+bool BitReaderInterface::ReadVariableWidthS8(int8_t* val,
+ size_t chunk_length,
+ size_t zigzag_exponent) {
+ return ReadVariableWidthSigned(this, val, chunk_length, zigzag_exponent);
+}
+
+BitReaderWord64::BitReaderWord64(std::vector<uint64_t>&& buffer)
+ : buffer_(std::move(buffer)), pos_(0) {}
+
+BitReaderWord64::BitReaderWord64(const std::vector<uint8_t>& buffer)
+ : buffer_(ToBuffer64(buffer)), pos_(0) {}
+
+size_t BitReaderWord64::ReadBits(uint64_t* bits, size_t num_bits) {
+ assert(num_bits <= 64);
+ const bool is_little_endian = IsLittleEndian();
+ assert(is_little_endian && "Big-endian architecture support not implemented");
+ if (!is_little_endian) return 0;
+
+ if (ReachedEnd())
+ return 0;
+
+ // Index of the current word.
+ const size_t index = pos_ / 64;
+
+ // Bit position in the current word where we start reading.
+ const size_t offset = pos_ % 64;
+
+ // Read all bits from the current word (it might be too much, but
+ // excessive bits will be removed later).
+ *bits = buffer_[index] >> offset;
+
+ const size_t num_read_from_first_word = std::min(64 - offset, num_bits);
+ pos_ += num_read_from_first_word;
+
+ if (pos_ >= buffer_.size() * 64) {
+ // Reached end of buffer_.
+ return num_read_from_first_word;
+ }
+
+ if (offset + num_bits > 64) {
+ // Requested |num_bits| overflows to next word.
+ // Write all bits from the beginning of next word to *bits after offset.
+ *bits |= buffer_[index + 1] << (64 - offset);
+ pos_ += offset + num_bits - 64;
+ }
+
+ // We likely have written more bits than requested. Clear excessive bits.
+ *bits = GetLowerBits(*bits, num_bits);
+ return num_bits;
+}
+
+bool BitReaderWord64::ReachedEnd() const {
+ return pos_ >= buffer_.size() * 64;
+}
+
+bool BitReaderWord64::OnlyZeroesLeft() const {
+ if (ReachedEnd())
+ return true;
+
+ const size_t index = pos_ / 64;
+ if (index < buffer_.size() - 1)
+ return false;
+
+ assert(index == buffer_.size() - 1);
+
+ const size_t offset = pos_ % 64;
+ const uint64_t remaining_bits = buffer_[index] >> offset;
+ return !remaining_bits;
+}
+
+} // namespace spvutils
diff --git a/source/util/bit_stream.h b/source/util/bit_stream.h
new file mode 100644
index 0000000..a139b63
--- /dev/null
+++ b/source/util/bit_stream.h
@@ -0,0 +1,378 @@
+// Copyright (c) 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Contains utils for reading, writing and debug printing bit streams.
+
+#ifndef LIBSPIRV_UTIL_BIT_STREAM_H_
+#define LIBSPIRV_UTIL_BIT_STREAM_H_
+
+#include <bitset>
+#include <cstdint>
+#include <string>
+#include <sstream>
+#include <vector>
+
+namespace spvutils {
+
+// Terminology:
+// Bits - usually used for a uint64 word, first bit is the lowest.
+// Stream - std::string of '0' and '1', read left-to-right,
+// i.e. first bit is at the front and not at the end as in
+// std::bitset::to_string().
+// Bitset - std::bitset corresponding to uint64 bits and to reverse(stream).
+
+// Converts number of bits to a respective number of chunks of size N.
+// For example NumBitsToNumWords<8> returns how many bytes are needed to store
+// |num_bits|.
+template <size_t N>
+inline size_t NumBitsToNumWords(size_t num_bits) {
+ return (num_bits + (N - 1)) / N;
+}
+
+// Returns value of the same type as |in|, where all but the first |num_bits|
+// are set to zero.
+template <typename T>
+inline T GetLowerBits(T in, size_t num_bits) {
+ return sizeof(T) * 8 == num_bits ? in : in & T((T(1) << num_bits) - T(1));
+}
+
+// Encodes signed integer as unsigned in zigzag order:
+// 0 -> 0
+// -1 -> 1
+// 1 -> 2
+// -2 -> 3
+// 2 -> 4
+// Motivation: -1 is 0xFF...FF what doesn't work very well with
+// WriteVariableWidth which prefers to have as many 0 bits as possible.
+inline uint64_t EncodeZigZag(int64_t val) {
+ return (val << 1) ^ (val >> 63);
+}
+
+// Decodes signed integer encoded with EncodeZigZag.
+inline int64_t DecodeZigZag(uint64_t val) {
+ if (val & 1) {
+ // Negative.
+ // 1 -> -1
+ // 3 -> -2
+ // 5 -> -3
+ return -1 - (val >> 1);
+ } else {
+ // Non-negative.
+ // 0 -> 0
+ // 2 -> 1
+ // 4 -> 2
+ return val >> 1;
+ }
+}
+
+// Encodes signed integer as unsigned. This is a generalized version of
+// EncodeZigZag, designed to favor small positive numbers.
+// Values are transformed in blocks of 2^|block_exponent|.
+// If |block_exponent| is zero, then this degenerates into normal EncodeZigZag.
+// Example when |block_exponent| is 1 (return value is the index):
+// 0, 1, -1, -2, 2, 3, -3, -4, 4, 5, -5, -6, 6, 7, -7, -8
+// Example when |block_exponent| is 2:
+// 0, 1, 2, 3, -1, -2, -3, -4, 4, 5, 6, 7, -5, -6, -7, -8
+inline uint64_t EncodeZigZag(int64_t val, size_t block_exponent) {
+ assert(block_exponent < 64);
+ const uint64_t uval = static_cast<uint64_t>(val >= 0 ? val : -val - 1);
+ const uint64_t block_num = ((uval >> block_exponent) << 1) + (val >= 0 ? 0 : 1);
+ const uint64_t pos = GetLowerBits(uval, block_exponent);
+ return (block_num << block_exponent) + pos;
+}
+
+// Decodes signed integer encoded with EncodeZigZag. |block_exponent| must be
+// the same.
+inline int64_t DecodeZigZag(uint64_t val, size_t block_exponent) {
+ assert(block_exponent < 64);
+ const uint64_t block_num = val >> block_exponent;
+ const uint64_t pos = GetLowerBits(val, block_exponent);
+ if (block_num & 1) {
+ // Negative.
+ return -1LL - ((block_num >> 1) << block_exponent) - pos;
+ } else {
+ // Positive.
+ return ((block_num >> 1) << block_exponent) + pos;
+ }
+}
+
+// Converts |buffer| to a stream of '0' and '1'.
+template <typename T>
+std::string BufferToStream(const std::vector<T>& buffer) {
+ std::stringstream ss;
+ for (auto it = buffer.begin(); it != buffer.end(); ++it) {
+ std::string str = std::bitset<sizeof(T) * 8>(*it).to_string();
+ // Strings generated by std::bitset::to_string are read right to left.
+ // Reversing to left to right.
+ std::reverse(str.begin(), str.end());
+ ss << str;
+ }
+ return ss.str();
+}
+
+// Converts a left-to-right input string of '0' and '1' to a buffer of |T|
+// words.
+template <typename T>
+std::vector<T> StreamToBuffer(std::string str) {
+ // The input string is left-to-right, the input argument of std::bitset needs
+ // to right-to-left. Instead of reversing tokens, reverse the entire string
+ // and iterate tokens from end to begin.
+ std::reverse(str.begin(), str.end());
+ const int word_size = static_cast<int>(sizeof(T) * 8);
+ const int str_length = static_cast<int>(str.length());
+ std::vector<T> buffer;
+ buffer.reserve(NumBitsToNumWords<sizeof(T)>(str.length()));
+ for (int index = str_length - word_size; index >= 0; index -= word_size) {
+ buffer.push_back(static_cast<T>(std::bitset<sizeof(T) * 8>(
+ str, index, word_size).to_ullong()));
+ }
+ const size_t suffix_length = str.length() % word_size;
+ if (suffix_length != 0) {
+ buffer.push_back(static_cast<T>(std::bitset<sizeof(T) * 8>(
+ str, 0, suffix_length).to_ullong()));
+ }
+ return buffer;
+}
+
+// Adds '0' chars at the end of the string until the size is a multiple of N.
+template <size_t N>
+inline std::string PadToWord(std::string&& str) {
+ const size_t tail_length = str.size() % N;
+ if (tail_length != 0)
+ str += std::string(N - tail_length, '0');
+ return str;
+}
+
+// Adds '0' chars at the end of the string until the size is a multiple of N.
+template <size_t N>
+inline std::string PadToWord(const std::string& str) {
+ return PadToWord<N>(std::string(str));
+}
+
+// Converts a left-to-right stream of bits to std::bitset.
+template <size_t N>
+inline std::bitset<N> StreamToBitset(std::string str) {
+ std::reverse(str.begin(), str.end());
+ return std::bitset<N>(str);
+}
+
+// Converts first |num_bits| of std::bitset to a left-to-right stream of bits.
+template <size_t N>
+inline std::string BitsetToStream(const std::bitset<N>& bits, size_t num_bits = N) {
+ std::string str = bits.to_string().substr(N - num_bits);
+ std::reverse(str.begin(), str.end());
+ return str;
+}
+
+// Converts a left-to-right stream of bits to uint64.
+inline uint64_t StreamToBits(std::string str) {
+ std::reverse(str.begin(), str.end());
+ return std::bitset<64>(str).to_ullong();
+}
+
+// Converts first |num_bits| stored in uint64 to a left-to-right stream of bits.
+inline std::string BitsToStream(uint64_t bits, size_t num_bits = 64) {
+ std::bitset<64> bitset(bits);
+ return BitsetToStream(bitset, num_bits);
+}
+
+// Base class for writing sequences of bits.
+class BitWriterInterface {
+ public:
+ BitWriterInterface() {}
+ virtual ~BitWriterInterface() {}
+
+ // Writes lower |num_bits| in |bits| to the stream.
+ // |num_bits| must be no greater than 64.
+ virtual void WriteBits(uint64_t bits, size_t num_bits) = 0;
+
+ // Writes left-to-right string of '0' and '1' to stream.
+ // String length must be no greater than 64.
+ // Note: "01" will be writen as 0x2, not 0x1. The string doesn't represent
+ // numbers but a stream of bits in the order they come from encoder.
+ virtual void WriteStream(const std::string& bits) {
+ WriteBits(StreamToBits(bits), bits.length());
+ }
+
+ // Writes lower |num_bits| in |bits| to the stream.
+ // |num_bits| must be no greater than 64.
+ template <size_t N>
+ void WriteBitset(const std::bitset<N>& bits, size_t num_bits = N) {
+ WriteBits(bits.to_ullong(), num_bits);
+ }
+
+ // Writes |val| in chunks of size |chunk_length| followed by a signal bit:
+ // 0 - no more chunks to follow
+ // 1 - more chunks to follow
+ // for example 255 is encoded into 1111 1 1111 0 for chunk length 4.
+ // The last chunk can be truncated and signal bit omitted, if the entire
+ // payload (for example 16 bit for uint16_t has already been written).
+ void WriteVariableWidthU64(uint64_t val, size_t chunk_length);
+ void WriteVariableWidthU32(uint32_t val, size_t chunk_length);
+ void WriteVariableWidthU16(uint16_t val, size_t chunk_length);
+ void WriteVariableWidthU8(uint8_t val, size_t chunk_length);
+ void WriteVariableWidthS64(
+ int64_t val, size_t chunk_length, size_t zigzag_exponent);
+ void WriteVariableWidthS32(
+ int32_t val, size_t chunk_length, size_t zigzag_exponent);
+ void WriteVariableWidthS16(
+ int16_t val, size_t chunk_length, size_t zigzag_exponent);
+ void WriteVariableWidthS8(
+ int8_t val, size_t chunk_length, size_t zigzag_exponent);
+
+ // Returns number of bits written.
+ virtual size_t GetNumBits() const = 0;
+
+ // Provides direct access to the buffer data if implemented.
+ virtual const uint8_t* GetData() const {
+ return nullptr;
+ }
+
+ // Returns buffer size in bytes.
+ size_t GetDataSizeBytes() const {
+ return NumBitsToNumWords<8>(GetNumBits());
+ }
+
+ // Generates and returns byte array containing written bits.
+ virtual std::vector<uint8_t> GetDataCopy() const = 0;
+
+ BitWriterInterface(const BitWriterInterface&) = delete;
+ BitWriterInterface& operator=(const BitWriterInterface&) = delete;
+};
+
+// This class is an implementation of BitWriterInterface, using
+// std::vector<uint64_t> to store written bits.
+class BitWriterWord64 : public BitWriterInterface {
+ public:
+ explicit BitWriterWord64(size_t reserve_bits = 64);
+
+ void WriteBits(uint64_t bits, size_t num_bits) override;
+
+ size_t GetNumBits() const override {
+ return end_;
+ }
+
+ const uint8_t* GetData() const override {
+ return reinterpret_cast<const uint8_t*>(buffer_.data());
+ }
+
+ std::vector<uint8_t> GetDataCopy() const override {
+ return std::vector<uint8_t>(GetData(), GetData() + GetDataSizeBytes());
+ }
+
+ // Returns written stream as std::string, padded with zeroes so that the
+ // length is a multiple of 64.
+ std::string GetStreamPadded64() const {
+ return BufferToStream(buffer_);
+ }
+
+ private:
+ std::vector<uint64_t> buffer_;
+ // Total number of bits written so far. Named 'end' as analogy to std::end().
+ size_t end_;
+};
+
+// Base class for reading sequences of bits.
+class BitReaderInterface {
+ public:
+ BitReaderInterface() {}
+ virtual ~BitReaderInterface() {}
+
+ // Reads |num_bits| from the stream, stores them in |bits|.
+ // Returns number of read bits. |num_bits| must be no greater than 64.
+ virtual size_t ReadBits(uint64_t* bits, size_t num_bits) = 0;
+
+ // Reads |num_bits| from the stream, stores them in |bits|.
+ // Returns number of read bits. |num_bits| must be no greater than 64.
+ template <size_t N>
+ size_t ReadBitset(std::bitset<N>* bits, size_t num_bits = N) {
+ uint64_t val = 0;
+ size_t num_read = ReadBits(&val, num_bits);
+ if (num_read) {
+ *bits = std::bitset<N>(val);
+ }
+ return num_read;
+ }
+
+ // Reads |num_bits| from the stream, returns string in left-to-right order.
+ // The length of the returned string may be less than |num_bits| if end was
+ // reached.
+ std::string ReadStream(size_t num_bits) {
+ uint64_t bits = 0;
+ size_t num_read = ReadBits(&bits, num_bits);
+ return BitsToStream(bits, num_read);
+ }
+
+ // These two functions define 'hard' and 'soft' EOF.
+ //
+ // Returns true if the end of the buffer was reached.
+ virtual bool ReachedEnd() const = 0;
+ // Returns true if we reached the end of the buffer or are nearing it and only
+ // zero bits are left to read. Implementations of this function are allowed to
+ // commit a "false negative" error if the end of the buffer was not reached,
+ // i.e. it can return false even if indeed only zeroes are left.
+ // It is assumed that the consumer expects that
+ // the buffer stream ends with padding zeroes, and would accept this as a
+ // 'soft' EOF. Implementations of this class do not necessarily need to
+ // implement this, default behavior can simply delegate to ReachedEnd().
+ virtual bool OnlyZeroesLeft() const {
+ return ReachedEnd();
+ }
+
+ // Reads value encoded with WriteVariableWidthXXX (see BitWriterInterface).
+ // Reader and writer must use the same |chunk_length| and variable type.
+ // Returns true on success, false if the bit stream ends prematurely.
+ bool ReadVariableWidthU64(uint64_t* val, size_t chunk_length);
+ bool ReadVariableWidthU32(uint32_t* val, size_t chunk_length);
+ bool ReadVariableWidthU16(uint16_t* val, size_t chunk_length);
+ bool ReadVariableWidthU8(uint8_t* val, size_t chunk_length);
+ bool ReadVariableWidthS64(
+ int64_t* val, size_t chunk_length, size_t zigzag_exponent);
+ bool ReadVariableWidthS32(
+ int32_t* val, size_t chunk_length, size_t zigzag_exponent);
+ bool ReadVariableWidthS16(
+ int16_t* val, size_t chunk_length, size_t zigzag_exponent);
+ bool ReadVariableWidthS8(
+ int8_t* val, size_t chunk_length, size_t zigzag_exponent);
+
+ BitReaderInterface(const BitReaderInterface&) = delete;
+ BitReaderInterface& operator=(const BitReaderInterface&) = delete;
+};
+
+// This class is an implementation of BitReaderInterface which accepts both
+// uint8_t and uint64_t buffers as input. uint64_t buffers are consumed and
+// owned. uint8_t buffers are copied.
+class BitReaderWord64 : public BitReaderInterface {
+ public:
+ // Consumes and owns the buffer.
+ explicit BitReaderWord64(std::vector<uint64_t>&& buffer);
+
+ // Copies the buffer and casts it to uint64.
+ // Consuming the original buffer and casting it to uint64 is difficult,
+ // as it would potentially cause data misalignment and poor performance.
+ explicit BitReaderWord64(const std::vector<uint8_t>& buffer);
+
+ size_t ReadBits(uint64_t* bits, size_t num_bits) override;
+ bool ReachedEnd() const override;
+ bool OnlyZeroesLeft() const override;
+
+ BitReaderWord64() = delete;
+ private:
+ const std::vector<uint64_t> buffer_;
+ size_t pos_;
+};
+
+} // namespace spvutils
+
+#endif // LIBSPIRV_UTIL_BIT_STREAM_H_
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index dfddd09..926dade 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -164,6 +164,11 @@
SRCS preserve_numeric_ids_test.cpp
LIBS ${SPIRV_TOOLS})
+add_spvtools_unittest(
+ TARGET bit_stream
+ SRCS bit_stream.cpp
+ LIBS ${SPIRV_TOOLS})
+
add_subdirectory(opt)
add_subdirectory(val)
add_subdirectory(stats)
diff --git a/test/bit_stream.cpp b/test/bit_stream.cpp
new file mode 100644
index 0000000..7e18e64
--- /dev/null
+++ b/test/bit_stream.cpp
@@ -0,0 +1,1138 @@
+// Copyright (c) 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "util/bit_stream.h"
+
+namespace {
+
+using spvutils::BitWriterInterface;
+using spvutils::BitReaderInterface;
+using spvutils::BitWriterWord64;
+using spvutils::BitReaderWord64;
+using spvutils::StreamToBuffer;
+using spvutils::BufferToStream;
+using spvutils::NumBitsToNumWords;
+using spvutils::PadToWord;
+using spvutils::StreamToBitset;
+using spvutils::BitsetToStream;
+using spvutils::BitsToStream;
+using spvutils::StreamToBits;
+using spvutils::GetLowerBits;
+using spvutils::EncodeZigZag;
+using spvutils::DecodeZigZag;
+
+// A simple and inefficient implementatition of BitWriterInterface,
+// using std::stringstream. Intended for tests only.
+class BitWriterStringStream : public BitWriterInterface {
+ public:
+ void WriteStream(const std::string& bits) override {
+ ss_ << bits;
+ }
+
+ void WriteBits(uint64_t bits, size_t num_bits) override {
+ assert(num_bits <= 64);
+ ss_ << BitsToStream(bits, num_bits);
+ }
+
+ size_t GetNumBits() const override {
+ return ss_.str().size();
+ }
+
+ std::vector<uint8_t> GetDataCopy() const override {
+ return StreamToBuffer<uint8_t>(ss_.str());
+ }
+
+ std::string GetStreamRaw() const {
+ return ss_.str();
+ }
+
+ private:
+ std::stringstream ss_;
+};
+
+// A simple and inefficient implementatition of BitReaderInterface.
+// Intended for tests only.
+class BitReaderFromString : public BitReaderInterface {
+ public:
+ explicit BitReaderFromString(std::string&& str)
+ : str_(std::move(str)), pos_(0) {}
+
+ explicit BitReaderFromString(const std::vector<uint64_t>& buffer)
+ : str_(BufferToStream(buffer)), pos_(0) {}
+
+ explicit BitReaderFromString(const std::vector<uint8_t>& buffer)
+ : str_(PadToWord<64>(BufferToStream(buffer))), pos_(0) {}
+
+ size_t ReadBits(uint64_t* bits, size_t num_bits) override {
+ if (ReachedEnd())
+ return 0;
+ std::string sub = str_.substr(pos_, num_bits);
+ *bits = StreamToBits(sub);
+ pos_ += sub.length();
+ return sub.length();
+ }
+
+ bool ReachedEnd() const override {
+ return pos_ >= str_.length();
+ }
+
+ const std::string& GetStreamPadded64() const {
+ return str_;
+ }
+
+ private:
+ std::string str_;
+ size_t pos_;
+};
+
+TEST(NumBitsToNumWords, Word8) {
+ EXPECT_EQ(0u, NumBitsToNumWords<8>(0));
+ EXPECT_EQ(1u, NumBitsToNumWords<8>(1));
+ EXPECT_EQ(1u, NumBitsToNumWords<8>(7));
+ EXPECT_EQ(1u, NumBitsToNumWords<8>(8));
+ EXPECT_EQ(2u, NumBitsToNumWords<8>(9));
+ EXPECT_EQ(2u, NumBitsToNumWords<8>(16));
+ EXPECT_EQ(3u, NumBitsToNumWords<8>(17));
+ EXPECT_EQ(3u, NumBitsToNumWords<8>(23));
+ EXPECT_EQ(3u, NumBitsToNumWords<8>(24));
+ EXPECT_EQ(4u, NumBitsToNumWords<8>(25));
+}
+
+TEST(NumBitsToNumWords, Word64) {
+ EXPECT_EQ(0u, NumBitsToNumWords<64>(0));
+ EXPECT_EQ(1u, NumBitsToNumWords<64>(1));
+ EXPECT_EQ(1u, NumBitsToNumWords<64>(64));
+ EXPECT_EQ(2u, NumBitsToNumWords<64>(65));
+ EXPECT_EQ(2u, NumBitsToNumWords<64>(128));
+ EXPECT_EQ(3u, NumBitsToNumWords<64>(129));
+}
+
+TEST(ZigZagCoding, Encode) {
+ EXPECT_EQ(0u, EncodeZigZag(0));
+ EXPECT_EQ(1u, EncodeZigZag(-1));
+ EXPECT_EQ(2u, EncodeZigZag(1));
+ EXPECT_EQ(3u, EncodeZigZag(-2));
+ EXPECT_EQ(4u, EncodeZigZag(2));
+ EXPECT_EQ(5u, EncodeZigZag(-3));
+ EXPECT_EQ(6u, EncodeZigZag(3));
+ EXPECT_EQ(std::numeric_limits<uint64_t>::max() - 1,
+ EncodeZigZag(std::numeric_limits<int64_t>::max()));
+ EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
+ EncodeZigZag(std::numeric_limits<int64_t>::min()));
+}
+
+TEST(ZigZagCoding, Decode) {
+ EXPECT_EQ(0, DecodeZigZag(0));
+ EXPECT_EQ(-1, DecodeZigZag(1));
+ EXPECT_EQ(1, DecodeZigZag(2));
+ EXPECT_EQ(-2, DecodeZigZag(3));
+ EXPECT_EQ(2, DecodeZigZag(4));
+ EXPECT_EQ(-3, DecodeZigZag(5));
+ EXPECT_EQ(3, DecodeZigZag(6));
+ EXPECT_EQ(std::numeric_limits<int64_t>::min(),
+ DecodeZigZag(std::numeric_limits<uint64_t>::max()));
+ EXPECT_EQ(std::numeric_limits<int64_t>::max(),
+ DecodeZigZag(std::numeric_limits<uint64_t>::max() - 1));
+}
+
+TEST(ZigZagCoding, Encode0) {
+ EXPECT_EQ(0u, EncodeZigZag(0, 0));
+ EXPECT_EQ(1u, EncodeZigZag(-1, 0));
+ EXPECT_EQ(2u, EncodeZigZag(1, 0));
+ EXPECT_EQ(3u, EncodeZigZag(-2, 0));
+ EXPECT_EQ(std::numeric_limits<uint64_t>::max() - 1,
+ EncodeZigZag(std::numeric_limits<int64_t>::max(), 0));
+ EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
+ EncodeZigZag(std::numeric_limits<int64_t>::min(), 0));
+}
+
+TEST(ZigZagCoding, Decode0) {
+ EXPECT_EQ(0, DecodeZigZag(0, 0));
+ EXPECT_EQ(-1, DecodeZigZag(1, 0));
+ EXPECT_EQ(1, DecodeZigZag(2, 0));
+ EXPECT_EQ(-2, DecodeZigZag(3, 0));
+ EXPECT_EQ(std::numeric_limits<int64_t>::min(),
+ DecodeZigZag(std::numeric_limits<uint64_t>::max(), 0));
+ EXPECT_EQ(std::numeric_limits<int64_t>::max(),
+ DecodeZigZag(std::numeric_limits<uint64_t>::max() - 1, 0));
+}
+
+TEST(ZigZagCoding, Decode0SameAsNormalZigZag) {
+ for (int32_t i = -10000; i < 10000; i += 123) {
+ ASSERT_EQ(DecodeZigZag(i), DecodeZigZag(i, 0));
+ }
+}
+
+TEST(ZigZagCoding, Encode0SameAsNormalZigZag) {
+ for (uint32_t i = 0; i < 10000; i += 123) {
+ ASSERT_EQ(EncodeZigZag(i), EncodeZigZag(i, 0));
+ }
+}
+
+TEST(ZigZagCoding, Encode1) {
+ EXPECT_EQ(0u, EncodeZigZag(0, 1));
+ EXPECT_EQ(1u, EncodeZigZag(1, 1));
+ EXPECT_EQ(2u, EncodeZigZag(-1, 1));
+ EXPECT_EQ(3u, EncodeZigZag(-2, 1));
+ EXPECT_EQ(4u, EncodeZigZag(2, 1));
+ EXPECT_EQ(5u, EncodeZigZag(3, 1));
+ EXPECT_EQ(6u, EncodeZigZag(-3, 1));
+ EXPECT_EQ(7u, EncodeZigZag(-4, 1));
+ EXPECT_EQ(std::numeric_limits<uint64_t>::max() - 2,
+ EncodeZigZag(std::numeric_limits<int64_t>::max(), 1));
+ EXPECT_EQ(std::numeric_limits<uint64_t>::max() - 1,
+ EncodeZigZag(std::numeric_limits<int64_t>::min() + 1, 1));
+ EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
+ EncodeZigZag(std::numeric_limits<int64_t>::min(), 1));
+}
+
+TEST(ZigZagCoding, Decode1) {
+ EXPECT_EQ(0, DecodeZigZag(0, 1));
+ EXPECT_EQ(1, DecodeZigZag(1, 1));
+ EXPECT_EQ(-1, DecodeZigZag(2, 1));
+ EXPECT_EQ(-2, DecodeZigZag(3, 1));
+ EXPECT_EQ(2, DecodeZigZag(4, 1));
+ EXPECT_EQ(3, DecodeZigZag(5, 1));
+ EXPECT_EQ(-3, DecodeZigZag(6, 1));
+ EXPECT_EQ(-4, DecodeZigZag(7, 1));
+ EXPECT_EQ(std::numeric_limits<int64_t>::min(),
+ DecodeZigZag(std::numeric_limits<uint64_t>::max(), 1));
+ EXPECT_EQ(std::numeric_limits<int64_t>::min() + 1,
+ DecodeZigZag(std::numeric_limits<uint64_t>::max() - 1, 1));
+ EXPECT_EQ(std::numeric_limits<int64_t>::max(),
+ DecodeZigZag(std::numeric_limits<uint64_t>::max() - 2, 1));
+}
+
+TEST(ZigZagCoding, Encode2) {
+ EXPECT_EQ(0u, EncodeZigZag(0, 2));
+ EXPECT_EQ(1u, EncodeZigZag(1, 2));
+ EXPECT_EQ(2u, EncodeZigZag(2, 2));
+ EXPECT_EQ(3u, EncodeZigZag(3, 2));
+ EXPECT_EQ(4u, EncodeZigZag(-1, 2));
+ EXPECT_EQ(5u, EncodeZigZag(-2, 2));
+ EXPECT_EQ(6u, EncodeZigZag(-3, 2));
+ EXPECT_EQ(7u, EncodeZigZag(-4, 2));
+ EXPECT_EQ(8u, EncodeZigZag(4, 2));
+ EXPECT_EQ(9u, EncodeZigZag(5, 2));
+ EXPECT_EQ(10u, EncodeZigZag(6, 2));
+ EXPECT_EQ(11u, EncodeZigZag(7, 2));
+ EXPECT_EQ(12u, EncodeZigZag(-5, 2));
+ EXPECT_EQ(13u, EncodeZigZag(-6, 2));
+ EXPECT_EQ(14u, EncodeZigZag(-7, 2));
+ EXPECT_EQ(15u, EncodeZigZag(-8, 2));
+ EXPECT_EQ(std::numeric_limits<uint64_t>::max() - 4,
+ EncodeZigZag(std::numeric_limits<int64_t>::max(), 2));
+ EXPECT_EQ(std::numeric_limits<uint64_t>::max() - 3,
+ EncodeZigZag(std::numeric_limits<int64_t>::min() + 3, 2));
+ EXPECT_EQ(std::numeric_limits<uint64_t>::max() - 2,
+ EncodeZigZag(std::numeric_limits<int64_t>::min() + 2, 2));
+ EXPECT_EQ(std::numeric_limits<uint64_t>::max() - 1,
+ EncodeZigZag(std::numeric_limits<int64_t>::min() + 1, 2));
+ EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
+ EncodeZigZag(std::numeric_limits<int64_t>::min(), 2));
+}
+
+TEST(ZigZagCoding, Decode2) {
+ EXPECT_EQ(0, DecodeZigZag(0, 2));
+ EXPECT_EQ(1, DecodeZigZag(1, 2));
+ EXPECT_EQ(2, DecodeZigZag(2, 2));
+ EXPECT_EQ(3, DecodeZigZag(3, 2));
+ EXPECT_EQ(-1, DecodeZigZag(4, 2));
+ EXPECT_EQ(-2, DecodeZigZag(5, 2));
+ EXPECT_EQ(-3, DecodeZigZag(6, 2));
+ EXPECT_EQ(-4, DecodeZigZag(7, 2));
+ EXPECT_EQ(4, DecodeZigZag(8, 2));
+ EXPECT_EQ(5, DecodeZigZag(9, 2));
+ EXPECT_EQ(6, DecodeZigZag(10, 2));
+ EXPECT_EQ(7, DecodeZigZag(11, 2));
+ EXPECT_EQ(-5, DecodeZigZag(12, 2));
+ EXPECT_EQ(-6, DecodeZigZag(13, 2));
+ EXPECT_EQ(-7, DecodeZigZag(14, 2));
+ EXPECT_EQ(-8, DecodeZigZag(15, 2));
+ EXPECT_EQ(std::numeric_limits<int64_t>::min(),
+ DecodeZigZag(std::numeric_limits<uint64_t>::max(), 2));
+ EXPECT_EQ(std::numeric_limits<int64_t>::min() + 1,
+ DecodeZigZag(std::numeric_limits<uint64_t>::max() - 1, 2));
+ EXPECT_EQ(std::numeric_limits<int64_t>::min() + 2,
+ DecodeZigZag(std::numeric_limits<uint64_t>::max() - 2, 2));
+ EXPECT_EQ(std::numeric_limits<int64_t>::min() + 3,
+ DecodeZigZag(std::numeric_limits<uint64_t>::max() - 3, 2));
+ EXPECT_EQ(std::numeric_limits<int64_t>::max(),
+ DecodeZigZag(std::numeric_limits<uint64_t>::max() - 4, 2));
+}
+
+TEST(ZigZagCoding, Encode63) {
+ EXPECT_EQ(0u, EncodeZigZag(0, 63));
+
+ for (int64_t i = 0; i < 0xFFFFFFFF; i += 1234567) {
+ const int64_t positive_val = GetLowerBits(i * i * i + i * i, 63) | 1UL;
+ ASSERT_EQ(static_cast<uint64_t>(positive_val),
+ EncodeZigZag(positive_val, 63));
+ ASSERT_EQ((1ULL << 63) - 1 + positive_val, EncodeZigZag(-positive_val, 63));
+ }
+
+ EXPECT_EQ((1ULL << 63) - 1,
+ EncodeZigZag(std::numeric_limits<int64_t>::max(), 63));
+ EXPECT_EQ(std::numeric_limits<uint64_t>::max() - 1,
+ EncodeZigZag(std::numeric_limits<int64_t>::min() + 1, 63));
+ EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
+ EncodeZigZag(std::numeric_limits<int64_t>::min(), 63));
+}
+
+TEST(BufToStream, UInt8_Empty) {
+ const std::string expected_bits = "";
+ std::vector<uint8_t> buffer = StreamToBuffer<uint8_t>(expected_bits);
+ EXPECT_TRUE(buffer.empty());
+ const std::string result_bits = BufferToStream(buffer);
+ EXPECT_EQ(expected_bits, result_bits);
+}
+
+TEST(BufToStream, UInt8_OneWord) {
+ const std::string expected_bits = "00101100";
+ std::vector<uint8_t> buffer = StreamToBuffer<uint8_t>(expected_bits);
+ EXPECT_EQ(
+ std::vector<uint8_t>(
+ {static_cast<uint8_t>(StreamToBitset<8>(expected_bits).to_ulong())}),
+ buffer);
+ const std::string result_bits = BufferToStream(buffer);
+ EXPECT_EQ(expected_bits, result_bits);
+}
+
+TEST(BufToStream, UInt8_MultipleWords) {
+ const std::string expected_bits = "00100010""01101010""01111101""00100010";
+ std::vector<uint8_t> buffer = StreamToBuffer<uint8_t>(expected_bits);
+ EXPECT_EQ(
+ std::vector<uint8_t>({
+ static_cast<uint8_t>(StreamToBitset<8>("00100010").to_ulong()),
+ static_cast<uint8_t>(StreamToBitset<8>("01101010").to_ulong()),
+ static_cast<uint8_t>(StreamToBitset<8>("01111101").to_ulong()),
+ static_cast<uint8_t>(StreamToBitset<8>("00100010").to_ulong()),
+ }), buffer);
+ const std::string result_bits = BufferToStream(buffer);
+ EXPECT_EQ(expected_bits, result_bits);
+}
+
+TEST(BufToStream, UInt64_Empty) {
+ const std::string expected_bits = "";
+ std::vector<uint64_t> buffer = StreamToBuffer<uint64_t>(expected_bits);
+ EXPECT_TRUE(buffer.empty());
+ const std::string result_bits = BufferToStream(buffer);
+ EXPECT_EQ(expected_bits, result_bits);
+}
+
+TEST(BufToStream, UInt64_OneWord) {
+ const std::string expected_bits =
+ "0001000111101110011001101010101000100010110011000100010010001000";
+ std::vector<uint64_t> buffer = StreamToBuffer<uint64_t>(expected_bits);
+ ASSERT_EQ(1u, buffer.size());
+ EXPECT_EQ(0x1122334455667788u, buffer[0]);
+ const std::string result_bits = BufferToStream(buffer);
+ EXPECT_EQ(expected_bits, result_bits);
+}
+
+TEST(BufToStream, UInt64_Unaligned) {
+ const std::string expected_bits =
+ "0010001001101010011111010010001001001010000111110010010010010101"
+ "0010001001101010011111111111111111111111";
+ std::vector<uint64_t> buffer = StreamToBuffer<uint64_t>(expected_bits);
+ EXPECT_EQ(std::vector<uint64_t>({
+ StreamToBits(expected_bits.substr(0, 64)),
+ StreamToBits(expected_bits.substr(64, 64)),
+ }), buffer);
+ const std::string result_bits = BufferToStream(buffer);
+ EXPECT_EQ(PadToWord<64>(expected_bits), result_bits);
+}
+
+TEST(BufToStream, UInt64_MultipleWords) {
+ const std::string expected_bits =
+ "0010001001101010011111010010001001001010000111110010010010010101"
+ "0010001001101010011111111111111111111111000111110010010010010111"
+ "0000000000000000000000000000000000000000000000000010010011111111";
+ std::vector<uint64_t> buffer = StreamToBuffer<uint64_t>(expected_bits);
+ EXPECT_EQ(std::vector<uint64_t>({
+ StreamToBits(expected_bits.substr(0, 64)),
+ StreamToBits(expected_bits.substr(64, 64)),
+ StreamToBits(expected_bits.substr(128, 64)),
+ }), buffer);
+ const std::string result_bits = BufferToStream(buffer);
+ EXPECT_EQ(expected_bits, result_bits);
+}
+
+TEST(PadToWord, Test) {
+ EXPECT_EQ("10100000", PadToWord<8>("101"));
+ EXPECT_EQ("10100000""00000000", PadToWord<16>("101"));
+ EXPECT_EQ("10100000""00000000""00000000""00000000",
+ PadToWord<32>("101"));
+ EXPECT_EQ("10100000""00000000""00000000""00000000"
+ "00000000""00000000""00000000""00000000",
+ PadToWord<64>("101"));
+}
+
+TEST(BitWriterStringStream, Empty) {
+ BitWriterStringStream writer;
+ EXPECT_EQ(0u, writer.GetNumBits());
+ EXPECT_EQ(0u, writer.GetDataSizeBytes());
+ EXPECT_EQ("", writer.GetStreamRaw());
+}
+
+TEST(BitWriterStringStream, WriteStream) {
+ BitWriterStringStream writer;
+ const std::string bits1 = "1011111111111111111";
+ writer.WriteStream(bits1);
+ EXPECT_EQ(19u, writer.GetNumBits());
+ EXPECT_EQ(3u, writer.GetDataSizeBytes());
+ EXPECT_EQ(bits1, writer.GetStreamRaw());
+
+ const std::string bits2 = "10100001010101010000111111111111111111111111111";
+ writer.WriteStream(bits2);
+ EXPECT_EQ(66u, writer.GetNumBits());
+ EXPECT_EQ(9u, writer.GetDataSizeBytes());
+ EXPECT_EQ(bits1 + bits2, writer.GetStreamRaw());
+}
+
+TEST(BitWriterStringStream, WriteBitSet) {
+ BitWriterStringStream writer;
+ const std::string bits1 = "10101";
+ writer.WriteBitset(StreamToBitset<16>(bits1));
+ EXPECT_EQ(16u, writer.GetNumBits());
+ EXPECT_EQ(2u, writer.GetDataSizeBytes());
+ EXPECT_EQ(PadToWord<16>(bits1), writer.GetStreamRaw());
+}
+
+TEST(BitWriterStringStream, WriteBits) {
+ BitWriterStringStream writer;
+ const uint64_t bits1 = 0x1 | 0x2 | 0x10;
+ writer.WriteBits(bits1, 5);
+ EXPECT_EQ(5u, writer.GetNumBits());
+ EXPECT_EQ(1u, writer.GetDataSizeBytes());
+ EXPECT_EQ("11001", writer.GetStreamRaw());
+}
+
+TEST(BitWriterStringStream, WriteMultiple) {
+ BitWriterStringStream writer;
+
+ std::string expected_result;
+ const std::string bits1 = "101001111111001100010000001110001111111100";
+ writer.WriteStream(bits1);
+
+ const std::string bits2 = "10100011000010010101";
+ writer.WriteBitset(StreamToBitset<20>(bits2));
+
+ const uint64_t val = 0x1 | 0x2 | 0x10;
+ const std::string bits3 = BitsToStream(val, 8);
+ writer.WriteBits(val, 8);
+
+ const std::string expected = bits1 + bits2 + bits3;
+
+ EXPECT_EQ(expected.length(), writer.GetNumBits());
+ EXPECT_EQ(9u, writer.GetDataSizeBytes());
+ EXPECT_EQ(expected, writer.GetStreamRaw());
+
+ EXPECT_EQ(PadToWord<8>(expected), BufferToStream(writer.GetDataCopy()));
+}
+
+TEST(BitWriterWord64, Empty) {
+ BitWriterWord64 writer;
+ EXPECT_EQ(0u, writer.GetNumBits());
+ EXPECT_EQ(0u, writer.GetDataSizeBytes());
+ EXPECT_EQ("", writer.GetStreamPadded64());
+}
+
+TEST(BitWriterWord64, WriteStream) {
+ BitWriterWord64 writer;
+ std::string expected;
+
+ {
+ const std::string bits = "101";
+ expected += bits;
+ writer.WriteStream(bits);
+ EXPECT_EQ(expected.length(), writer.GetNumBits());
+ EXPECT_EQ(1u, writer.GetDataSizeBytes());
+ EXPECT_EQ(PadToWord<64>(expected), writer.GetStreamPadded64());
+ }
+
+ {
+ const std::string bits = "10000111111111110000000";
+ expected += bits;
+ writer.WriteStream(bits);
+ EXPECT_EQ(expected.length(), writer.GetNumBits());
+ EXPECT_EQ(PadToWord<64>(expected), writer.GetStreamPadded64());
+ }
+
+ {
+ const std::string bits = "101001111111111100000111111111111100";
+ expected += bits;
+ writer.WriteStream(bits);
+ EXPECT_EQ(expected.length(), writer.GetNumBits());
+ EXPECT_EQ(PadToWord<64>(expected), writer.GetStreamPadded64());
+ }
+}
+
+TEST(BitWriterWord64, WriteBitset) {
+ BitWriterWord64 writer;
+ const std::string bits1 = "10101";
+ writer.WriteBitset(StreamToBitset<16>(bits1), 12);
+ EXPECT_EQ(12u, writer.GetNumBits());
+ EXPECT_EQ(2u, writer.GetDataSizeBytes());
+ EXPECT_EQ(PadToWord<64>(bits1), writer.GetStreamPadded64());
+}
+
+TEST(BitWriterWord64, WriteBits) {
+ BitWriterWord64 writer;
+ const uint64_t bits1 = 0x1 | 0x2 | 0x10;
+ writer.WriteBits(bits1, 5);
+ writer.WriteBits(bits1, 5);
+ writer.WriteBits(bits1, 5);
+ EXPECT_EQ(15u, writer.GetNumBits());
+ EXPECT_EQ(2u, writer.GetDataSizeBytes());
+ EXPECT_EQ(PadToWord<64>("110011100111001"), writer.GetStreamPadded64());
+}
+
+TEST(BitWriterWord64, ComparisonTestWriteLotsOfBits) {
+ BitWriterStringStream writer1;
+ BitWriterWord64 writer2(16384);
+
+ for (uint64_t i = 0; i < 65000; i += 25) {
+ writer1.WriteBits(i, 16);
+ writer2.WriteBits(i, 16);
+ ASSERT_EQ(writer1.GetNumBits(), writer2.GetNumBits());
+ }
+
+ EXPECT_EQ(PadToWord<64>(writer1.GetStreamRaw()),
+ writer2.GetStreamPadded64());
+}
+
+TEST(BitWriterWord64, ComparisonTestWriteLotsOfStreams) {
+ BitWriterStringStream writer1;
+ BitWriterWord64 writer2(16384);
+
+ for (int i = 0; i < 1000; ++i) {
+ std::string bits = "1111100000";
+ if (i % 2)
+ bits += "101010";
+ if (i % 3)
+ bits += "1110100";
+ if (i % 5)
+ bits += "1110100111111111111";
+ writer1.WriteStream(bits);
+ writer2.WriteStream(bits);
+ ASSERT_EQ(writer1.GetNumBits(), writer2.GetNumBits());
+ }
+
+ EXPECT_EQ(PadToWord<64>(writer1.GetStreamRaw()),
+ writer2.GetStreamPadded64());
+}
+
+TEST(BitWriterWord64, ComparisonTestWriteLotsOfBitsets) {
+ BitWriterStringStream writer1;
+ BitWriterWord64 writer2(16384);
+
+ for (uint64_t i = 0; i < 65000; i += 25) {
+ std::bitset<16> bits1(i);
+ std::bitset<24> bits2(i);
+ writer1.WriteBitset(bits1);
+ writer1.WriteBitset(bits2);
+ writer2.WriteBitset(bits1);
+ writer2.WriteBitset(bits2);
+ ASSERT_EQ(writer1.GetNumBits(), writer2.GetNumBits());
+ }
+
+ EXPECT_EQ(PadToWord<64>(writer1.GetStreamRaw()),
+ writer2.GetStreamPadded64());
+}
+
+TEST(GetLowerBits, Test) {
+ EXPECT_EQ(0u, GetLowerBits<uint8_t>(255, 0));
+ EXPECT_EQ(1u, GetLowerBits<uint8_t>(255, 1));
+ EXPECT_EQ(3u, GetLowerBits<uint8_t>(255, 2));
+ EXPECT_EQ(7u, GetLowerBits<uint8_t>(255, 3));
+ EXPECT_EQ(15u, GetLowerBits<uint8_t>(255, 4));
+ EXPECT_EQ(31u, GetLowerBits<uint8_t>(255, 5));
+ EXPECT_EQ(63u, GetLowerBits<uint8_t>(255, 6));
+ EXPECT_EQ(127u, GetLowerBits<uint8_t>(255, 7));
+ EXPECT_EQ(255u, GetLowerBits<uint8_t>(255, 8));
+ EXPECT_EQ(0xFFu, GetLowerBits<uint32_t>(0xFFFFFFFF, 8));
+ EXPECT_EQ(0xFFFFu, GetLowerBits<uint32_t>(0xFFFFFFFF, 16));
+ EXPECT_EQ(0xFFFFFFu, GetLowerBits<uint32_t>(0xFFFFFFFF, 24));
+ EXPECT_EQ(0xFFFFFFu, GetLowerBits<uint64_t>(0xFFFFFFFFFFFF, 24));
+ EXPECT_EQ(0xFFFFFFFFFFFFFFFFu,
+ GetLowerBits<uint64_t>(0xFFFFFFFFFFFFFFFFu, 64));
+ EXPECT_EQ(StreamToBits("1010001110"),
+ GetLowerBits<uint64_t>(
+ StreamToBits("1010001110111101111111"), 10));
+}
+
+TEST(BitReaderFromString, FromU8) {
+ std::vector<uint8_t> buffer = {
+ 0xAA, 0xBB, 0xCC, 0xDD,
+ };
+
+ const std::string total_stream =
+ "01010101""11011101""00110011""10111011";
+
+ BitReaderFromString reader(buffer);
+ EXPECT_EQ(PadToWord<64>(total_stream), reader.GetStreamPadded64());
+
+ uint64_t bits = 0;
+ EXPECT_EQ(2u, reader.ReadBits(&bits, 2));
+ EXPECT_EQ(PadToWord<64>("01"), BitsToStream(bits));
+ EXPECT_EQ(20u, reader.ReadBits(&bits, 20));
+ EXPECT_EQ(PadToWord<64>("01010111011101001100"), BitsToStream(bits));
+ EXPECT_EQ(20u, reader.ReadBits(&bits, 20));
+ EXPECT_EQ(PadToWord<64>("11101110110000000000"), BitsToStream(bits));
+ EXPECT_EQ(22u, reader.ReadBits(&bits, 30));
+ EXPECT_EQ(PadToWord<64>("0000000000000000000000"), BitsToStream(bits));
+ EXPECT_TRUE(reader.ReachedEnd());
+}
+
+TEST(BitReaderFromString, FromU64) {
+ std::vector<uint64_t> buffer = {
+ 0xAAAAAAAAAAAAAAAA,
+ 0xBBBBBBBBBBBBBBBB,
+ 0xCCCCCCCCCCCCCCCC,
+ 0xDDDDDDDDDDDDDDDD,
+ };
+
+ const std::string total_stream =
+ "0101010101010101010101010101010101010101010101010101010101010101"
+ "1101110111011101110111011101110111011101110111011101110111011101"
+ "0011001100110011001100110011001100110011001100110011001100110011"
+ "1011101110111011101110111011101110111011101110111011101110111011";
+
+ BitReaderFromString reader(buffer);
+ EXPECT_EQ(total_stream, reader.GetStreamPadded64());
+
+ uint64_t bits = 0;
+ size_t pos = 0;
+ size_t to_read = 5;
+ while (reader.ReadBits(&bits, to_read) > 0) {
+ EXPECT_EQ(BitsToStream(bits),
+ PadToWord<64>(total_stream.substr(pos, to_read)));
+ pos += to_read;
+ to_read = (to_read + 35) % 64 + 1;
+ }
+ EXPECT_TRUE(reader.ReachedEnd());
+}
+
+TEST(BitReaderWord64, ReadBitsSingleByte) {
+ BitReaderWord64 reader(std::vector<uint8_t>({uint8_t(0xF0)}));
+ EXPECT_FALSE(reader.ReachedEnd());
+
+ uint64_t bits = 0;
+ EXPECT_EQ(1u, reader.ReadBits(&bits, 1));
+ EXPECT_EQ(0u, bits);
+ EXPECT_EQ(2u, reader.ReadBits(&bits, 2));
+ EXPECT_EQ(0u, bits);
+ EXPECT_EQ(2u, reader.ReadBits(&bits, 2));
+ EXPECT_EQ(2u, bits);
+ EXPECT_EQ(2u, reader.ReadBits(&bits, 2));
+ EXPECT_EQ(3u, bits);
+ EXPECT_FALSE(reader.OnlyZeroesLeft());
+ EXPECT_FALSE(reader.ReachedEnd());
+ EXPECT_EQ(2u, reader.ReadBits(&bits, 2));
+ EXPECT_EQ(1u, bits);
+ EXPECT_TRUE(reader.OnlyZeroesLeft());
+ EXPECT_FALSE(reader.ReachedEnd());
+ EXPECT_EQ(55u, reader.ReadBits(&bits, 64));
+ EXPECT_EQ(0u, bits);
+ EXPECT_TRUE(reader.ReachedEnd());
+}
+
+TEST(BitReaderWord64, ReadBitsetSingleByte) {
+ BitReaderWord64 reader(std::vector<uint8_t>({uint8_t(0xCC)}));
+ std::bitset<4> bits;
+ EXPECT_EQ(2u, reader.ReadBitset(&bits, 2));
+ EXPECT_EQ(0u, bits.to_ullong());
+ EXPECT_EQ(2u, reader.ReadBitset(&bits, 2));
+ EXPECT_EQ(3u, bits.to_ullong());
+ EXPECT_FALSE(reader.OnlyZeroesLeft());
+ EXPECT_EQ(4u, reader.ReadBitset(&bits, 4));
+ EXPECT_EQ(12u, bits.to_ullong());
+ EXPECT_TRUE(reader.OnlyZeroesLeft());
+}
+
+TEST(BitReaderWord64, ReadStreamSingleByte) {
+ BitReaderWord64 reader(std::vector<uint8_t>({uint8_t(0xAA)}));
+ EXPECT_EQ("", reader.ReadStream(0));
+ EXPECT_EQ("0", reader.ReadStream(1));
+ EXPECT_EQ("101", reader.ReadStream(3));
+ EXPECT_EQ("01010000", reader.ReadStream(8));
+ EXPECT_TRUE(reader.OnlyZeroesLeft());
+ EXPECT_EQ("0000000000000000000000000000000000000000000000000000",
+ reader.ReadStream(64));
+ EXPECT_TRUE(reader.ReachedEnd());
+}
+
+TEST(BitReaderWord64, ReadStreamEmpty) {
+ std::vector<uint64_t> buffer;
+ BitReaderWord64 reader(std::move(buffer));
+ EXPECT_TRUE(reader.OnlyZeroesLeft());
+ EXPECT_TRUE(reader.ReachedEnd());
+ EXPECT_EQ("", reader.ReadStream(10));
+ EXPECT_TRUE(reader.ReachedEnd());
+}
+
+TEST(BitReaderWord64, ReadBitsTwoWords) {
+ std::vector<uint64_t> buffer = {
+ 0x0000000000000001,
+ 0x0000000000FFFFFF
+ };
+
+ BitReaderWord64 reader(std::move(buffer));
+
+ uint64_t bits = 0;
+ EXPECT_EQ(1u, reader.ReadBits(&bits, 1));
+ EXPECT_EQ(1u, bits);
+ EXPECT_EQ(62u, reader.ReadBits(&bits, 62));
+ EXPECT_EQ(0u, bits);
+ EXPECT_EQ(2u, reader.ReadBits(&bits, 2));
+ EXPECT_EQ(2u, bits);
+ EXPECT_EQ(3u, reader.ReadBits(&bits, 3));
+ EXPECT_EQ(7u, bits);
+ EXPECT_FALSE(reader.OnlyZeroesLeft());
+ EXPECT_EQ(32u, reader.ReadBits(&bits, 32));
+ EXPECT_EQ(0xFFFFFu, bits);
+ EXPECT_TRUE(reader.OnlyZeroesLeft());
+ EXPECT_FALSE(reader.ReachedEnd());
+ EXPECT_EQ(28u, reader.ReadBits(&bits, 32));
+ EXPECT_EQ(0u, bits);
+ EXPECT_TRUE(reader.ReachedEnd());
+}
+
+TEST(BitReaderWord64, FromU8) {
+ std::vector<uint8_t> buffer = {
+ 0xAA, 0xBB, 0xCC, 0xDD,
+ };
+
+ BitReaderWord64 reader(std::move(buffer));
+
+ uint64_t bits = 0;
+ EXPECT_EQ(2u, reader.ReadBits(&bits, 2));
+ EXPECT_EQ(PadToWord<64>("01"), BitsToStream(bits));
+ EXPECT_EQ(20u, reader.ReadBits(&bits, 20));
+ EXPECT_EQ(PadToWord<64>("01010111011101001100"), BitsToStream(bits));
+ EXPECT_EQ(20u, reader.ReadBits(&bits, 20));
+ EXPECT_EQ(PadToWord<64>("11101110110000000000"), BitsToStream(bits));
+ EXPECT_EQ(22u, reader.ReadBits(&bits, 30));
+ EXPECT_EQ(PadToWord<64>("0000000000000000000000"), BitsToStream(bits));
+ EXPECT_TRUE(reader.ReachedEnd());
+}
+
+TEST(BitReaderWord64, FromU64) {
+ std::vector<uint64_t> buffer = {
+ 0xAAAAAAAAAAAAAAAA,
+ 0xBBBBBBBBBBBBBBBB,
+ 0xCCCCCCCCCCCCCCCC,
+ 0xDDDDDDDDDDDDDDDD,
+ };
+
+ const std::string total_stream =
+ "0101010101010101010101010101010101010101010101010101010101010101"
+ "1101110111011101110111011101110111011101110111011101110111011101"
+ "0011001100110011001100110011001100110011001100110011001100110011"
+ "1011101110111011101110111011101110111011101110111011101110111011";
+
+ BitReaderWord64 reader(std::move(buffer));
+
+ uint64_t bits = 0;
+ size_t pos = 0;
+ size_t to_read = 5;
+ while (reader.ReadBits(&bits, to_read) > 0) {
+ EXPECT_EQ(BitsToStream(bits),
+ PadToWord<64>(total_stream.substr(pos, to_read)));
+ pos += to_read;
+ to_read = (to_read + 35) % 64 + 1;
+ }
+ EXPECT_TRUE(reader.ReachedEnd());
+}
+
+TEST(BitReaderWord64, ComparisonLotsOfU8) {
+ std::vector<uint8_t> buffer;
+ for(uint32_t i = 0; i < 10003; ++i) {
+ buffer.push_back(static_cast<uint8_t>(i % 255));
+ }
+
+ BitReaderFromString reader1(buffer);
+ BitReaderWord64 reader2(std::move(buffer));
+
+ uint64_t bits1 = 0, bits2 = 0;
+ size_t to_read = 5;
+ while (reader1.ReadBits(&bits1, to_read) > 0) {
+ reader2.ReadBits(&bits2, to_read);
+ EXPECT_EQ(bits1, bits2);
+ to_read = (to_read + 35) % 64 + 1;
+ }
+
+ EXPECT_EQ(0u, reader2.ReadBits(&bits2, 1));
+}
+
+TEST(BitReaderWord64, ComparisonLotsOfU64) {
+ std::vector<uint64_t> buffer;
+ for(uint64_t i = 0; i < 1000; ++i) {
+ buffer.push_back(i);
+ }
+
+ BitReaderFromString reader1(buffer);
+ BitReaderWord64 reader2(std::move(buffer));
+
+ uint64_t bits1 = 0, bits2 = 0;
+ size_t to_read = 5;
+ while (reader1.ReadBits(&bits1, to_read) > 0) {
+ reader2.ReadBits(&bits2, to_read);
+ EXPECT_EQ(bits1, bits2);
+ to_read = (to_read + 35) % 64 + 1;
+ }
+
+ EXPECT_EQ(0u, reader2.ReadBits(&bits2, 1));
+}
+
+TEST(ReadWriteWord64, ReadWriteLotsOfBits) {
+ BitWriterWord64 writer(16384);
+ for (uint64_t i = 0; i < 65000; i += 25) {
+ const size_t num_bits = i % 64 + 1;
+ const uint64_t bits = i >> (64 - num_bits);
+ writer.WriteBits(bits, num_bits);
+ }
+
+ BitReaderWord64 reader(writer.GetDataCopy());
+ for (uint64_t i = 0; i < 65000; i += 25) {
+ const size_t num_bits = i % 64 + 1;
+ const uint64_t expected_bits = i >> (64 - num_bits);
+ uint64_t bits = 0;
+ reader.ReadBits(&bits, num_bits);
+ EXPECT_EQ(expected_bits, bits);
+ }
+
+ EXPECT_TRUE(reader.OnlyZeroesLeft());
+}
+
+TEST(VariableWidthWrite, Write0U) {
+ BitWriterStringStream writer;
+ writer.WriteVariableWidthU64(0, 2);
+ EXPECT_EQ("000", writer.GetStreamRaw ());
+ writer.WriteVariableWidthU32(0, 2);
+ EXPECT_EQ("000""000", writer.GetStreamRaw());
+ writer.WriteVariableWidthU16(0, 2);
+ EXPECT_EQ("000""000""000", writer.GetStreamRaw());
+ writer.WriteVariableWidthU8(0, 2);
+ EXPECT_EQ("000""000""000""000", writer.GetStreamRaw());
+}
+
+TEST(VariableWidthWrite, Write0S) {
+ BitWriterStringStream writer;
+ writer.WriteVariableWidthS64(0, 2, 0);
+ EXPECT_EQ("000", writer.GetStreamRaw ());
+ writer.WriteVariableWidthS32(0, 2, 0);
+ EXPECT_EQ("000""000", writer.GetStreamRaw());
+ writer.WriteVariableWidthS16(0, 2, 0);
+ EXPECT_EQ("000""000""000", writer.GetStreamRaw());
+ writer.WriteVariableWidthS8(0, 2, 0);
+ EXPECT_EQ("000""000""000""000", writer.GetStreamRaw());
+}
+
+TEST(VariableWidthWrite, WriteSmallUnsigned) {
+ BitWriterStringStream writer;
+ writer.WriteVariableWidthU64(1, 2);
+ EXPECT_EQ("100", writer.GetStreamRaw ());
+ writer.WriteVariableWidthU32(2, 2);
+ EXPECT_EQ("100""010", writer.GetStreamRaw());
+ writer.WriteVariableWidthU16(3, 2);
+ EXPECT_EQ("100""010""110", writer.GetStreamRaw());
+ writer.WriteVariableWidthU8(4, 2);
+ EXPECT_EQ("100""010""110""001100", writer.GetStreamRaw());
+}
+
+TEST(VariableWidthWrite, WriteSmallSigned) {
+ BitWriterStringStream writer;
+ writer.WriteVariableWidthS64(1, 2, 0);
+ EXPECT_EQ("010", writer.GetStreamRaw ());
+ writer.WriteVariableWidthS64(-1, 2, 0);
+ EXPECT_EQ("010""100", writer.GetStreamRaw());
+ writer.WriteVariableWidthS16(3, 2, 0);
+ EXPECT_EQ("010""100""011100", writer.GetStreamRaw());
+ writer.WriteVariableWidthS8(-4, 2, 0);
+ EXPECT_EQ("010""100""011100""111100", writer.GetStreamRaw());
+}
+
+TEST(VariableWidthWrite, U64Val127ChunkLength7) {
+ BitWriterStringStream writer;
+ writer.WriteVariableWidthU64(127, 7);
+ EXPECT_EQ("1111111""0", writer.GetStreamRaw());
+}
+
+TEST(VariableWidthWrite, U32Val255ChunkLength7) {
+ BitWriterStringStream writer;
+ writer.WriteVariableWidthU32(255, 7);
+ EXPECT_EQ("1111111""1""1000000""0", writer.GetStreamRaw());
+}
+
+TEST(VariableWidthWrite, U16Val2ChunkLength4) {
+ BitWriterStringStream writer;
+ writer.WriteVariableWidthU16(2, 4);
+ EXPECT_EQ("0100""0", writer.GetStreamRaw());
+}
+
+TEST(VariableWidthWrite, U8Val128ChunkLength7) {
+ BitWriterStringStream writer;
+ writer.WriteVariableWidthU8(128, 7);
+ EXPECT_EQ("0000000""1""1", writer.GetStreamRaw());
+}
+
+TEST(VariableWidthWrite, U64ValAAAAChunkLength2) {
+ BitWriterStringStream writer;
+ writer.WriteVariableWidthU64(0xAAAA, 2);
+ EXPECT_EQ("01""1""01""1""01""1""01""1"
+ "01""1""01""1""01""1""01""0", writer.GetStreamRaw());
+}
+
+TEST(VariableWidthWrite, S8ValM128ChunkLength7) {
+ BitWriterStringStream writer;
+ writer.WriteVariableWidthS8(-128, 7, 0);
+ EXPECT_EQ("1111111""1""1", writer.GetStreamRaw());
+}
+
+TEST(VariableWidthRead, U64Val127ChunkLength7) {
+ BitReaderFromString reader("1111111""0");
+ uint64_t val = 0;
+ ASSERT_TRUE(reader.ReadVariableWidthU64(&val, 7));
+ EXPECT_EQ(127u, val);
+}
+
+TEST(VariableWidthRead, U32Val255ChunkLength7) {
+ BitReaderFromString reader("1111111""1""1000000""0");
+ uint32_t val = 0;
+ ASSERT_TRUE(reader.ReadVariableWidthU32(&val, 7));
+ EXPECT_EQ(255u, val);
+}
+
+TEST(VariableWidthRead, U16Val2ChunkLength4) {
+ BitReaderFromString reader("0100""0");
+ uint16_t val = 0;
+ ASSERT_TRUE(reader.ReadVariableWidthU16(&val, 4));
+ EXPECT_EQ(2u, val);
+}
+
+TEST(VariableWidthRead, U8Val128ChunkLength7) {
+ BitReaderFromString reader("0000000""1""1");
+ uint8_t val = 0;
+ ASSERT_TRUE(reader.ReadVariableWidthU8(&val, 7));
+ EXPECT_EQ(128u, val);
+}
+
+TEST(VariableWidthRead, U64ValAAAAChunkLength2) {
+ BitReaderFromString reader("01""1""01""1""01""1""01""1"
+ "01""1""01""1""01""1""01""0");
+ uint64_t val = 0;
+ ASSERT_TRUE(reader.ReadVariableWidthU64(&val, 2));
+ EXPECT_EQ(0xAAAAu, val);
+}
+
+TEST(VariableWidthRead, S8ValM128ChunkLength7) {
+ BitReaderFromString reader("1111111""1""1");
+ int8_t val = 0;
+ ASSERT_TRUE(reader.ReadVariableWidthS8(&val, 7, 0));
+ EXPECT_EQ(-128, val);
+}
+
+TEST(VariableWidthRead, FailTooShort) {
+ BitReaderFromString reader("00000001100000");
+ uint64_t val = 0;
+ ASSERT_FALSE(reader.ReadVariableWidthU64(&val, 7));
+}
+
+TEST(VariableWidthWriteRead, SingleWriteReadU64) {
+ for (uint64_t i = 0; i < 1000000; i += 1234) {
+ const uint64_t val = i * i * i;
+ const size_t chunk_length = i % 16 + 1;
+
+ BitWriterWord64 writer;
+ writer.WriteVariableWidthU64(val, chunk_length);
+
+ BitReaderWord64 reader(writer.GetDataCopy());
+ uint64_t read_val = 0;
+ ASSERT_TRUE(reader.ReadVariableWidthU64(&read_val, chunk_length));
+
+ ASSERT_EQ(val, read_val) << "Chunk length " << chunk_length;
+ }
+}
+
+TEST(VariableWidthWriteRead, SingleWriteReadS64) {
+ for (int64_t i = 0; i < 1000000; i += 4321) {
+ const int64_t val = i * i * (i % 2 ? -i : i);
+ const size_t chunk_length = i % 16 + 1;
+ const size_t zigzag_exponent = i % 13;
+
+ BitWriterWord64 writer;
+ writer.WriteVariableWidthS64(val, chunk_length, zigzag_exponent);
+
+ BitReaderWord64 reader(writer.GetDataCopy());
+ int64_t read_val = 0;
+ ASSERT_TRUE(reader.ReadVariableWidthS64(&read_val, chunk_length,
+ zigzag_exponent));
+
+ ASSERT_EQ(val, read_val) << "Chunk length " << chunk_length;
+ }
+}
+
+TEST(VariableWidthWriteRead, SingleWriteReadU32) {
+ for (uint32_t i = 0; i < 100000; i += 123) {
+ const uint32_t val = i * i;
+ const size_t chunk_length = i % 16 + 1;
+
+ BitWriterWord64 writer;
+ writer.WriteVariableWidthU32(val, chunk_length);
+
+ BitReaderWord64 reader(writer.GetDataCopy());
+ uint32_t read_val = 0;
+ ASSERT_TRUE(reader.ReadVariableWidthU32(&read_val, chunk_length));
+
+ ASSERT_EQ(val, read_val) << "Chunk length " << chunk_length;
+ }
+}
+
+TEST(VariableWidthWriteRead, SingleWriteReadS32) {
+ for (int32_t i = 0; i < 100000; i += 123) {
+ const int32_t val = i * (i % 2 ? -i : i);
+ const size_t chunk_length = i % 16 + 1;
+ const size_t zigzag_exponent = i % 11;
+
+ BitWriterWord64 writer;
+ writer.WriteVariableWidthS32(val, chunk_length, zigzag_exponent);
+
+ BitReaderWord64 reader(writer.GetDataCopy());
+ int32_t read_val = 0;
+ ASSERT_TRUE(reader.ReadVariableWidthS32(
+ &read_val, chunk_length, zigzag_exponent));
+
+ ASSERT_EQ(val, read_val) << "Chunk length " << chunk_length;
+ }
+}
+
+TEST(VariableWidthWriteRead, SingleWriteReadU16) {
+ for (int i = 0; i < 65536; i += 123) {
+ const uint16_t val = static_cast<int16_t>(i);
+ const size_t chunk_length = val % 10 + 1;
+
+ BitWriterWord64 writer;
+ writer.WriteVariableWidthU16(val, chunk_length);
+
+ BitReaderWord64 reader(writer.GetDataCopy());
+ uint16_t read_val = 0;
+ ASSERT_TRUE(reader.ReadVariableWidthU16(&read_val, chunk_length));
+
+ ASSERT_EQ(val, read_val) << "Chunk length " << chunk_length;
+ }
+}
+
+TEST(VariableWidthWriteRead, SingleWriteReadS16) {
+ for (int i = -32768; i < 32768; i += 123) {
+ const int16_t val = static_cast<int16_t>(i);
+ const size_t chunk_length = std::abs(i) % 10 + 1;
+ const size_t zigzag_exponent = std::abs(i) % 7;
+
+ BitWriterWord64 writer;
+ writer.WriteVariableWidthS16(val, chunk_length, zigzag_exponent);
+
+ BitReaderWord64 reader(writer.GetDataCopy());
+ int16_t read_val = 0;
+ ASSERT_TRUE(reader.ReadVariableWidthS16(&read_val, chunk_length,
+ zigzag_exponent));
+
+ ASSERT_EQ(val, read_val) << "Chunk length " << chunk_length;
+ }
+}
+
+TEST(VariableWidthWriteRead, SingleWriteReadU8) {
+ for (int i = 0; i < 256; ++i) {
+ const uint8_t val = static_cast<uint8_t>(i);
+ const size_t chunk_length = val % 5 + 1;
+
+ BitWriterWord64 writer;
+ writer.WriteVariableWidthU8(val, chunk_length);
+
+ BitReaderWord64 reader(writer.GetDataCopy());
+ uint8_t read_val = 0;
+ ASSERT_TRUE(reader.ReadVariableWidthU8(&read_val, chunk_length));
+
+ ASSERT_EQ(val, read_val) << "Chunk length " << chunk_length;
+ }
+}
+
+TEST(VariableWidthWriteRead, SingleWriteReadS8) {
+ for (int i = -128; i < 128; ++i) {
+ const int8_t val = static_cast<int8_t>(i);
+ const size_t chunk_length = std::abs(i) % 5 + 1;
+ const size_t zigzag_exponent = std::abs(i) % 3;
+
+ BitWriterWord64 writer;
+ writer.WriteVariableWidthS8(val, chunk_length, zigzag_exponent);
+
+ BitReaderWord64 reader(writer.GetDataCopy());
+ int8_t read_val = 0;
+ ASSERT_TRUE(reader.ReadVariableWidthS8(&read_val, chunk_length,
+ zigzag_exponent));
+
+ ASSERT_EQ(val, read_val) << "Chunk length " << chunk_length;
+ }
+}
+
+TEST(VariableWidthWriteRead, SmallNumbersChunkLength4) {
+ const std::vector<uint64_t> expected_values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+ BitWriterWord64 writer;
+ for (uint64_t val : expected_values) {
+ writer.WriteVariableWidthU64(val, 4);
+ }
+
+ EXPECT_EQ(50u, writer.GetNumBits());
+
+ std::vector<uint64_t> actual_values;
+ BitReaderWord64 reader(writer.GetDataCopy());
+ while(!reader.OnlyZeroesLeft()) {
+ uint64_t val = 0;
+ ASSERT_TRUE(reader.ReadVariableWidthU64(&val, 4));
+ actual_values.push_back(val);
+ }
+
+ EXPECT_EQ(expected_values, actual_values);
+}
+
+TEST(VariableWidthWriteRead, VariedNumbersChunkLength8) {
+ const std::vector<uint64_t> expected_values = {1000, 0, 255, 4294967296};
+ const size_t kExpectedNumBits = 9 * (2 + 1 + 1 + 5);
+
+ BitWriterWord64 writer;
+ for (uint64_t val : expected_values) {
+ writer.WriteVariableWidthU64(val, 8);
+ }
+
+ EXPECT_EQ(kExpectedNumBits, writer.GetNumBits());
+
+ std::vector<uint64_t> actual_values;
+ BitReaderWord64 reader(writer.GetDataCopy());
+ while (!reader.OnlyZeroesLeft()) {
+ uint64_t val = 0;
+ ASSERT_TRUE(reader.ReadVariableWidthU64(&val, 8));
+ actual_values.push_back(val);
+ }
+
+ EXPECT_EQ(expected_values, actual_values);
+}
+
+} // anonymous namespace