///|
/// Appends directly to packed bytes; does not retain a Sample array or one Int per bit.
pub struct Encoder {
priv writer : Writer
priv mut count : Int
priv mut previous : UInt64
priv mut time : Int64
priv mut delta : Int64
priv mut leading : Int
priv mut trailing : Int
priv mut closed : Bool
}
///|
pub fn Encoder::new() -> Encoder {
let writer = Writer::{ bytes: [], partial: 0, used: 0, }
writer.put(0x474f5231UL, 32)
writer.put(0UL, 32)
{
writer,
count: 0,
previous: 0UL,
time: 0L,
delta: 0L,
leading: 64,
trailing: 0,
closed: false,
}
}
///|
/// Rejected samples leave the encoder unchanged; finish seals further appends.
pub fn Encoder::append(
self : Encoder,
sample : Sample,
) -> Unit raise CodecError {
if self.closed {
raise Invalid("encoder finished")
}
if self.count == 100000 {
raise Invalid("sample limit")
}
if sample.timestamp < 0L || sample.timestamp > 9007199254740991L {
raise Invalid("timestamp outside safe range")
}
if self.count > 0 && sample.timestamp < self.time {
raise Invalid("timestamps must be nondecreasing")
}
let w = self.writer
if self.count == 0 {
w.put(sample.timestamp.reinterpret_as_uint64(), 64)
w.put(sample.bits, 64)
} else {
let d = sample.timestamp - self.time
let dd = d - self.delta
if dd == 0L {
w.put(0UL, 1)
} else if dd >= -63L && dd <= 64L {
w.put(2UL, 2)
w.put((dd + 63L).reinterpret_as_uint64(), 7)
} else if dd >= -255L && dd <= 256L {
w.put(6UL, 3)
w.put((dd + 255L).reinterpret_as_uint64(), 9)
} else if dd >= -2047L && dd <= 2048L {
w.put(14UL, 4)
w.put((dd + 2047L).reinterpret_as_uint64(), 12)
} else {
w.put(15UL, 4)
w.put(dd.reinterpret_as_uint64(), 64)
}
let xor = self.previous ^ sample.bits
if xor == 0UL {
w.put(0UL, 1)
} else {
w.put(1UL, 1)
let (leading, trailing) = zeros(xor)
if leading >= self.leading && trailing >= self.trailing {
w.put(0UL, 1)
w.put(xor >> self.trailing, 64 - self.leading - self.trailing)
} else {
self.leading = leading
self.trailing = trailing
w.put(1UL, 1)
w.put(leading.to_uint64(), 6)
w.put((64 - leading - trailing).to_uint64() & 63UL, 6)
w.put(xor >> trailing, 64 - leading - trailing)
}
}
self.delta = d
}
self.time = sample.timestamp
self.previous = sample.bits
self.count += 1
}
///|
/// Independent, padded GOR1 snapshot. Continuing to append cannot mutate a prior snapshot.
pub fn Encoder::snapshot(self : Encoder) -> Bytes {
self.writer.snapshot(self.count)
}
///|
pub fn Encoder::finish(self : Encoder) -> Bytes {
self.closed = true
self.snapshot()
}
///|
pub fn Encoder::length(self : Encoder) -> Int {
self.count
}
///|
pub fn Encoder::encoded_size(self : Encoder) -> Int {
self.writer.bytes.length() + (if self.writer.used > 0 { 1 } else { 0 })
}
///|
/// Iterates over a complete GOR1 byte block using constant decoder state.
/// This is a sample iterator, not a transport that accepts partial byte chunks.
pub struct Decoder {
priv reader : Reader
priv count : Int
priv mut index : Int
priv mut time : Int64
priv mut delta : Int64
priv mut previous : UInt64
priv mut leading : Int
priv mut trailing : Int
priv mut failed : Bool
priv mut verified : Bool
}
///|
pub fn Decoder::new(data : Bytes) -> Decoder raise CodecError {
if data.length() > 2000000 {
raise Invalid("encoded block limit")
}
let reader = Reader::{ bytes: data, pos: 0, }
if reader.get(32) != 0x474f5231UL {
raise Invalid("not GOR1")
}
let count = reader.get(32)
if count > 100000UL {
raise Invalid("sample limit")
}
{
reader,
count: count.to_int(),
index: 0,
time: 0L,
delta: 0L,
previous: 0UL,
leading: 64,
trailing: 0,
failed: false,
verified: false,
}
}
///|
fn Decoder::verify_tail(self : Decoder) -> Unit raise CodecError {
let r = self.reader
if r.bytes.length() * 8 - r.pos >= 8 {
raise Invalid("trailing bytes")
}
while r.pos < r.bytes.length() * 8 {
if r.get(1) != 0UL {
raise Invalid("nonzero padding")
}
}
self.verified = true
}
///|
fn Decoder::read_sample(self : Decoder) -> Sample? raise CodecError {
if self.index == self.count {
if !self.verified {
self.verify_tail()
}
return None
}
let r = self.reader
if self.index == 0 {
self.time = r.get(64).reinterpret_as_int64()
self.previous = r.get(64)
} else {
let dd = if r.get(1) == 0UL {
0L
} else if r.get(1) == 0UL {
r.get(7).reinterpret_as_int64() - 63L
} else if r.get(1) == 0UL {
r.get(9).reinterpret_as_int64() - 255L
} else if r.get(1) == 0UL {
r.get(12).reinterpret_as_int64() - 2047L
} else {
r.get(64).reinterpret_as_int64()
}
if dd < -9007199254740991L || dd > 9007199254740991L {
raise Invalid("delta range")
}
self.delta += dd
if self.delta < 0L || self.delta > 9007199254740991L - self.time {
raise Invalid("timestamp overflow/order")
}
self.time += self.delta
if r.get(1) != 0UL {
if r.get(1) != 0UL {
self.leading = r.get(6).to_int()
let size = r.get(6).to_int()
let size = if size == 0 { 64 } else { size }
self.trailing = 64 - self.leading - size
if self.trailing < 0 {
raise Invalid("invalid XOR window")
}
} else if self.leading == 64 {
raise Invalid("missing XOR window")
}
self.previous = self.previous ^
(r.get(64 - self.leading - self.trailing) << self.trailing)
}
}
if self.time < 0L || self.time > 9007199254740991L {
raise Invalid("timestamp range")
}
self.index += 1
// Validate the tail before exposing the final sample, so truncation/padding is never reported as EOF.
if self.index == self.count {
self.verify_tail()
}
Some({ timestamp: self.time, bits: self.previous, })
}
///|
pub fn Decoder::next(self : Decoder) -> Sample? raise CodecError {
if self.failed {
raise Invalid("decoder is in failed state")
}
errdefer {
self.failed = true
}
self.read_sample()
}
///|
/// Drain remaining samples without retaining them; validates all remaining bytes.
pub fn Decoder::finish(self : Decoder) -> Unit raise CodecError {
while self.next() is Some(_) {
}
}
///|
pub fn Decoder::remaining(self : Decoder) -> Int {
self.count - self.index
}
///|
pub fn Decoder::is_verified(self : Decoder) -> Bool {
self.verified && !self.failed
}