feat: buffered decoding API

This commit is contained in:
2024-06-19 04:08:54 +02:00
parent ae6ad428ab
commit c7a634eeb3
10 changed files with 1985 additions and 305 deletions
+58 -92
View File
@@ -8,119 +8,85 @@ import "core:io"
Uses core:bytes reader under the hood with a few useful procedures for reading structs and other types.
*/
Reader :: struct {
using _: bytes.Reader,
bits_read: u8,
using r: io.Stream,
// Temporary read buffer.
buf: [8]u8,
// Between 0 and 7 buffered bits since previous read operations.
x: u8,
// The number of buffered bits in x.
n: uint,
}
read_slice :: #force_inline proc(r: ^Reader, size: int) -> (res: []u8, err: io.Error) {
#no_bounds_check {
if r.i + i64(size) <= i64(len(r.s)) {
res = r.s[r.i:r.i + i64(size)]
r.i += i64(size)
return res, .None
}
}
read_slice :: #force_inline proc(r: ^Reader, buf: []u8) -> io.Error {
io.read_full(r^, buf) or_return
if i64(len(r.s)) == r.i {
return []u8{}, .EOF
} else {
return []u8{}, .Short_Buffer
}
return nil
}
read_data :: #force_inline proc(r: ^Reader, $T: typeid) -> (res: T, err: io.Error) {
b := read_slice(r, size_of(T)) or_return
b: [size_of(T)]byte
read_slice(r, b[:]) or_return
return (^T)(&b[0])^, nil
}
read_bit :: proc(r: ^Reader) -> (bit: byte, err: Error) #no_bounds_check {
if r.i >= i64(len(r.s)) {
return 0, .EOF
// Read reads and returns the next n bits, at most 64. It buffers bits up to the
// next byte boundary.
// Borrowed from https://github.com/mewkiz/flac
read_bits :: proc(r: ^Reader, n: uint) -> (res: u64, err: io.Error) #no_bounds_check {
if n == 0 {
return 0, nil
}
bits := 8 - (r.bits_read + 1)
b := (r.s[r.i] >> bits) & 1
r.bits_read += 1
n := n
if r.bits_read == 8 {
r.i += 1
r.bits_read = 0
}
return b, nil
}
read_bits :: proc(r: ^Reader, n: uint) -> (bits: u64, err: Error) #no_bounds_check {
res: u64
// Fast path: if we can read all bits within the current byte.
if r.bits_read + u8(n) <= 8 {
res = (u64(r.s[r.i]) >> (8 - r.bits_read - u8(n))) & ((1 << n) - 1)
r.bits_read += u8(n)
if r.bits_read == 8 {
r.bits_read = 0
r.i += 1
// Read buffered bits.
if r.n > 0 {
switch {
case r.n == n:
r.n = 0
return u64(r.x), nil
case r.n > n:
r.n -= n
mask := ~u8(0) << r.n
res = u64(r.x&mask) >> r.n
r.x &~= mask
return res, nil
}
return res, nil
n -= r.n
res = u64(r.x)
r.n = 0
}
// General case: reading across multiple bytes.
rem_bits := n
if r.bits_read > 0 {
// Read the remaining bits in the current byte.
bits_in_byte := 8 - r.bits_read
res = u64(r.s[r.i]) & ((1 << bits_in_byte) - 1)
rem_bits -= uint(bits_in_byte)
r.bits_read = 0
r.i += 1
// Fill the temporary buffer.
bytes := n / 8
bits := n % 8
if bits > 0 {
bytes += 1
}
io.read_full(r, r.buf[:bytes]) or_return
// Read full bytes.
for rem_bits >= 8 {
res = (res << 8) | u64(r.s[r.i])
rem_bits -= 8
r.i += 1
// Read bits from the temporary buffer.
for b in r.buf[:bytes-1] {
res <<= 8
res |= u64(b)
}
// Read remaining bits in the final byte.
if rem_bits > 0 {
res = (res << rem_bits) | (u64(r.s[r.i]) >> (8 - rem_bits))
r.bits_read += u8(rem_bits)
b := r.buf[bytes-1]
if bits > 0 {
res <<= bits
r.n = 8 - bits
mask := ~u8(0) << r.n
res |= u64(b&mask) >> r.n
r.x = b & ~mask
} else {
res <<= 8
res |= u64(b)
}
return res, nil
}
read_byte :: proc(r: ^Reader) -> (b: byte, err: Error) #no_bounds_check {
if r.i >= i64(len(r.s)) {
return 0, .EOF
}
// Continue reading from the current byte if it has unread bits.
if r.bits_read != 0 {
cur_byte := r.s[r.i]
next_byte := r.s[r.i + 1]
rem_bits := 8 - r.bits_read
rem_bits_mask: u8 = (1 << rem_bits) - 1
b = ((cur_byte & rem_bits_mask) << r.bits_read) | (next_byte >> rem_bits)
r.i += 1
} else {
b = r.s[r.i]
r.i += 1
}
return b, nil
}
align_to_byte :: proc(r: ^Reader) -> Error {
if r.bits_read == 0 {
return nil
}
if r.i >= i64(len(r.s)) {
return .EOF
}
// Align by skipping the remaining bits in the byte
r.bits_read = 0
r.i += 1
return nil
align_to_byte :: proc(r: ^Reader) {
r.x = 0
r.n = 0
}