///|
/// Prometheus XOR chunk, with its two-byte sample count and no segment wrapper.
pub struct XorEncoder {
priv writer : Writer
priv mut count : Int
priv mut time : Int64
priv mut delta : UInt64
priv mut bits : UInt64
priv mut leading : Int
priv mut trailing : Int
priv mut closed : Bool
}
///|
pub fn XorEncoder::new() -> XorEncoder {
{
writer: { bytes: [0, 0], partial: 0, used: 0, },
count: 0,
time: 0L,
delta: 0UL,
bits: 0UL,
leading: 64,
trailing: 0,
closed: false,
}
}
///|
fn Writer::varuint(self : Writer, n : UInt64) -> Unit {
let mut n = n
while n >= 128UL {
self.put((n & 127UL) | 128UL, 8)
n = n >> 7
}
self.put(n, 8)
}
///|
fn Reader::varuint(self : Reader) -> UInt64 raise CodecError {
let mut n = 0UL
for i in 0..<10 {
let b = self.get(8)
if i == 9 && b > 1UL {
raise Invalid("varint overflow")
}
n = n | ((b & 127UL) << (i * 7))
if b < 128UL {
return n
}
}
raise Invalid("unterminated varint")
}
///|
/// Uses signed 64-bit timestamps and bit-exact values; ordering is not imposed.
/// Like the reference chunk codec, delta arithmetic wraps modulo 2^64.
pub fn XorEncoder::append(
self : XorEncoder,
sample : Sample,
) -> Unit raise CodecError {
if self.closed {
raise Invalid("encoder finished")
}
if self.count >= 65535 {
raise Invalid("XOR chunk sample limit")
}
let writer = self.writer
let time_bits = sample.timestamp.reinterpret_as_uint64()
let delta = time_bits - self.time.reinterpret_as_uint64()
if self.count == 0 {
writer.varuint(
(time_bits << 1) ^ (sample.timestamp >> 63).reinterpret_as_uint64(),
)
writer.put(sample.bits, 64)
} else {
if self.count == 1 {
writer.varuint(delta)
} else {
let difference = (delta - self.delta).reinterpret_as_int64()
if difference == 0L {
writer.put(0UL, 1)
} else {
let width = if difference >= -8191L && difference <= 8192L {
14
} else if difference >= -65535L && difference <= 65536L {
17
} else if difference >= -524287L && difference <= 524288L {
20
} else {
64
}
if width == 14 {
writer.put(2UL, 2)
} else if width == 17 {
writer.put(6UL, 3)
} else if width == 20 {
writer.put(14UL, 4)
} else {
writer.put(15UL, 4)
}
writer.put(difference.reinterpret_as_uint64(), width)
}
}
let changed = self.bits ^ sample.bits
if changed == 0UL {
writer.put(0UL, 1)
} else {
writer.put(1UL, 1)
let (lz, tz) = zeros(changed)
let lz = if lz > 31 { 31 } else { lz }
if self.leading != 64 && lz >= self.leading && tz >= self.trailing {
writer.put(0UL, 1)
writer.put(changed >> self.trailing, 64 - self.leading - self.trailing)
} else {
writer.put(1UL, 1)
writer.put(lz.to_uint64(), 5)
writer.put((64 - lz - tz).to_uint64() & 63UL, 6)
writer.put(changed >> tz, 64 - lz - tz)
self.leading = lz
self.trailing = tz
}
}
}
self.delta = delta
self.time = sample.timestamp
self.bits = sample.bits
self.count += 1
}
///|
pub fn XorEncoder::snapshot(self : XorEncoder) -> Bytes {
let bytes = self.writer.bytes.copy()
if self.writer.used > 0 {
bytes.push((self.writer.partial << (8 - self.writer.used)).to_byte())
}
bytes[0] = (self.count >> 8).to_byte()
bytes[1] = (self.count & 255).to_byte()
Bytes::from_array(bytes)
}
///|
pub fn XorEncoder::finish(self : XorEncoder) -> Bytes {
self.closed = true
self.snapshot()
}
///|
pub fn XorEncoder::length(self : XorEncoder) -> Int {
self.count
}
///|
pub fn XorEncoder::encoded_size(self : XorEncoder) -> Int {
self.writer.bytes.length() + (if self.writer.used == 0 { 0 } else { 1 })
}
///|
pub fn encode_xor(samples : Array[Sample]) -> Bytes raise CodecError {
if samples.length() > 65535 {
raise Invalid("XOR chunk sample limit")
}
let encoder = XorEncoder::new()
for sample in samples {
encoder.append(sample)
}
encoder.finish()
}
///|
pub struct XorDecoder {
priv reader : Reader
priv count : Int
priv strict : Bool
priv mut index : Int
priv mut time : Int64
priv mut delta : UInt64
priv mut bits : UInt64
priv mut leading : Int
priv mut trailing : Int
priv mut failed : Bool
priv mut verified : Bool
}
///|
/// strict=true rejects nonzero padding/trailing bytes. The reference iterator
/// stops at the sample count; strict=false retains that framing behavior.
pub fn XorDecoder::new(
data : Bytes,
strict? : Bool = true,
) -> XorDecoder raise CodecError {
if data.length() < 2 || data.length() > 2000000 {
raise Invalid("XOR chunk byte limit")
}
{
reader: { bytes: data, pos: 16, },
count: (data[0].to_int() << 8) | data[1].to_int(),
strict,
index: 0,
time: 0L,
delta: 0UL,
bits: 0UL,
leading: 0,
trailing: 0,
failed: false,
verified: false,
}
}
///|
fn XorDecoder::verify_tail(self : XorDecoder) -> Unit raise CodecError {
if self.strict {
if self.reader.bytes.length() * 8 - self.reader.pos >= 8 {
raise Invalid("trailing XOR chunk bytes")
}
while self.reader.pos < self.reader.bytes.length() * 8 {
if self.reader.get(1) != 0UL {
raise Invalid("nonzero XOR padding")
}
}
}
self.verified = true
}
///|
pub fn XorDecoder::next(self : XorDecoder) -> Sample? raise CodecError {
if self.failed {
raise Invalid("decoder is in failed state")
}
errdefer {
self.failed = true
}
if self.index == self.count {
if !self.verified {
self.verify_tail()
}
return None
}
let reader = self.reader
if self.index == 0 {
let n = reader.varuint()
self.time = ((n >> 1) ^ (0UL - (n & 1UL))).reinterpret_as_int64()
self.bits = reader.get(64)
} else {
if self.index == 1 {
self.delta = reader.varuint()
} else {
let mut ones = 0
while ones < 4 {
if reader.get(1) == 0UL {
break
}
ones += 1
}
let width = match ones {
0 => 0
1 => 14
2 => 17
3 => 20
_ => 64
}
if width > 0 {
let n = reader.get(width)
let difference = if width < 64 && n > 1UL << (width - 1) {
n - (1UL << width)
} else {
n
}
self.delta += difference
}
}
self.time = (self.time.reinterpret_as_uint64() + self.delta).reinterpret_as_int64()
if reader.get(1) == 1UL {
if reader.get(1) == 1UL {
self.leading = reader.get(5).to_int()
let width = reader.get(6).to_int()
let width = if width == 0 { 64 } else { width }
if self.leading + width > 64 {
raise Invalid("invalid XOR window")
}
self.trailing = 64 - self.leading - width
}
self.bits = self.bits ^
(reader.get(64 - self.leading - self.trailing) << self.trailing)
}
}
self.index += 1
if self.index == self.count {
self.verify_tail()
}
Some({ timestamp: self.time, bits: self.bits, })
}
///|
pub fn XorDecoder::finish(self : XorDecoder) -> Unit raise CodecError {
while self.next() is Some(_) {
}
}
///|
pub fn XorDecoder::remaining(self : XorDecoder) -> Int {
self.count - self.index
}
///|
pub fn XorDecoder::is_verified(self : XorDecoder) -> Bool {
self.verified && !self.failed
}
///|
pub fn decode_xor(
data : Bytes,
strict? : Bool = true,
) -> Array[Sample] raise CodecError {
let decoder = XorDecoder::new(data, strict~)
let samples = []
while decoder.next() is Some(sample) {
samples.push(sample)
}
samples
}