// byte_reader.mbt
//
// 带边界检查的小端字节游标。7z 头部里既有定长整数(UINT32 / UINT64),
// 也有可变长 NUMBER 与嵌套属性树,所以"先证明区间存在、再读取"必须写成
// 可复用的原语,而不是在每个解析分支里各写一遍下标判断。
///|
/// 顺序读取字节的小端游标。
pub struct ByteReader {
data : Bytes
mut pos : Int32
}
///|
/// 在 `data` 上建立游标。
pub fn ByteReader::new(data : Bytes) -> ByteReader {
{ data, pos: 0, }
}
///|
/// 当前读取位置。
pub fn ByteReader::position(self : ByteReader) -> Int32 {
self.pos
}
///|
/// 剩余字节数。
pub fn ByteReader::remaining(self : ByteReader) -> Int32 {
self.data.length() - self.pos
}
///|
/// 要求剩余至少 `need` 字节;不足时抛出 `Truncated`。
pub fn ByteReader::require(
self : ByteReader,
need : Int32,
) -> Unit raise SevenZError {
if need < 0 || self.pos + need > self.data.length() {
raise Truncated(
offset=self.pos.to_int64(),
need=need.to_int64(),
actual=self.remaining().to_int64(),
)
}
}
///|
/// 读 1 字节(0 .. 255)。
pub fn ByteReader::read_byte(self : ByteReader) -> Int32 raise SevenZError {
self.require(1)
let b = self.data.unsafe_get(self.pos).to_int()
self.pos += 1
b
}
///|
/// 读小端 UINT32(以 Int64 返回,值域 0 .. 2^32-1)。
pub fn ByteReader::read_u32(self : ByteReader) -> Int64 raise SevenZError {
self.require(4)
let mut v = 0L
let mut i = 0
while i < 4 {
v = v | (self.data.unsafe_get(self.pos + i).to_int().to_int64() << (8 * i))
i += 1
}
self.pos += 4
v
}
///|
/// 读小端 UINT64(以 Int64 返回;7z 的实际字段远小于 2^63)。
pub fn ByteReader::read_u64(self : ByteReader) -> Int64 raise SevenZError {
self.require(8)
let mut v = 0L
let mut i = 0
while i < 8 {
v = v | (self.data.unsafe_get(self.pos + i).to_int().to_int64() << (8 * i))
i += 1
}
self.pos += 8
v
}
///|
/// 读 `n` 字节并复制成新的 `Bytes`。
pub fn ByteReader::read_bytes(
self : ByteReader,
n : Int32,
) -> Bytes raise SevenZError {
self.require(n)
let buf = @buffer.Buffer()
let mut i = 0
while i < n {
buf.write_byte(self.data.unsafe_get(self.pos + i))
i += 1
}
self.pos += n
buf.to_bytes()
}
///|
/// 跳过 `n` 字节。
pub fn ByteReader::skip(self : ByteReader, n : Int32) -> Unit raise SevenZError {
self.require(n)
self.pos += n
}
///|
/// 复制 `data[start, end)` 成新的 `Bytes`(区间自动裁剪到数据长度内)。
pub fn copy_range(data : Bytes, start : Int32, end : Int32) -> Bytes {
let buf = @buffer.Buffer()
let lo = if start > 0 { start } else { 0 }
let hi = if end < data.length() { end } else { data.length() }
let mut i = lo
while i < hi {
buf.write_byte(data.unsafe_get(i))
i += 1
}
buf.to_bytes()
}