diff options
author | Maxim Pimenov <m@maps.me> | 2015-07-15 17:04:19 +0300 |
---|---|---|
committer | Alex Zolotarev <alex@maps.me> | 2015-09-23 02:56:23 +0300 |
commit | 4b670d0671e6172ad41048ab8048a6a1a508902e (patch) | |
tree | 43e9da9292d7f29037b4e2f5ab44731b89cc4874 /coding/bit_streams.hpp | |
parent | c82b3e927f383a5a77863bd58b74ac1bbdc3675d (diff) |
[omim] [coding] BitReader and BitWriter.
Diffstat (limited to 'coding/bit_streams.hpp')
-rw-r--r-- | coding/bit_streams.hpp | 141 |
1 files changed, 113 insertions, 28 deletions
diff --git a/coding/bit_streams.hpp b/coding/bit_streams.hpp index 93d585f696..2751403cbf 100644 --- a/coding/bit_streams.hpp +++ b/coding/bit_streams.hpp @@ -1,42 +1,127 @@ -// Author: Artyom Polkovnikov. -// Bits source and sink for sequential read and write of bits. - #pragma once +#include "std/algorithm.hpp" #include "std/cstdint.hpp" +#include "std/limits.hpp" + +#include "base/assert.hpp" +#include "base/logging.hpp" -// Forward declarations. -class Reader; -class Writer; +namespace +{ +uint64_t const kByteMask = (static_cast<uint64_t>(1) << CHAR_BIT) - 1; +} // namespace -class BitSink +template <typename TWriter> +class BitWriter { public: - BitSink(Writer & writer); - // Note! Last byte is flushed in destructor. - ~BitSink(); - uint64_t NumBitsWritten() const { return m_size; } - // Write writeSize number of bits from least significant side of bits number. - void Write(uint64_t bits, uint32_t writeSize); + BitWriter(TWriter & writer) : m_writer(writer), m_buf(0), m_bitsWritten(0) {} + + ~BitWriter() + { + try + { + Flush(); + } + catch (...) + { + LOG(LWARNING, ("Caught an exception when flushing BitWriter.")); + } + } + + // Writes up to CHAR_BIT-1 last bits if they have not been written yet + // and pads them with zeros. + void Flush() + { + if (m_bitsWritten % CHAR_BIT != 0) + m_writer.Write(&m_buf, 1); + } + + // Returns the number of bits that have been sent to BitWriter, + // including those that are in m_buf and are possibly + // not flushed yet. + uint64_t BitsWritten() const { return m_bitsWritten; } + + // Writes n bits starting with the least significant bit. + // They are written one byte at a time so endianness is of no concern. + // All the other bits except for the first n must be set to zero. + void Write(uint8_t bits, uint32_t n) + { + if (n == 0) + return; + CHECK_LESS_OR_EQUAL(n, CHAR_BIT, ()); + uint32_t bufferedBits = m_bitsWritten % CHAR_BIT; + m_bitsWritten += n; + if (n + bufferedBits > 8) + { + uint8_t b = (bits << bufferedBits) | m_buf; + m_writer.Write(&b, 1); + m_buf = bits >> (8 - bufferedBits); + } + else + { + if (bufferedBits > 0) + { + bits = (bits << bufferedBits) | m_buf; + n += bufferedBits; + } + if (n == CHAR_BIT) + { + m_writer.Write(&bits, 1); + bits = 0; + } + m_buf = bits; + } + } + private: - Writer & m_writer; - uint8_t m_lastByte; - uint64_t m_size; - uint64_t m_totalBits; + TWriter & m_writer; + uint8_t m_buf; + uint64_t m_bitsWritten; }; -class BitSource +template <typename TReader> +class BitReader { public: - BitSource(Reader & reader); - uint64_t NumBitsRead() const { return m_totalBitsRead; } - // Read readSize number of bits, return it as least significant bits of 64-bit number. - uint64_t Read(uint32_t readSize); + BitReader(TReader & reader) : m_reader(reader), m_bitsRead(0), m_bufferedBits(0), m_buf(0) {} + + // Returns the total number of bits read from this BitReader. + uint32_t BitsRead() const { return m_bitsRead; } + + // Reads n bits and returns them as the least significant bits of an 8-bit number. + // The underlying m_reader is supposed to be byte-aligned (which is the + // case when it reads from the place that was written with BitWriter) because + // Read may use one lookahead byte. + uint8_t Read(uint32_t n) + { + if (n == 0) + return 0; + CHECK_LESS_OR_EQUAL(n, CHAR_BIT, ()); + m_bitsRead += n; + uint8_t result = 0; + if (n <= m_bufferedBits) + { + result = m_buf & (kByteMask >> (CHAR_BIT - n)); + m_bufferedBits -= n; + m_buf >>= n; + } + else + { + uint8_t nextByte; + m_reader.Read(&nextByte, 1); + uint32_t low = n - m_bufferedBits; + result = ((nextByte & (kByteMask >> (CHAR_BIT - low))) << m_bufferedBits) | m_buf; + m_buf = nextByte >> low; + m_bufferedBits = CHAR_BIT - low; + } + return result; + } + private: - Reader & m_reader; - uint64_t m_serialCur; - uint64_t m_serialEnd; - uint64_t m_bits; - uint32_t m_bitsSize; - uint64_t m_totalBitsRead; + TReader & m_reader; + uint32_t m_bitsRead; + uint32_t m_bufferedBits; + uint8_t m_buf; }; |