Changeset View
Standalone View
src/streams.h
Show First 20 Lines • Show All 464 Lines • ▼ Show 20 Lines | void Xor(const std::vector<uint8_t> &key) { | ||||
// important that we calculate `j`, i.e. the `key` index in this way | // important that we calculate `j`, i.e. the `key` index in this way | ||||
// instead of doing a %, which would effectively be a division for | // instead of doing a %, which would effectively be a division for | ||||
// each byte Xor'd -- much slower than need be. | // each byte Xor'd -- much slower than need be. | ||||
if (j == key.size()) j = 0; | if (j == key.size()) j = 0; | ||||
} | } | ||||
} | } | ||||
}; | }; | ||||
template <typename IStream> class BitStreamReader { | |||||
private: | |||||
IStream &m_istream; | |||||
/// Buffered byte read in from the input stream. A new byte is read into the | |||||
/// buffer when m_offset reaches 8. | |||||
uint8_t m_buffer{0}; | |||||
/// Number of high order bits in m_buffer already returned by previous | |||||
/// Read() calls. The next bit to be returned is at this offset from the | |||||
/// most significant bit position. | |||||
int m_offset{8}; | |||||
public: | |||||
explicit BitStreamReader(IStream &istream) : m_istream(istream) {} | |||||
/** Read the specified number of bits from the stream. The data is returned | |||||
FabienUnsubmitted Done Inline ActionsFabien: ```
/**
* Read the specified ...
``` | |||||
* in the nbits least signficant bits of a 64-bit uint. | |||||
FabienUnsubmitted Done Inline Actionssignficant => significant Fabien: `signficant` => `significant` | |||||
markblundebergAuthorUnsubmitted Done Inline ActionsInterestingly, Core has a codespell thing that catches these... https://github.com/bitcoin/bitcoin/pull/13954#issuecomment-416261394 markblundeberg: Interestingly, Core has a codespell thing that catches these... https://github. | |||||
*/ | |||||
uint64_t Read(int nbits) { | |||||
if (nbits < 0 || nbits > 64) { | |||||
throw std::out_of_range("nbits must be between 0 and 64"); | |||||
FabienUnsubmitted Done Inline Actions0 being a valid value is questionable. Fabien: 0 being a valid value is questionable.
It likely indicates that the caller is misbehaving, and… | |||||
markblundebergAuthorUnsubmitted Done Inline ActionsIndeed for any value of nbits, the result is valid for any greater number of nbits. The GCSFilter class constructor defaults to P=0, and would thus by default end up calling Read(0) for every element. This is a degenerate case of golomb filter where each element is encoded in plain unary, not very practical but technically valid. It won't actually get used in the block filters where P=19, but I'd prefer to allow nbits=0 for the simplicity. (I could also imagine a practical protocol where Read(0) might be used: consider a bitwise varint protocol that encodes the number of bits in an upcoming integer, then followed by the content of the integer itself. The minimal encoding of the content of the integer 0 would be a zero bitlength; the encoder/decoder would then benefit from being able to write/read zero bitlength.) markblundeberg: Indeed for any value of nbits, the result is valid for any greater number of nbits.
The… | |||||
} | |||||
uint64_t data = 0; | |||||
while (nbits > 0) { | |||||
if (m_offset == 8) { | |||||
m_istream >> m_buffer; | |||||
m_offset = 0; | |||||
} | |||||
int bits = std::min(8 - m_offset, nbits); | |||||
data <<= bits; | |||||
data |= static_cast<uint8_t>(m_buffer << m_offset) >> (8 - bits); | |||||
m_offset += bits; | |||||
nbits -= bits; | |||||
} | |||||
return data; | |||||
} | |||||
}; | |||||
template <typename OStream> class BitStreamWriter { | |||||
private: | |||||
OStream &m_ostream; | |||||
/// Buffered byte waiting to be written to the output stream. The byte is | |||||
/// written buffer when m_offset reaches 8 or Flush() is called. | |||||
uint8_t m_buffer{0}; | |||||
/// Number of high order bits in m_buffer already written by previous | |||||
/// Write() calls and not yet flushed to the stream. The next bit to be | |||||
/// written to is at this offset from the most significant bit position. | |||||
int m_offset{0}; | |||||
public: | |||||
explicit BitStreamWriter(OStream &ostream) : m_ostream(ostream) {} | |||||
~BitStreamWriter() { Flush(); } | |||||
/** Write the nbits least significant bits of a 64-bit int to the output | |||||
FabienUnsubmitted Done Inline ActionsDito. Fabien: Dito. | |||||
* stream. Data is buffered until it completes an octet. | |||||
*/ | |||||
void Write(uint64_t data, int nbits) { | |||||
if (nbits < 0 || nbits > 64) { | |||||
throw std::out_of_range("nbits must be between 0 and 64"); | |||||
} | |||||
while (nbits > 0) { | |||||
int bits = std::min(8 - m_offset, nbits); | |||||
m_buffer |= (data << (64 - nbits)) >> (64 - 8 + m_offset); | |||||
m_offset += bits; | |||||
nbits -= bits; | |||||
if (m_offset == 8) { | |||||
Flush(); | |||||
} | |||||
} | |||||
} | |||||
/** Flush any unwritten bits to the output stream, padding with 0's to the | |||||
FabienUnsubmitted Done Inline ActionsDito. Fabien: Dito. | |||||
* next byte boundary. | |||||
*/ | |||||
void Flush() { | |||||
if (m_offset == 0) { | |||||
return; | |||||
} | |||||
m_ostream << m_buffer; | |||||
m_buffer = 0; | |||||
m_offset = 0; | |||||
} | |||||
}; | |||||
/** | /** | ||||
* Non-refcounted RAII wrapper for FILE* | * Non-refcounted RAII wrapper for FILE* | ||||
* | * | ||||
* Will automatically close the file when it goes out of scope if not null. If | * Will automatically close the file when it goes out of scope if not null. If | ||||
* you're returning the file pointer, return file.release(). If you need to | * you're returning the file pointer, return file.release(). If you need to | ||||
* close the file early, use file.fclose() instead of fclose(file). | * close the file early, use file.fclose() instead of fclose(file). | ||||
*/ | */ | ||||
class CAutoFile { | class CAutoFile { | ||||
▲ Show 20 Lines • Show All 247 Lines • Show Last 20 Lines |