Skip to content

Commit fe943f9

Browse files
Jim Posenjimpo
authored andcommitted
streams: Implement BitStreamReader/Writer classes.
Golomb-Rice coding, as specified in BIP 158, involves operations on individual bits. These classes will be used to implement the encoding/decoding operations.
1 parent 87f2d9e commit fe943f9

File tree

1 file changed

+93
-0
lines changed

1 file changed

+93
-0
lines changed

src/streams.h

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,12 +510,105 @@ class CDataStream
510510
}
511511
};
512512

513+
template <typename IStream>
514+
class BitStreamReader
515+
{
516+
private:
517+
IStream& m_istream;
518+
519+
/// Buffered byte read in from the input stream. A new byte is read into the
520+
/// buffer when m_offset reaches 8.
521+
uint8_t m_buffer{0};
522+
523+
/// Number of high order bits in m_buffer already returned by previous
524+
/// Read() calls. The next bit to be returned is at this offset from the
525+
/// most significant bit position.
526+
int m_offset{8};
527+
528+
public:
529+
explicit BitStreamReader(IStream& istream) : m_istream(istream) {}
530+
531+
/** Read the specified number of bits from the stream. The data is returned
532+
* in the nbits least signficant bits of a 64-bit uint.
533+
*/
534+
uint64_t Read(int nbits) {
535+
if (nbits < 0 || nbits > 64) {
536+
throw std::out_of_range("nbits must be between 0 and 64");
537+
}
538+
539+
uint64_t data = 0;
540+
while (nbits > 0) {
541+
if (m_offset == 8) {
542+
m_istream >> m_buffer;
543+
m_offset = 0;
544+
}
545+
546+
int bits = std::min(8 - m_offset, nbits);
547+
data <<= bits;
548+
data |= static_cast<uint8_t>(m_buffer << m_offset) >> (8 - bits);
549+
m_offset += bits;
550+
nbits -= bits;
551+
}
552+
return data;
553+
}
554+
};
555+
556+
template <typename OStream>
557+
class BitStreamWriter
558+
{
559+
private:
560+
OStream& m_ostream;
513561

562+
/// Buffered byte waiting to be written to the output stream. The byte is
563+
/// written buffer when m_offset reaches 8 or Flush() is called.
564+
uint8_t m_buffer{0};
514565

566+
/// Number of high order bits in m_buffer already written by previous
567+
/// Write() calls and not yet flushed to the stream. The next bit to be
568+
/// written to is at this offset from the most significant bit position.
569+
int m_offset{0};
515570

571+
public:
572+
explicit BitStreamWriter(OStream& ostream) : m_ostream(ostream) {}
516573

574+
~BitStreamWriter()
575+
{
576+
Flush();
577+
}
517578

579+
/** Write the nbits least significant bits of a 64-bit int to the output
580+
* stream. Data is buffered until it completes an octet.
581+
*/
582+
void Write(uint64_t data, int nbits) {
583+
if (nbits < 0 || nbits > 64) {
584+
throw std::out_of_range("nbits must be between 0 and 64");
585+
}
586+
587+
while (nbits > 0) {
588+
int bits = std::min(8 - m_offset, nbits);
589+
m_buffer |= (data << (64 - nbits)) >> (64 - 8 + m_offset);
590+
m_offset += bits;
591+
nbits -= bits;
518592

593+
if (m_offset == 8) {
594+
Flush();
595+
}
596+
}
597+
}
598+
599+
/** Flush any unwritten bits to the output stream, padding with 0's to the
600+
* next byte boundary.
601+
*/
602+
void Flush() {
603+
if (m_offset == 0) {
604+
return;
605+
}
606+
607+
m_ostream << m_buffer;
608+
m_buffer = 0;
609+
m_offset = 0;
610+
}
611+
};
519612

520613

521614

0 commit comments

Comments
 (0)