#pragma once
#include <library/cpp/deprecated/accessors/accessors.h>
#include <util/stream/output.h>
#include <util/system/yassert.h>
#include <util/generic/bitops.h>
#include <util/generic/vector.h>
#include <util/generic/yexception.h>
namespace NBitIO {
// Based on junk/solar/codecs/bitstream.h
// Almost all code is hard tuned for sequential write performance.
// Use tools/bursttrie/benchmarks/bitstreams_benchmark to check your changes.
inline constexpr ui64 BytesUp(ui64 bits) {
return (bits + 7ULL) >> 3ULL;
}
template <typename TStorage>
class TBitOutputBase {
protected:
TStorage* Storage;
ui64 FreeBits;
ui64 Active;
ui64 Offset;
public:
TBitOutputBase(TStorage* storage)
: Storage(storage)
, FreeBits(64)
, Active()
, Offset()
{
}
ui64 GetOffset() const {
return Offset + BytesUp(64ULL - FreeBits);
}
ui64 GetBitOffset() const {
return (64ULL - FreeBits) & 7ULL;
}
ui64 GetByteReminder() const {
return FreeBits & 7ULL;
}
public:
// interface
// Write "bits" lower bits.
Y_FORCE_INLINE void Write(ui64 data, ui64 bits) {
if (FreeBits < bits) {
if (FreeBits) {
bits -= FreeBits;
Active |= (data & MaskLowerBits(FreeBits)) << (64ULL - FreeBits);
data >>= FreeBits;
FreeBits = 0ULL;
}
Flush();
}
Active |= bits ? ((data & MaskLowerBits(bits)) << (64ULL - FreeBits)) : 0;
FreeBits -= bits;
}
// Write "bits" lower bits starting from "skipbits" bit.
Y_FORCE_INLINE void Write(ui64 data, ui64 bits, ui64 skipbits) {
Write(data >> skipbits, bits);
}
// Unsigned wordwise write. Underlying data is splitted in "words" of "bits(data) + 1(flag)" bits.
// Like this: (unsigned char)0x2E<3> (0000 0010 1110) <=> 1110 0101
// fddd fddd
template <ui64 bits>
Y_FORCE_INLINE void WriteWords(ui64 data) {
do {
ui64 part = data;
data >>= bits;
part |= FastZeroIfFalse(data, NthBit64(bits));
Write(part, bits + 1ULL);
} while (data);
}
Y_FORCE_INLINE ui64 /* padded bits */ Flush() {
const ui64 ubytes = 8ULL - (FreeBits >> 3ULL);
if (ubytes) {
Active <<= FreeBits;
Active >>= FreeBits;
Storage->WriteData((const char*)&Active, (const char*)&Active + ubytes);
Offset += ubytes;
}
const ui64 padded = FreeBits & 7;
FreeBits = 64ULL;
Active = 0ULL;
return padded;
}
virtual ~TBitOutputBase() {
Flush();
}
private:
static Y_FORCE_INLINE ui64 FastZeroIfFalse(bool cond, ui64 iftrue) {
return -i64(cond) & iftrue;
}
};
template <typename TVec>
class TBitOutputVectorImpl {
TVec* Data;
public:
void WriteData(const char* begin, const char* end) {
NAccessors::Append(*Data, begin, end);
}
TBitOutputVectorImpl(TVec* data)
: Data(data)
{
}
};
template <typename TVec>
struct TBitOutputVector: public TBitOutputVectorImpl<TVec>, public TBitOutputBase<TBitOutputVectorImpl<TVec>> {
inline TBitOutputVector(TVec* data)
: TBitOutputVectorImpl<TVec>(data)
, TBitOutputBase<TBitOutputVectorImpl<TVec>>(this)
{
}
};
class TBitOutputArrayImpl {
char* Data;
size_t Left;
public:
void WriteData(const char* begin, const char* end) {
size_t sz = end - begin;
Y_VERIFY(sz <= Left, " ");
memcpy(Data, begin, sz);
Data += sz;
Left -= sz;
}
TBitOutputArrayImpl(char* begin, size_t len)
: Data(begin)
, Left(len)
{
}
};
struct TBitOutputArray: public TBitOutputArrayImpl, public TBitOutputBase<TBitOutputArrayImpl> {
inline TBitOutputArray(char* begin, size_t len)
: TBitOutputArrayImpl(begin, len)
, TBitOutputBase<TBitOutputArrayImpl>(this)
{
}
};
using TBitOutputYVector = TBitOutputVector<TVector<char>>;
class TBitOutputStreamImpl {
IOutputStream* Out;
public:
void WriteData(const char* begin, const char* end) {
Out->Write(begin, end - begin);
}
TBitOutputStreamImpl(IOutputStream* out)
: Out(out)
{
}
};
struct TBitOutputStream: public TBitOutputStreamImpl, public TBitOutputBase<TBitOutputStreamImpl> {
inline TBitOutputStream(IOutputStream* out)
: TBitOutputStreamImpl(out)
, TBitOutputBase<TBitOutputStreamImpl>(this)
{
}
};
}