#include <limits.h>
#include <stdlib.h>
#ifndef inline_
#define inline_
#endif
#define unused_(x) ((void)(x))
#ifdef BIT_STREAM_WORD_TYPE
typedef BIT_STREAM_WORD_TYPE word;
#else
typedef uint64 word;
#endif
#define wsize ((uint)(CHAR_BIT * sizeof(word)))
struct bitstream {
uint bits;
word buffer;
word* ptr;
word* begin;
word* end;
#ifdef BIT_STREAM_STRIDED
size_t mask;
ptrdiff_t delta;
#endif
};
static word
stream_read_word(bitstream* s)
{
word w = *s->ptr++;
#ifdef BIT_STREAM_STRIDED
if (!((s->ptr - s->begin) & s->mask))
s->ptr += s->delta;
#endif
return w;
}
static void
stream_write_word(bitstream* s, word value)
{
*s->ptr++ = value;
#ifdef BIT_STREAM_STRIDED
if (!((s->ptr - s->begin) & s->mask))
s->ptr += s->delta;
#endif
}
inline_ size_t
stream_alignment(void)
{
return wsize;
}
inline_ void*
stream_data(const bitstream* s)
{
return s->begin;
}
inline_ size_t
stream_size(const bitstream* s)
{
return sizeof(word) * (size_t)(s->ptr - s->begin);
}
inline_ size_t
stream_capacity(const bitstream* s)
{
return sizeof(word) * (size_t)(s->end - s->begin);
}
inline_ size_t
stream_stride_block(const bitstream* s)
{
#ifdef BIT_STREAM_STRIDED
return s->mask + 1;
#else
unused_(s);
return 1;
#endif
}
inline_ ptrdiff_t
stream_stride_delta(const bitstream* s)
{
#ifdef BIT_STREAM_STRIDED
return s->delta / (s->mask + 1);
#else
unused_(s);
return 0;
#endif
}
inline_ uint
stream_read_bit(bitstream* s)
{
uint bit;
if (!s->bits) {
s->buffer = stream_read_word(s);
s->bits = wsize;
}
s->bits--;
bit = (uint)s->buffer & 1u;
s->buffer >>= 1;
return bit;
}
inline_ uint
stream_write_bit(bitstream* s, uint bit)
{
s->buffer += (word)bit << s->bits;
if (++s->bits == wsize) {
stream_write_word(s, s->buffer);
s->buffer = 0;
s->bits = 0;
}
return bit;
}
inline_ uint64
stream_read_bits(bitstream* s, uint n)
{
uint64 value = s->buffer;
if (s->bits < n) {
do {
s->buffer = stream_read_word(s);
value += (uint64)s->buffer << s->bits;
s->bits += wsize;
} while (sizeof(s->buffer) < sizeof(value) && s->bits < n);
s->bits -= n;
if (!s->bits) {
s->buffer = 0;
}
else {
s->buffer >>= wsize - s->bits;
value &= ((uint64)2 << (n - 1)) - 1;
}
}
else {
s->bits -= n;
s->buffer >>= n;
value &= ((uint64)1 << n) - 1;
}
return value;
}
inline_ uint64
stream_write_bits(bitstream* s, uint64 value, uint n)
{
s->buffer += (word)(value << s->bits);
s->bits += n;
if (s->bits >= wsize) {
value >>= 1;
n--;
do {
s->bits -= wsize;
stream_write_word(s, s->buffer);
s->buffer = (word)(value >> (n - s->bits));
} while (sizeof(s->buffer) < sizeof(value) && s->bits >= wsize);
}
s->buffer &= ((word)1 << s->bits) - 1;
return value >> n;
}
inline_ size_t
stream_rtell(const bitstream* s)
{
return wsize * (size_t)(s->ptr - s->begin) - s->bits;
}
inline_ size_t
stream_wtell(const bitstream* s)
{
return wsize * (size_t)(s->ptr - s->begin) + s->bits;
}
inline_ void
stream_rewind(bitstream* s)
{
s->ptr = s->begin;
s->buffer = 0;
s->bits = 0;
}
inline_ void
stream_rseek(bitstream* s, size_t offset)
{
uint n = offset % wsize;
s->ptr = s->begin + offset / wsize;
if (n) {
s->buffer = stream_read_word(s) >> n;
s->bits = wsize - n;
}
else {
s->buffer = 0;
s->bits = 0;
}
}
inline_ void
stream_wseek(bitstream* s, size_t offset)
{
uint n = offset % wsize;
s->ptr = s->begin + offset / wsize;
if (n) {
word buffer = *s->ptr;
buffer &= ((word)1 << n) - 1;
s->buffer = buffer;
s->bits = n;
}
else {
s->buffer = 0;
s->bits = 0;
}
}
inline_ void
stream_skip(bitstream* s, uint n)
{
stream_rseek(s, stream_rtell(s) + n);
}
inline_ void
stream_pad(bitstream* s, uint n)
{
for (s->bits += n; s->bits >= wsize; s->bits -= wsize) {
stream_write_word(s, s->buffer);
s->buffer = 0;
}
}
inline_ size_t
stream_align(bitstream* s)
{
uint bits = s->bits;
if (bits)
stream_skip(s, bits);
return bits;
}
inline_ size_t
stream_flush(bitstream* s)
{
uint bits = (wsize - s->bits) % wsize;
if (bits)
stream_pad(s, bits);
return bits;
}
inline_ void
stream_copy(bitstream* dst, bitstream* src, size_t n)
{
while (n > wsize) {
word w = (word)stream_read_bits(src, wsize);
stream_write_bits(dst, w, wsize);
n -= wsize;
}
if (n) {
word w = (word)stream_read_bits(src, (uint)n);
stream_write_bits(dst, w, (uint)n);
}
}
#ifdef BIT_STREAM_STRIDED
inline_ int
stream_set_stride(bitstream* s, size_t block, ptrdiff_t delta)
{
if (block & (block - 1))
return 0;
s->mask = block - 1;
s->delta = delta * block;
return 1;
}
#endif
inline_ bitstream*
stream_open(void* buffer, size_t bytes)
{
bitstream* s = (bitstream*)malloc(sizeof(bitstream));
if (s) {
s->begin = (word*)buffer;
s->end = s->begin + bytes / sizeof(word);
#ifdef BIT_STREAM_STRIDED
stream_set_stride(s, 0, 0);
#endif
stream_rewind(s);
}
return s;
}
inline_ void
stream_close(bitstream* s)
{
free(s);
}
inline_ bitstream*
stream_clone(const bitstream* s)
{
bitstream* c = (bitstream*)malloc(sizeof(bitstream));
if (c)
*c = *s;
return c;
}
#undef unused_