// MoonDNS - Pure MoonBit Authoritative DNS Server
// Copyright 2026 chgttyyr
// Licensed under the Apache-2.0 License.
///|
/// ByteReader provides safe, bounds-checked big-endian binary stream reading.
pub(all) struct ByteReader {
bytes : Bytes
mut cursor : Int
} derive(Debug)
///|
/// Create a new ByteReader starting at position 0
pub fn ByteReader::new(bytes : Bytes) -> ByteReader {
{ bytes, cursor: 0, }
}
///|
/// Create a ByteReader starting at a specific offset
pub fn ByteReader::from_offset(
bytes : Bytes,
offset : Int,
) -> Result[ByteReader, @types.DnsError] {
if offset < 0 || offset > bytes.length() {
return Err(
@types.BufferUnderflow("Invalid starting offset: " + offset.to_string()),
)
}
Ok({ bytes, cursor: offset, })
}
///|
/// Number of remaining unread bytes in buffer
pub fn ByteReader::remaining(self : ByteReader) -> Int {
self.bytes.length() - self.cursor
}
///|
/// Check if buffer has at least n remaining bytes
pub fn ByteReader::has_remaining(self : ByteReader, n : Int) -> Bool {
self.remaining() >= n
}
///|
/// Current read cursor position (0-indexed)
pub fn ByteReader::position(self : ByteReader) -> Int {
self.cursor
}
///|
/// Total length of the underlying bytes
pub fn ByteReader::total_length(self : ByteReader) -> Int {
self.bytes.length()
}
///|
/// Reposition the read cursor safely
pub fn ByteReader::set_position(
self : ByteReader,
pos : Int,
) -> Result[Unit, @types.DnsError] {
if pos < 0 || pos > self.bytes.length() {
return Err(
@types.BufferUnderflow("Position out of bounds: " + pos.to_string()),
)
}
self.cursor = pos
Ok(())
}
///|
/// Advance cursor by n bytes without reading
pub fn ByteReader::skip(
self : ByteReader,
n : Int,
) -> Result[Unit, @types.DnsError] {
if n < 0 || self.cursor + n > self.bytes.length() {
return Err(
@types.BufferUnderflow("Cannot skip " + n.to_string() + " bytes"),
)
}
self.cursor = self.cursor + n
Ok(())
}
///|
/// Read a single byte and advance cursor
pub fn ByteReader::read_byte(
self : ByteReader,
) -> Result[Byte, @types.DnsError] {
if self.cursor >= self.bytes.length() {
return Err(
@types.BufferUnderflow("End of buffer reached while reading byte"),
)
}
let b = self.bytes[self.cursor]
self.cursor = self.cursor + 1
Ok(b)
}
///|
/// Peek at the current byte without advancing the cursor
pub fn ByteReader::peek_byte(
self : ByteReader,
) -> Result[Byte, @types.DnsError] {
if self.cursor >= self.bytes.length() {
return Err(
@types.BufferUnderflow("End of buffer reached while peeking byte"),
)
}
Ok(self.bytes[self.cursor])
}
///|
/// Read a big-endian 16-bit unsigned integer and advance cursor by 2
pub fn ByteReader::read_uint16(
self : ByteReader,
) -> Result[UInt16, @types.DnsError] {
if self.cursor + 2 > self.bytes.length() {
return Err(
@types.BufferUnderflow(
"Not enough bytes to read UInt16 at " + self.cursor.to_string(),
),
)
}
let b0 = self.bytes[self.cursor].to_int()
let b1 = self.bytes[self.cursor + 1].to_int()
self.cursor = self.cursor + 2
let val = ((b0 << 8) | b1).to_uint16()
Ok(val)
}
///|
/// Read a big-endian 32-bit unsigned integer and advance cursor by 4
pub fn ByteReader::read_uint32(
self : ByteReader,
) -> Result[UInt, @types.DnsError] {
if self.cursor + 4 > self.bytes.length() {
return Err(
@types.BufferUnderflow(
"Not enough bytes to read UInt32 at " + self.cursor.to_string(),
),
)
}
let b0 = self.bytes[self.cursor].to_int().reinterpret_as_uint()
let b1 = self.bytes[self.cursor + 1].to_int().reinterpret_as_uint()
let b2 = self.bytes[self.cursor + 2].to_int().reinterpret_as_uint()
let b3 = self.bytes[self.cursor + 3].to_int().reinterpret_as_uint()
self.cursor = self.cursor + 4
let val = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3
Ok(val)
}
///|
/// Read a fixed number of bytes into a new Bytes instance
pub fn ByteReader::read_bytes(
self : ByteReader,
len : Int,
) -> Result[Bytes, @types.DnsError] {
if len < 0 || self.cursor + len > self.bytes.length() {
return Err(
@types.BufferUnderflow(
"Not enough bytes to read " + len.to_string() + " bytes",
),
)
}
let arr : Array[Byte] = []
for i = 0; i < len; i = i + 1 {
arr.push(self.bytes[self.cursor + i])
}
self.cursor = self.cursor + len
Ok(Bytes::from_array(arr))
}
///|
/// Extract a subslice without moving the primary cursor
pub fn ByteReader::sub_slice(
self : ByteReader,
start : Int,
len : Int,
) -> Result[Bytes, @types.DnsError] {
if start < 0 || len < 0 || start + len > self.bytes.length() {
return Err(
@types.BufferUnderflow(
"Invalid slice range: start=" +
start.to_string() +
", len=" +
len.to_string(),
),
)
}
let arr : Array[Byte] = []
for i = 0; i < len; i = i + 1 {
arr.push(self.bytes[start + i])
}
Ok(Bytes::from_array(arr))
}