948 lines
21 KiB
C
948 lines
21 KiB
C
// TODO: Safety check that functions taking byte length can't overflow bit conversion (log2(num_bytes) > 61)
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Buff management
|
|
|
|
BB_Buff BB_AcquireDynamicBuff(u64 arena_reserve)
|
|
{
|
|
BB_Buff result = Zi;
|
|
result.arena = AcquireArena(arena_reserve);
|
|
result.is_backed_by_arena = 1;
|
|
return result;
|
|
}
|
|
|
|
void BB_ReleaseDynamicBuff(BB_Buff *bb)
|
|
{
|
|
// Only arena bitbuffs need to be released
|
|
if (bb->is_backed_by_arena)
|
|
{
|
|
ReleaseArena(bb->arena);
|
|
}
|
|
}
|
|
|
|
BB_Buff BB_BuffFromString(String s)
|
|
{
|
|
BB_Buff result = Zi;
|
|
result.fixed_buffer = s;
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Writer management
|
|
|
|
BB_Writer BB_WriterFromBuff(BB_Buff *bb)
|
|
{
|
|
BB_Writer result = Zi;
|
|
result.bb = bb;
|
|
if (bb->is_backed_by_arena)
|
|
{
|
|
result.base = ArenaFirst(bb->arena, u8);
|
|
}
|
|
else
|
|
{
|
|
result.base = bb->fixed_buffer.text;
|
|
}
|
|
result.cur_bit = 0;
|
|
#if BITBUFF_DEBUG
|
|
result.debug_enabled = 1;
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
// Use this when writing external formats that will not verify bitbuff debug symbols / magic numbers
|
|
BB_Writer BB_WriterFromBuffNoDebug(BB_Buff *bb)
|
|
{
|
|
BB_Writer result = BB_WriterFromBuff(bb);
|
|
#if BITBUFF_DEBUG
|
|
result.debug_enabled = 0;
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
void BB_ResetWriter(BB_Writer *bbw)
|
|
{
|
|
bbw->overflowed = 0;
|
|
bbw->cur_bit = 0;
|
|
if (bbw->bb->is_backed_by_arena)
|
|
{
|
|
ResetArena(bbw->bb->arena);
|
|
}
|
|
}
|
|
|
|
// FIXME: Handle overflowed bbw
|
|
u64 BB_GetNumBitsWritten(BB_Writer *bbw)
|
|
{
|
|
return bbw->cur_bit;
|
|
}
|
|
|
|
// FIXME: Handle overflowed bbw
|
|
u64 BB_GetNumBytesWritten(BB_Writer *bbw)
|
|
{
|
|
return (bbw->cur_bit + 7) >> 3;
|
|
}
|
|
|
|
// FIXME: Handle overflowed bbw
|
|
String BB_GetWritten(Arena *arena, BB_Writer *bbw)
|
|
{
|
|
String result = Zi;
|
|
result.len = (bbw->cur_bit + 7) >> 3;
|
|
result.text = PushStructsNoZero(arena, u8, result.len);
|
|
CopyBytes(result.text, bbw->base, result.len);
|
|
return result;
|
|
}
|
|
|
|
// FIXME: Handle overflowed bbw
|
|
u8 *BB_GetWrittenRaw(BB_Writer *bbw)
|
|
{
|
|
return bbw->base;
|
|
}
|
|
|
|
// Returns 1 if num_bits would cause the writer to overflow its fixed buffer size (if writer is not backed by a dynamic arena bitbuff)
|
|
b32 BB_CheckWriterOverflowBits(BB_Writer *bbw, u64 num_bits)
|
|
{
|
|
b32 result = 0;
|
|
BB_Buff *bb = bbw->bb;
|
|
if (bbw->overflowed)
|
|
{
|
|
result = 1;
|
|
}
|
|
else
|
|
{
|
|
u64 bytes_needed = (bbw->cur_bit + num_bits + 7) >> 3;
|
|
if (bb->is_backed_by_arena)
|
|
{
|
|
Arena *arena = bb->arena;
|
|
if (bytes_needed >= arena->pos)
|
|
{
|
|
// Grow arena
|
|
u64 push_size = (((bytes_needed - arena->pos) / BB_WriterOverflowArenaPushSize) + 1) * BB_WriterOverflowArenaPushSize;
|
|
PushStructsNoZero(arena, u8, push_size);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
u64 max_len = bb->fixed_buffer.len;
|
|
if (bytes_needed > max_len)
|
|
{
|
|
// Writer overflowed fixed buffer
|
|
#if BITBUFF_DEBUG
|
|
Assert(0);
|
|
#endif
|
|
result = 1;
|
|
bbw->cur_bit = max_len << 3;
|
|
bbw->overflowed = 1;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Align writer
|
|
|
|
// Align the pos to the next byte
|
|
void BB_WriteAlignToNextByte(BB_Writer *bbw)
|
|
{
|
|
#if BITBUFF_DEBUG
|
|
if ((bbw->cur_bit & 7) != 0)
|
|
{
|
|
BB_WriteDebugMagic(bbw, BB_DebugMagicKind_AlignNextByte, 0);
|
|
}
|
|
#endif
|
|
bbw->cur_bit += (8 - (bbw->cur_bit & 7)) & 7;
|
|
}
|
|
|
|
void BB_WriteAlignBytes(BB_Writer *bbw, u64 align)
|
|
{
|
|
BB_WriteAlignToNextByte(bbw);
|
|
BB_WriteDebugMagic(bbw, BB_DebugMagicKind_AlignBytes, align);
|
|
if (align > 0)
|
|
{
|
|
u64 new_pos = (bbw->cur_bit >> 3);
|
|
new_pos += (align - 1);
|
|
new_pos -= new_pos % align;
|
|
if (BB_CheckWriterOverflowBits(bbw, (new_pos << 3) - bbw->cur_bit))
|
|
{
|
|
return;
|
|
}
|
|
bbw->cur_bit = new_pos << 3;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Write bits
|
|
|
|
void BB_WriteUBitsNoMagic(BB_Writer *bbw, u64 value, u8 num_bits)
|
|
{
|
|
Assert(num_bits > 0 && (num_bits == 64 || value <= ~(U64Max << num_bits))); // Bit count must be able to hold value
|
|
if (BB_CheckWriterOverflowBits(bbw, num_bits))
|
|
{
|
|
return;
|
|
}
|
|
|
|
u8 offset = bbw->cur_bit & 7;
|
|
if (offset != 0)
|
|
{
|
|
// Write unaligned bits
|
|
u8 *at = bbw->base + (bbw->cur_bit >> 3);
|
|
u8 num_mix_bits = MinU8((8 - offset), num_bits);
|
|
u8 mix_byte = (u8)((value & ((1 << num_mix_bits) - 1)) << offset);
|
|
*at |= mix_byte;
|
|
value >>= num_mix_bits;
|
|
num_bits -= num_mix_bits;
|
|
bbw->cur_bit += num_mix_bits;
|
|
}
|
|
|
|
// cur_bit is now aligned to byte
|
|
u8 *at = bbw->base + (bbw->cur_bit >> 3);
|
|
u8 num_bytes = (num_bits + 7) >> 3;
|
|
CopyBytes(at, &value, num_bytes);
|
|
bbw->cur_bit += num_bits;
|
|
}
|
|
|
|
void BB_WriteUBits(BB_Writer *bbw, u64 value, u8 num_bits)
|
|
{
|
|
BB_WriteDebugMagic(bbw, BB_DebugMagicKind_UBits, num_bits);
|
|
BB_WriteUBitsNoMagic(bbw, value, num_bits);
|
|
}
|
|
|
|
void BB_WriteIBits(BB_Writer *bbw, i64 value, u8 num_bits)
|
|
{
|
|
BB_WriteDebugMagic(bbw, BB_DebugMagicKind_IBits, num_bits);
|
|
u64 ubits;
|
|
if (value >= 0)
|
|
{
|
|
ubits = value;
|
|
}
|
|
else
|
|
{
|
|
ubits = BB_TwosComplimentFromUint(-value, num_bits);
|
|
}
|
|
BB_WriteUBits(bbw, ubits, num_bits);
|
|
}
|
|
|
|
// Returns written bit to make writing delta encoding logic cleaner
|
|
b32 BB_WriteBit(BB_Writer *bbw, u8 value)
|
|
{
|
|
BB_WriteUBits(bbw, value, 1);
|
|
return value;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Write variable length integers
|
|
|
|
// Writes a variable length unsigned integer.
|
|
// Value is written in chunks of 7 bits w/ 8th bit signaling continuation.
|
|
void BB_WriteUV(BB_Writer *bbw, u64 value)
|
|
{
|
|
BB_WriteDebugMagic(bbw, BB_DebugMagicKind_UV, 0);
|
|
while (value > 0x7F)
|
|
{
|
|
u8 cont_byte = 0x80 | (value & 0x7F);
|
|
BB_WriteUBits(bbw, cont_byte, 8);
|
|
value >>= 7;
|
|
}
|
|
BB_WriteUBits(bbw, value, 8);
|
|
}
|
|
|
|
// Writes a variable length signed integer.
|
|
// Similar to BB_WriteUV, except the 7th bit of the first byte is a sign bit
|
|
// indicating that the value is stored in twos compliment.
|
|
void BB_WriteIV(BB_Writer *bbw, i64 value)
|
|
{
|
|
BB_WriteDebugMagic(bbw, BB_DebugMagicKind_IV, 0);
|
|
u8 sign_bit;
|
|
u64 tc;
|
|
if (value >= 0)
|
|
{
|
|
sign_bit = 0;
|
|
tc = value;
|
|
}
|
|
else
|
|
{
|
|
sign_bit = 1;
|
|
u64 unsigned_value = -value;
|
|
u8 num_bits = 6;
|
|
unsigned_value >>= 6;
|
|
while (unsigned_value > 0)
|
|
{
|
|
num_bits += 7;
|
|
unsigned_value >>= 7;
|
|
}
|
|
num_bits = MinU8(num_bits, 64);
|
|
tc = BB_TwosComplimentFromUint(-value, num_bits);
|
|
}
|
|
|
|
// First byte contains not just cont bit, but sign bit as well.
|
|
u8 first_byte = (tc & 0x3F);
|
|
tc >>= 6;
|
|
first_byte |= (tc > 0) << 7; // Cont bit
|
|
first_byte |= sign_bit << 6; // Sign bit
|
|
BB_WriteUBits(bbw, first_byte, 8);
|
|
|
|
if (tc > 0)
|
|
{
|
|
while (tc > 0x7F)
|
|
{
|
|
u8 cont_byte = 0x80 | (tc & 0x7F);
|
|
BB_WriteUBits(bbw, cont_byte, 8);
|
|
tc >>= 7;
|
|
}
|
|
BB_WriteUBits(bbw, tc, 8);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Write floating point
|
|
|
|
void BB_WriteF32(BB_Writer *bbw, f32 value)
|
|
{
|
|
BB_WriteDebugMagic(bbw, BB_DebugMagicKind_F32, 0);
|
|
BB_WriteUBits(bbw, *(u32 *)&value, 32);
|
|
}
|
|
|
|
void BB_WriteF64(BB_Writer *bbw, f64 value)
|
|
{
|
|
BB_WriteDebugMagic(bbw, BB_DebugMagicKind_F64, 0);
|
|
BB_WriteUBits(bbw, *(u64 *)&value, 64);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Write Uid
|
|
|
|
void BB_WriteUid(BB_Writer *bbw, Uid value)
|
|
{
|
|
BB_WriteDebugMagic(bbw, BB_DebugMagicKind_Uid, 128);
|
|
BB_WriteUBits(bbw, value.hi, 64);
|
|
BB_WriteUBits(bbw, value.lo, 64);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Write raw data
|
|
|
|
void BB_WriteString(BB_Writer *bbw, String s)
|
|
{
|
|
BB_WriteDebugMagic(bbw, BB_DebugMagicKind_String, 0);
|
|
BB_WriteUV(bbw, s.len);
|
|
BB_WriteBytes(bbw, s);
|
|
}
|
|
|
|
void BB_WriteBytes(BB_Writer *bbw, String bytes)
|
|
{
|
|
// Align start of bytes
|
|
BB_WriteAlignToNextByte(bbw);
|
|
|
|
u64 num_bits = bytes.len << 3;
|
|
if (BB_CheckWriterOverflowBits(bbw, num_bits))
|
|
{
|
|
return;
|
|
}
|
|
|
|
u8 *at = bbw->base + (bbw->cur_bit >> 3);
|
|
CopyBytes(at, bytes.text, bytes.len);
|
|
bbw->cur_bit += num_bits;
|
|
}
|
|
|
|
void BB_WriteSeekBytes(BB_Writer *bbw, u64 num_bytes)
|
|
{
|
|
BB_WriteAlignToNextByte(bbw);
|
|
|
|
u64 num_bits = num_bytes << 3;
|
|
if (BB_CheckWriterOverflowBits(bbw, num_bits))
|
|
{
|
|
return;
|
|
}
|
|
|
|
bbw->cur_bit += num_bits;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Writer debug
|
|
|
|
#if BITBUFF_DEBUG
|
|
void BB_WriteDebugMarker(BB_Writer *bbw, String name)
|
|
{
|
|
bbw->cur_bit += (8 - (bbw->cur_bit & 7)) & 7;
|
|
for (u64 i = 0; i < name.len; ++i)
|
|
{
|
|
BB_WriteUBitsNoMagic(bbw, name.text[i], 8);
|
|
}
|
|
}
|
|
|
|
void BB_WriteDebugMagic(BB_Writer *bbw, BB_DebugMagicKind magic, u8 num_bits)
|
|
{
|
|
if (bbw->debug_enabled)
|
|
{
|
|
if (BB_CheckWriterOverflowBits(bbw, 24))
|
|
{
|
|
return;
|
|
}
|
|
u64 magic_ubits = (u64)magic | ((u64)num_bits << 16);
|
|
BB_WriteUBitsNoMagic(bbw, magic_ubits, 24);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Reader management
|
|
|
|
BB_Reader BB_ReaderFromBuff(BB_Buff *bb)
|
|
{
|
|
BB_Reader result = Zi;
|
|
if (!bb->is_backed_by_arena)
|
|
{
|
|
result.base = bb->fixed_buffer.text;
|
|
result.base_len = bb->fixed_buffer.len;
|
|
}
|
|
else
|
|
{
|
|
Arena *arena = bb->arena;
|
|
result.base = ArenaFirst(arena, u8);
|
|
result.base_len = arena->pos;
|
|
}
|
|
result.cur_bit = 0;
|
|
#if BITBUFF_DEBUG
|
|
result.debug_enabled = 1;
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
// Use this when reading from external formats that will not contain bitbuff debug symbols / magic numbers
|
|
BB_Reader BB_ReaderFromBuffNoDebug(BB_Buff *bb)
|
|
{
|
|
BB_Reader result = BB_ReaderFromBuff(bb);
|
|
#if BITBUFF_DEBUG
|
|
result.debug_enabled = 0;
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
// Returns the number of bits read from the bitbuff
|
|
// FIXME: Handle overflowed bbr
|
|
u64 BB_GetCurrentReaderBit(BB_Reader *bbr)
|
|
{
|
|
return bbr->cur_bit;
|
|
}
|
|
|
|
// Returns the number of *full* bytes read from the bitbuff
|
|
// FIXME: Handle overflowed bbr
|
|
u64 BB_GetCurrentReaderByte(BB_Reader *bbr)
|
|
{
|
|
return bbr->cur_bit >> 3;
|
|
}
|
|
|
|
// Returns the number of bits left until the bitbuff overflows
|
|
// FIXME: Handle overflowed bbr
|
|
u64 BB_NumBitsRemaining(BB_Reader *bbr)
|
|
{
|
|
return (bbr->base_len << 3) - bbr->cur_bit;
|
|
}
|
|
|
|
// Returns the number of *full* bytes left until the bitbuff overflows
|
|
// FIXME: Handle overflowed bbr
|
|
u64 BB_NumBytesRemaining(BB_Reader *bbr)
|
|
{
|
|
return bbr->base_len - (bbr->cur_bit >> 3);
|
|
}
|
|
|
|
b32 BB_CheckReaderOverflowBits(BB_Reader *bbr, u64 num_bits)
|
|
{
|
|
b32 result = 0;
|
|
if (bbr->overflowed)
|
|
{
|
|
result = 1;
|
|
}
|
|
else
|
|
{
|
|
u64 bits_needed = bbr->cur_bit + num_bits;
|
|
u64 base_len_bits = bbr->base_len << 3;
|
|
if (bits_needed > base_len_bits)
|
|
{
|
|
// Tried to read past bitbuff memory
|
|
#if BITBUFF_DEBUG
|
|
Assert(0);
|
|
#endif
|
|
result = 1;
|
|
bbr->cur_bit = base_len_bits;
|
|
bbr->overflowed = 1;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Align reader
|
|
|
|
// Align the pos to the next byte
|
|
void BB_ReadAlignToNextByte(BB_Reader *bbr)
|
|
{
|
|
#if BITBUFF_DEBUG
|
|
if ((bbr->cur_bit & 7) != 0)
|
|
{
|
|
BB_ReadDebugMagic(bbr, BB_DebugMagicKind_AlignNextByte, 0);
|
|
}
|
|
#endif
|
|
bbr->cur_bit += (8 - (bbr->cur_bit & 7)) & 7;
|
|
}
|
|
|
|
void BB_ReadAlignBytes(BB_Reader *bbr, u64 align)
|
|
{
|
|
BB_ReadAlignToNextByte(bbr);
|
|
BB_ReadDebugMagic(bbr, BB_DebugMagicKind_AlignBytes, align);
|
|
if (align > 0)
|
|
{
|
|
u64 new_pos = (bbr->cur_bit >> 3);
|
|
new_pos += (align - 1);
|
|
new_pos -= new_pos % align;
|
|
if (BB_CheckReaderOverflowBits(bbr, (new_pos << 3) - bbr->cur_bit))
|
|
{
|
|
return;
|
|
}
|
|
bbr->cur_bit = new_pos << 3;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Read bits
|
|
|
|
u64 BB_ReadUBitsNoMagic(BB_Reader *bbr, u8 num_bits)
|
|
{
|
|
if (BB_CheckReaderOverflowBits(bbr, num_bits))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
u64 result = 0;
|
|
|
|
u8 offset = bbr->cur_bit & 7;
|
|
u8 num_trailing_bits = 0;
|
|
if (offset)
|
|
{
|
|
u8 *at = bbr->base + (bbr->cur_bit >> 3);
|
|
num_trailing_bits = MinU8(8 - offset, num_bits);
|
|
u8 mix_byte = *at;
|
|
mix_byte >>= offset;
|
|
mix_byte &= (1 << num_trailing_bits) - 1;
|
|
result = mix_byte;
|
|
num_bits -= num_trailing_bits;
|
|
bbr->cur_bit += num_trailing_bits;
|
|
}
|
|
|
|
// cur_bit is now aligned to byte
|
|
u8 *at = bbr->base + (bbr->cur_bit >> 3);
|
|
u8 num_bytes = (num_bits + 7) >> 3;
|
|
u64 tmp = 0;
|
|
CopyBytes(&tmp, at, num_bytes);
|
|
u64 mask = U64Max;
|
|
if (num_bits < 64)
|
|
{
|
|
mask = ~(U64Max << num_bits);
|
|
}
|
|
tmp &= mask;
|
|
result |= tmp << num_trailing_bits;
|
|
bbr->cur_bit += num_bits;
|
|
return result;
|
|
}
|
|
|
|
u64 BB_ReadUBits(BB_Reader *bbr, u8 num_bits)
|
|
{
|
|
BB_ReadDebugMagic(bbr, BB_DebugMagicKind_UBits, num_bits);
|
|
return BB_ReadUBitsNoMagic(bbr, num_bits);
|
|
}
|
|
|
|
i64 BB_ReadIBits(BB_Reader *bbr, u8 num_bits)
|
|
{
|
|
Assert(num_bits > 1);
|
|
BB_ReadDebugMagic(bbr, BB_DebugMagicKind_IBits, num_bits);
|
|
u64 tc = BB_ReadUBits(bbr, num_bits);
|
|
return BB_IntFromTwosCompliment(tc, num_bits);
|
|
}
|
|
|
|
u8 BB_ReadBit(BB_Reader *bbr)
|
|
{
|
|
return BB_ReadUBits(bbr, 1);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Read variable length integer
|
|
|
|
// Read a variable length unsigned integer.
|
|
// See BB_WriteUV for details.
|
|
u64 BB_ReadUV(BB_Reader *bbr)
|
|
{
|
|
BB_ReadDebugMagic(bbr, BB_DebugMagicKind_UV, 0);
|
|
|
|
u64 result = 0;
|
|
for (u64 i = 0; i <= 9; ++i)
|
|
{
|
|
u64 part = BB_ReadUBits(bbr, 8);
|
|
u8 is_last_part = part <= 0x7F;
|
|
result |= (part & 0x7F) << (i * 7);
|
|
if (is_last_part)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Read a variable length signed integer.
|
|
// See BB_WriteIV for details.
|
|
i64 BB_ReadIV(BB_Reader *bbr)
|
|
{
|
|
BB_ReadDebugMagic(bbr, BB_DebugMagicKind_IV, 0);
|
|
u8 first_byte = BB_ReadUBits(bbr, 8);
|
|
u8 cont_bit = first_byte & 0x80;
|
|
u8 sign_bit = first_byte & 0x40;
|
|
|
|
u8 num_bits = 6;
|
|
u64 tc = first_byte & 0x3F;
|
|
if (cont_bit)
|
|
{
|
|
for (u64 i = 0; i <= 9; ++i)
|
|
{
|
|
u64 part = BB_ReadUBits(bbr, 8);
|
|
u8 is_last_part = part <= 0x7F;
|
|
tc |= (part & 0x7F) << num_bits;
|
|
num_bits += 7;
|
|
if (is_last_part)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
num_bits = MinU8(num_bits, 64);
|
|
|
|
i64 result;
|
|
if (sign_bit)
|
|
{
|
|
// Sign bit is 1, indicating result is stored in twos compliment
|
|
result = BB_IntFromTwosCompliment(tc, num_bits);
|
|
}
|
|
else
|
|
{
|
|
result = (i64)tc;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Read floating point
|
|
|
|
f32 BB_ReadF32(BB_Reader *bbr)
|
|
{
|
|
BB_ReadDebugMagic(bbr, BB_DebugMagicKind_F32, 0);
|
|
u32 ubits = BB_ReadUBits(bbr, 32);
|
|
return *(f32 *)&ubits;
|
|
}
|
|
|
|
f64 BB_ReadF64(BB_Reader *bbr)
|
|
{
|
|
BB_ReadDebugMagic(bbr, BB_DebugMagicKind_F64, 0);
|
|
u64 ubits = BB_ReadUBits(bbr, 64);
|
|
return *(f64 *)&ubits;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Read Uid
|
|
|
|
Uid BB_ReadUid(BB_Reader *bbr)
|
|
{
|
|
BB_ReadDebugMagic(bbr, BB_DebugMagicKind_Uid, 128);
|
|
u64 hi = BB_ReadUBits(bbr, 64);
|
|
u64 lo = BB_ReadUBits(bbr, 64);
|
|
return UID(hi, lo);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Read raw data
|
|
|
|
String BB_ReadString(Arena *arena, BB_Reader *bbr)
|
|
{
|
|
BB_ReadDebugMagic(bbr, BB_DebugMagicKind_String, 0);
|
|
String result = Zi;
|
|
u64 len = BB_ReadUV(bbr);
|
|
u8 *src = BB_ReadBytesRaw(bbr, len);
|
|
if (src != 0)
|
|
{
|
|
result.len = len;
|
|
result.text = PushStructsNoZero(arena, u8, len);
|
|
CopyBytes(result.text, src, len);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Will fill dst with zeroes if bitbuff overflows
|
|
void BB_ReadBytes(BB_Reader *bbr, String out)
|
|
{
|
|
u8 *src = BB_ReadBytesRaw(bbr, out.len);
|
|
if (src)
|
|
{
|
|
CopyBytes(out.text, src, out.len);
|
|
}
|
|
else
|
|
{
|
|
ZeroBytes(out.text, out.len);
|
|
}
|
|
}
|
|
|
|
// Will return 0 on bitbuff overflow. Result should be checked.
|
|
u8 *BB_ReadBytesRaw(BB_Reader *bbr, u64 num_bytes)
|
|
{
|
|
BB_ReadAlignToNextByte(bbr);
|
|
|
|
u64 num_bits = num_bytes << 3;
|
|
if (BB_CheckReaderOverflowBits(bbr, num_bits))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
u8 *raw = bbr->base + (bbr->cur_bit >> 3);
|
|
bbr->cur_bit += num_bits;
|
|
|
|
return raw;
|
|
}
|
|
|
|
void BB_ReadSeekBytes(BB_Reader *bbr, u64 num_bytes)
|
|
{
|
|
BB_ReadAlignToNextByte(bbr);
|
|
|
|
u64 num_bits = num_bytes << 3;
|
|
if (BB_CheckReaderOverflowBits(bbr, num_bits))
|
|
{
|
|
return;
|
|
}
|
|
|
|
bbr->cur_bit += num_bits;
|
|
}
|
|
|
|
void BB_ReadSeekToByte(BB_Reader *bbr, u64 pos)
|
|
{
|
|
u64 cur_byte_pos = bbr->cur_bit >> 3;
|
|
if (pos >= cur_byte_pos)
|
|
{
|
|
BB_ReadSeekBytes(bbr, pos - cur_byte_pos);
|
|
}
|
|
else
|
|
{
|
|
// Tried to seek byte backwards in reader
|
|
Assert(0);
|
|
bbr->overflowed = 1;
|
|
bbr->cur_bit = (bbr->base_len << 3);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Reader debug
|
|
|
|
#if BITBUFF_DEBUG
|
|
|
|
void BB_ReadDebugMagic(BB_Reader *bbr, BB_DebugMagicKind expected_magic, u8 expected_num_bits)
|
|
{
|
|
if (bbr->debug_enabled)
|
|
{
|
|
if (BB_CheckReaderOverflowBits(bbr, 24))
|
|
{
|
|
return;
|
|
}
|
|
u64 stored = BB_ReadUBitsNoMagic(bbr, 24);
|
|
BB_DebugMagicKind stored_magic = stored & 0xFFFF;
|
|
u8 stored_num_bits = (stored >> 16) & 0xFF;
|
|
|
|
// Verify stored magic match
|
|
Assert(expected_magic == stored_magic);
|
|
|
|
// Verify stored bit count match
|
|
Assert(expected_num_bits == stored_num_bits);
|
|
}
|
|
}
|
|
|
|
void BB_ReadDebugMarker(BB_Reader *bbr, String name)
|
|
{
|
|
bbr->cur_bit += (8 - (bbr->cur_bit & 7)) & 7;
|
|
for (u64 i = 0; i < name.len; ++i)
|
|
{
|
|
u8 c_stored = BB_ReadUBitsNoMagic(bbr, 8);
|
|
u8 c_expected = name.text[i];
|
|
Assert(c_expected == c_stored);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Utils
|
|
|
|
u64 BB_TwosComplimentFromUint(u64 value, u8 num_bits)
|
|
{
|
|
u64 mask = U64Max;
|
|
if (num_bits < 64)
|
|
{
|
|
mask = ~(U64Max << num_bits);
|
|
}
|
|
u64 tc = (~value & mask) + 1;
|
|
tc &= mask;
|
|
return tc;
|
|
}
|
|
|
|
i64 BB_IntFromTwosCompliment(u64 tc, u8 num_bits)
|
|
{
|
|
u64 msb_mask = (u64)1 << (num_bits - 1);
|
|
i64 value = -(i64)(tc & msb_mask);
|
|
value += tc & ~msb_mask;
|
|
return value;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
//~ Test
|
|
|
|
void BB_Test(void)
|
|
{
|
|
TempArena scratch = BeginScratchNoConflict();
|
|
|
|
u8 kind_ubits = 0;
|
|
u8 kind_ibits = 1;
|
|
u8 kind_uv = 2;
|
|
u8 kind_iv = 3;
|
|
u8 kind_string = 4;
|
|
|
|
struct test_case_ubits { u64 v; u64 num_bits; };
|
|
struct test_case_ibits { i64 v; u64 num_bits; };
|
|
struct test_case_uv { u64 v; };
|
|
struct test_case_iv { i64 v; };
|
|
struct test_case_string { String v; };
|
|
struct test_case
|
|
{
|
|
u8 kind;
|
|
union
|
|
{
|
|
struct test_case_ubits ubits;
|
|
struct test_case_ibits ibits;
|
|
struct test_case_uv uv;
|
|
struct test_case_iv iv;
|
|
struct test_case_string s;
|
|
};
|
|
};
|
|
|
|
struct test_case cases[] = {
|
|
{ kind_ubits, .ubits = { 40, 8 } },
|
|
{ kind_ubits, .ubits = { 32, 8 } },
|
|
{ kind_ubits, .ubits = { 100, 7 } },
|
|
{ kind_ubits, .ubits = { 4, 3 } },
|
|
{ kind_ubits, .ubits = { 13, 8 } },
|
|
|
|
{ kind_ibits, .ibits = { 0, 8 } },
|
|
{ kind_ibits, .ibits = { -1, 8 } },
|
|
{ kind_ibits, .ibits = { -2, 8 } },
|
|
{ kind_ibits, .ibits = { -3, 8 } },
|
|
{ kind_ibits, .ibits = { -100, 8 } },
|
|
{ kind_ibits, .ibits = { -50, 7 } },
|
|
{ kind_ibits, .ibits = { 50, 7 } },
|
|
{ kind_ibits, .ibits = { 4, 7 } },
|
|
{ kind_ibits, .ibits = { 1, 7 } },
|
|
{ kind_ibits, .ibits = { 3, 3 } },
|
|
{ kind_ibits, .ibits = { 1, 2 } },
|
|
{ kind_ibits, .ibits = { 0, 2 } },
|
|
{ kind_ibits, .ibits = { -1, 2 } },
|
|
|
|
{ kind_uv, .uv = { 0 } },
|
|
{ kind_uv, .uv = { 100 } },
|
|
{ kind_uv, .uv = { 10000 } },
|
|
{ kind_uv, .uv = { 10000000000000 } },
|
|
{ kind_uv, .uv = { U64Max } },
|
|
|
|
{ kind_iv, .iv = { 0 } },
|
|
{ kind_iv, .iv = { -1 } },
|
|
{ kind_iv, .iv = { 10000000000000 } },
|
|
{ kind_iv, .iv = { -10000000000000 } },
|
|
{ kind_iv, .iv = { I64Max } },
|
|
{ kind_iv, .iv = { I64Min } },
|
|
|
|
{ kind_string, .s = { Lit("Hello there! Hope you're doing well.") } },
|
|
{ kind_ibits, .ibits = { 3, 3 } },
|
|
{ kind_string, .s = { Lit("Alriiiiiiiiiiiiiiiiiiighty then") } },
|
|
{ kind_string, .s = { Lit("Alriiiiiiiiiiiiiiiiiiighty then") } },
|
|
};
|
|
|
|
String encoded = Zi;
|
|
{
|
|
BB_Buff bb = BB_AcquireDynamicBuff(Gibi(64));
|
|
BB_Writer bbw = BB_WriterFromBuff(&bb);
|
|
for (u64 i = 0; i < countof(cases); ++i)
|
|
{
|
|
struct test_case c = cases[i];
|
|
if (c.kind == kind_ubits)
|
|
{
|
|
BB_WriteUBits(&bbw, c.ubits.v, c.ubits.num_bits);
|
|
}
|
|
else if (c.kind == kind_ibits)
|
|
{
|
|
BB_WriteIBits(&bbw, c.ibits.v, c.ibits.num_bits);
|
|
}
|
|
else if (c.kind == kind_uv)
|
|
{
|
|
BB_WriteUV(&bbw, c.uv.v);
|
|
}
|
|
else if (c.kind == kind_iv)
|
|
{
|
|
BB_WriteIV(&bbw, c.iv.v);
|
|
}
|
|
else if (c.kind == kind_string)
|
|
{
|
|
BB_WriteString(&bbw, c.s.v);
|
|
}
|
|
else
|
|
{
|
|
Assert(0);
|
|
}
|
|
}
|
|
encoded = BB_GetWritten(scratch.arena, &bbw);
|
|
}
|
|
|
|
{
|
|
BB_Buff bb = BB_BuffFromString(encoded);
|
|
BB_Reader bbr = BB_ReaderFromBuff(&bb);
|
|
for (u64 i = 0; i < countof(cases); ++i)
|
|
{
|
|
struct test_case c = cases[i];
|
|
if (c.kind == kind_ubits)
|
|
{
|
|
u64 w = c.ubits.v;
|
|
u64 r = BB_ReadUBits(&bbr, c.ubits.num_bits);
|
|
Assert(r == w);
|
|
}
|
|
else if (c.kind == kind_ibits)
|
|
{
|
|
i64 w = c.ibits.v;
|
|
i64 r = BB_ReadIBits(&bbr, c.ubits.num_bits);
|
|
Assert(r == w);
|
|
}
|
|
else if (c.kind == kind_uv)
|
|
{
|
|
u64 w = c.uv.v;
|
|
u64 r = BB_ReadUV(&bbr);
|
|
Assert(r == w);
|
|
}
|
|
else if (c.kind == kind_iv)
|
|
{
|
|
i64 w = c.iv.v;
|
|
i64 r = BB_ReadIV(&bbr);
|
|
Assert(r == w);
|
|
}
|
|
else if (c.kind == kind_string)
|
|
{
|
|
String w = c.s.v;
|
|
String r = BB_ReadString(scratch.arena, &bbr);
|
|
Assert(MatchString(r, w));
|
|
}
|
|
else
|
|
{
|
|
Assert(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
EndScratch(scratch);
|
|
}
|