///|
pub fn u8() -> Codec[UInt] {
uint_codec(1, Big)
}
///|
pub fn u16_be() -> Codec[UInt] {
uint_codec(2, Big)
}
///|
pub fn u16_le() -> Codec[UInt] {
uint_codec(2, Little)
}
///|
pub fn u32_be() -> Codec[UInt] {
uint_codec(4, Big)
}
///|
pub fn u32_le() -> Codec[UInt] {
uint_codec(4, Little)
}
///|
fn uint_codec(width : Int, endian : Endian) -> Codec[UInt] {
Codec::make(
fn(decoder) { decoder.read_uint(width, endian) },
fn(encoder, value) { encoder.write_uint(value, width, endian) },
kind=if width == 1 {
"u8"
} else if endian == Big {
"u\{width * 8}be"
} else {
"u\{width * 8}le"
},
render=fn(value) { value.to_string() },
)
}
///|
pub fn u64_be() -> Codec[UInt64] {
uint64_codec(Big)
}
///|
pub fn u64_le() -> Codec[UInt64] {
uint64_codec(Little)
}
///|
fn uint64_codec(endian : Endian) -> Codec[UInt64] {
Codec::make(
fn(decoder) { decoder.read_uint64(endian) },
fn(encoder, value) { encoder.write_uint64(value, endian) },
kind=if endian == Big { "u64be" } else { "u64le" },
render=fn(value) { value.to_string() },
)
}
///|
pub fn i8() -> Codec[Int] {
u8().xmap(
fn(value) {
let integer = value.reinterpret_as_int()
Ok(if value >= 128U { integer - 256 } else { integer })
},
fn(value) {
if value < -128 || value > 127 {
Err("i8 value out of range")
} else {
Ok(value.reinterpret_as_uint() & 0xffU)
}
},
render=fn(value) { value.to_string() },
)
}
///|
fn signed_codec(width : Int, endian : Endian) -> Codec[Int] {
let unsigned = uint_codec(width, endian)
unsigned.xmap(
fn(value) {
if width == 2 {
let integer = value.reinterpret_as_int()
Ok(if value >= 0x8000U { integer - 0x10000 } else { integer })
} else {
Ok(value.reinterpret_as_int())
}
},
fn(value) {
if width == 2 && (value < -32768 || value > 32767) {
Err("i16 value out of range")
} else if width == 2 {
Ok(value.reinterpret_as_uint() & 0xffffU)
} else {
Ok(value.reinterpret_as_uint())
}
},
render=fn(value) { value.to_string() },
)
}
///|
pub fn i16_be() -> Codec[Int] {
signed_codec(2, Big)
}
///|
pub fn i16_le() -> Codec[Int] {
signed_codec(2, Little)
}
///|
pub fn i32_be() -> Codec[Int] {
signed_codec(4, Big)
}
///|
pub fn i32_le() -> Codec[Int] {
signed_codec(4, Little)
}
///|
pub fn i64_be() -> Codec[Int64] {
u64_be().xmap(
fn(value) { Ok(value.reinterpret_as_int64()) },
fn(value) { Ok(value.reinterpret_as_uint64()) },
render=fn(value) { value.to_string() },
)
}
///|
pub fn i64_le() -> Codec[Int64] {
u64_le().xmap(
fn(value) { Ok(value.reinterpret_as_int64()) },
fn(value) { Ok(value.reinterpret_as_uint64()) },
render=fn(value) { value.to_string() },
)
}
///|
/// 零拷贝固定长度字节字段。返回的 `BytesView` 借用原始输入。
pub fn bytes_view_fixed(count : Int) -> Codec[BytesView] {
Codec::make(
fn(decoder) { decoder.take_view(count) },
fn(encoder, value) {
if value.length() != count {
Err(
BinError::new(
InvalidValue,
encoder.length(),
encoder.path(),
"byte view length does not match fixed field length",
),
)
} else {
encoder.write_bytes(value)
}
},
kind="bytes-view[\{count}]",
render=fn(value) { bytes_view_to_hex_preview(value, 32) },
)
}
///|
/// 零拷贝消费当前区域的全部剩余字节。
pub fn remaining_view() -> Codec[BytesView] {
Codec::make(
fn(decoder) { decoder.take_view(decoder.remaining()) },
fn(encoder, value) { encoder.write_bytes(value) },
kind="remaining-view",
render=fn(value) { bytes_view_to_hex_preview(value, 32) },
)
}
///|
/// 拥有型版本的“消费全部剩余字节”组合子。
pub fn remaining_bytes() -> Codec[Bytes] {
Codec::make(
fn(decoder) {
match decoder.take_view(decoder.remaining()) {
Err(error) => Err(error)
Ok(view) => Ok(view.to_owned())
}
},
fn(encoder, value) { encoder.write_bytes(value[:]) },
kind="remaining-bytes",
render=fn(value) { bytes_to_hex_preview(value, 32) },
)
}
///|
pub fn bytes_fixed(count : Int) -> Codec[Bytes] {
Codec::make(
fn(decoder) { decoder.take_bytes(count) },
fn(encoder, value) {
if value.length() != count {
Err(
BinError::new(
InvalidValue,
encoder.length(),
encoder.path(),
"byte string length does not match fixed field length",
),
)
} else {
encoder.write_bytes(value[:])
}
},
kind="bytes[\{count}]",
render=fn(value) { bytes_to_hex_preview(value, 32) },
)
}
///|
pub fn magic(expected : Bytes) -> Codec[Unit] {
bytes_fixed(expected.length()).xmap(
fn(actual) {
if actual == expected {
Ok(())
} else {
Err("magic bytes mismatch")
}
},
fn(_) { Ok(expected) },
render=fn(_) { bytes_to_hex(expected) },
)
}
///|
pub fn bits_msb(width : Int) -> Codec[UInt] {
Codec::make(
fn(decoder) { decoder.read_bits_msb(width) },
fn(encoder, value) { encoder.write_bits_msb(value, width) },
kind="bits\{width}msb",
render=fn(value) { value.to_string() },
)
}
///|
pub fn bool8() -> Codec[Bool] {
u8().xmap(
fn(value) {
match value {
0U => Ok(false)
1U => Ok(true)
_ => Err("boolean byte must be 0 or 1")
}
},
fn(value) { Ok(if value { 1U } else { 0U }) },
render=fn(value) { if value { "true" } else { "false" } },
)
}
///|
pub fn bytes_to_hex(bytes : Bytes) -> String {
let parts : Array[String] = []
for byte in bytes {
parts.push(byte.to_hex())
}
parts.join(" ")
}
///|
/// 为检查轨迹生成有界十六进制预览,避免大字段制造巨量日志。
pub fn bytes_to_hex_preview(bytes : Bytes, max_bytes : Int) -> String {
let limit = if max_bytes < 0 {
0
} else if max_bytes < bytes.length() {
max_bytes
} else {
bytes.length()
}
let parts : Array[String] = []
for index in 0.. String {
let limit = if max_bytes < 0 {
0
} else if max_bytes < bytes.length() {
max_bytes
} else {
bytes.length()
}
let parts : Array[String] = []
for index in 0.. Codec[Unit] {
Codec::make(
fn(decoder) {
if count < 0 {
return Err(
BinError::new(
InvalidValue,
decoder.absolute_offset(),
decoder.path(),
"padding count cannot be negative",
),
)
}
match decoder.take_view(count) {
Err(error) => Err(error)
Ok(bytes) => {
for byte in bytes {
if byte != 0U.to_byte() {
return Err(
BinError::new(
InvalidValue,
decoder.absolute_offset(),
decoder.path(),
"padding bytes must be zero",
),
)
}
}
Ok(())
}
}
},
fn(encoder, _) {
if count < 0 {
return Err(
BinError::new(
InvalidValue,
encoder.length(),
encoder.path(),
"padding count cannot be negative",
),
)
}
encoder.write_zeroes(count)
},
kind="padding[\{count}]",
render=fn(_) { "padding(\{count})" },
)
}
///|
/// 使用零填充将当前位置推进到给定字节边界;边界相对于当前解码区域起点。
pub fn align(boundary : Int) -> Codec[Unit] {
Codec::make(
fn(decoder) {
if boundary <= 0 {
return Err(
BinError::new(
InvalidValue,
decoder.absolute_offset(),
decoder.path(),
"alignment boundary must be positive",
),
)
}
let remainder = decoder.offset() % boundary
let count = if remainder == 0 { 0 } else { boundary - remainder }
padding(count).decode_from(decoder)
},
fn(encoder, _) {
if boundary <= 0 {
return Err(
BinError::new(
InvalidValue,
encoder.length(),
encoder.path(),
"alignment boundary must be positive",
),
)
}
let remainder = encoder.length() % boundary
let count = if remainder == 0 { 0 } else { boundary - remainder }
padding(count).encode_into(encoder, ())
},
kind="align[\{boundary}]",
render=fn(_) { "align(\{boundary})" },
)
}
///|
/// 读取以 NUL 结尾的字节串;返回值不包含终止字节。
pub fn nul_terminated_bytes(max_length? : Int = 4096) -> Codec[Bytes] {
Codec::make(
fn(decoder) {
if max_length < 0 {
return Err(
BinError::new(
InvalidValue,
decoder.absolute_offset(),
decoder.path(),
"maximum terminated byte length cannot be negative",
),
)
}
let bytes : Array[Byte] = []
while bytes.length() <= max_length {
let byte = match decoder.read_byte() {
Err(error) => return Err(error)
Ok(value) => value
}
if byte == 0U.to_byte() {
return Ok(Bytes::from_array(bytes))
}
if bytes.length() == max_length {
return Err(
BinError::new(
LimitExceeded,
decoder.absolute_offset(),
decoder.path(),
"terminated byte string exceeds configured limit",
),
)
}
bytes.push(byte)
}
Err(
BinError::new(
LimitExceeded,
decoder.absolute_offset(),
decoder.path(),
"terminated byte string exceeds configured limit",
),
)
},
fn(encoder, value) {
if max_length < 0 {
return Err(
BinError::new(
InvalidValue,
encoder.length(),
encoder.path(),
"maximum terminated byte length cannot be negative",
),
)
}
if value.length() > max_length {
return Err(
BinError::new(
LimitExceeded,
encoder.length(),
encoder.path(),
"terminated byte string exceeds configured limit",
),
)
}
for byte in value {
if byte == 0U.to_byte() {
return Err(
BinError::new(
InvalidValue,
encoder.length(),
encoder.path(),
"terminated byte string payload cannot contain NUL",
),
)
}
}
match encoder.write_bytes(value[:]) {
Err(error) => Err(error)
Ok(_) => encoder.write_bytes(b"\x00"[:])
}
},
kind="nul-terminated-bytes",
render=fn(value) { bytes_to_hex_preview(value, 32) },
)
}