// Copyright (c) 2025 lws
// Byte reading utilities for image decoding
//-----------------------------------------------------------------------------
// Byte-level reading helpers
//-----------------------------------------------------------------------------
///|
/// Read a 16-bit unsigned integer in little-endian byte order
fn read_u16_le(data : Bytes, pos : Int) -> Int raise Failure {
if pos + 2 > data.length() {
raise Failure::Failure("read_u16_le: position out of bounds")
}
let lo = data[pos].to_int()
let hi = data[pos + 1].to_int()
lo | (hi << 8)
}
///|
/// Read a 32-bit unsigned integer in little-endian byte order
fn read_u32_le(data : Bytes, pos : Int) -> Int raise Failure {
if pos + 4 > data.length() {
raise Failure::Failure("read_u32_le: position out of bounds")
}
let b0 = data[pos].to_int()
let b1 = data[pos + 1].to_int()
let b2 = data[pos + 2].to_int()
let b3 = data[pos + 3].to_int()
b0 | (b1 << 8) | (b2 << 16) | (b3 << 24)
}
///|
/// Read a 32-bit unsigned integer in big-endian byte order
fn read_u32_be(data : Bytes, pos : Int) -> Int raise Failure {
if pos + 4 > data.length() {
raise Failure::Failure("read_u32_be: position out of bounds")
}
let b0 = data[pos].to_int()
let b1 = data[pos + 1].to_int()
let b2 = data[pos + 2].to_int()
let b3 = data[pos + 3].to_int()
(b0 << 24) | (b1 << 16) | (b2 << 8) | b3
}
//-----------------------------------------------------------------------------
// CRC-32 checksum (used by PNG)
//-----------------------------------------------------------------------------
///|
/// Pre-computed CRC-32 lookup table (computed once at module init)
let crc32_table : Array[Int] = make_crc32_table()
///|
/// Logical right shift: forces unsigned semantics by masking out sign-extended bits
fn lsr8(v : Int) -> Int {
(v >> 8) & 0x00FFFFFF
}
///|
fn lsr1(v : Int) -> Int {
(v >> 1) & 0x7FFFFFFF
}
///|
fn make_crc32_table() -> Array[Int] {
let table = Array::make(256, 0)
for i = 0; i < 256; i = i + 1 {
let mut c = i
for _j = 0; _j < 8; _j = _j + 1 {
if (c & 1) != 0 {
c = 0xEDB88320 ^ lsr1(c)
} else {
c = lsr1(c)
}
}
table[i] = c
}
table
}
///|
/// Compute CRC-32 over multiple Bytes segments efficiently without concatenation
/// Pass previous crc32 result as `initial` to continue (use 0 for first segment)
fn crc32_update(initial : Int, data : Bytes) -> Int {
let mut c = initial ^ 0xFFFFFFFF
for i = 0; i < data.length(); i = i + 1 {
let byte_val = data[i].to_int()
let idx = (c ^ byte_val) & 0xFF
c = crc32_table[idx] ^ lsr8(c)
}
c ^ 0xFFFFFFFF
}
///|
/// Convenience: compute CRC-32 of chunk_type + chunk_data without allocating
fn crc32_chunk(chunk_type : Bytes, chunk_data : Bytes) -> Int {
let initial = crc32_update(0, chunk_type)
crc32_update(initial, chunk_data)
}
//-----------------------------------------------------------------------------
// Adler-32 checksum (used by zlib/DEFLATE)
//-----------------------------------------------------------------------------
///|
/// Compute Adler-32 checksum over the given bytes
fn adler32(data : Bytes) -> Int {
let mut s1 = 1
let mut s2 = 0
let prime = 65521
for i = 0; i < data.length(); i = i + 1 {
let byte_val = data[i].to_int()
s1 = (s1 + byte_val) % prime
s2 = (s2 + s1) % prime
}
(s2 << 16) | s1
}