///|
/// 一份 Codec 同时定义解码和编码行为。
pub struct Codec[T] {
decode_impl : (Decoder) -> Result[T, BinError]
encode_impl : (Encoder, T) -> Result[Unit, BinError]
kind : String
render : (T) -> String
schema : SchemaNode
}
///|
pub fn[T] Codec::make(
decode_impl : (Decoder) -> Result[T, BinError],
encode_impl : (Encoder, T) -> Result[Unit, BinError],
kind~ : String,
render~ : (T) -> String,
schema? : SchemaNode = SchemaNode::leaf(kind),
) -> Codec[T] {
{ decode_impl, encode_impl, kind, render, schema, }
}
///|
pub fn[T] Codec::decode_from(
self : Codec[T],
decoder : Decoder,
) -> Result[T, BinError] {
(self.decode_impl)(decoder)
}
///|
pub fn[T] Codec::encode_into(
self : Codec[T],
encoder : Encoder,
value : T,
) -> Result[Unit, BinError] {
(self.encode_impl)(encoder, value)
}
///|
/// 返回当前 Codec 的独立 Schema 快照。
pub fn[T] Codec::schema_node(self : Codec[T]) -> SchemaNode {
self.schema.copy()
}
///|
/// 渲染当前 Codec 的 Schema 树。
pub fn[T] Codec::describe(self : Codec[T]) -> String {
self.schema.render()
}
///|
/// 为字段增加稳定路径和字节范围追踪。
pub fn[T] Codec::named(self : Codec[T], name : String) -> Codec[T] {
let inner = self
Codec::make(
fn(decoder) {
let start = decoder.absolute_offset()
let full_path = if decoder.path() == "" {
name
} else {
decoder.path() + "." + name
}
decoder.with_path(name, fn() {
match inner.decode_from(decoder) {
Err(error) => Err(error)
Ok(value) =>
match
decoder.record(
full_path,
inner.kind,
start,
decoder.trace_end_offset(),
fn() { (inner.render)(value) },
) {
Err(error) => Err(error)
Ok(_) => Ok(value)
}
}
})
},
fn(encoder, value) {
encoder.with_path(name, fn() { inner.encode_into(encoder, value) })
},
kind=inner.kind,
render=inner.render,
schema=inner.schema.with_name(name),
)
}
///|
/// 在不改变底层字节布局的前提下映射类型。
pub fn[A, B] Codec::xmap(
self : Codec[A],
decode_map : (A) -> Result[B, String],
encode_map : (B) -> Result[A, String],
render~ : (B) -> String,
) -> Codec[B] {
let inner = self
Codec::make(
fn(decoder) {
match inner.decode_from(decoder) {
Err(error) => Err(error)
Ok(value) =>
match decode_map(value) {
Ok(mapped) => Ok(mapped)
Err(message) =>
Err(
BinError::new(
InvalidValue,
decoder.absolute_offset(),
decoder.path(),
message,
),
)
}
}
},
fn(encoder, value) {
match encode_map(value) {
Ok(mapped) => inner.encode_into(encoder, mapped)
Err(message) =>
Err(
BinError::new(
InvalidValue,
encoder.length(),
encoder.path(),
message,
),
)
}
},
kind=inner.kind,
render~,
schema=inner.schema,
)
}
///|
/// 顺序组合两个 Codec。
pub fn[A, B] pair(first : Codec[A], second : Codec[B]) -> Codec[(A, B)] {
Codec::make(
fn(decoder) {
match first.decode_from(decoder) {
Err(error) => Err(error)
Ok(left) =>
match second.decode_from(decoder) {
Err(error) => Err(error)
Ok(right) => Ok((left, right))
}
}
},
fn(encoder, value) {
match first.encode_into(encoder, value.0) {
Err(error) => Err(error)
Ok(_) => second.encode_into(encoder, value.1)
}
},
kind="pair",
render=fn(_) { "" },
schema=SchemaNode::node("pair", [first.schema, second.schema]),
)
}
///|
/// 按固定数量重复元素 Codec。
pub fn[T] repeat(count : Int, item : Codec[T]) -> Codec[Array[T]] {
Codec::make(
fn(decoder) {
if count < 0 {
return Err(
BinError::new(
InvalidValue,
decoder.absolute_offset(),
decoder.path(),
"repeat count cannot be negative",
),
)
}
if count > decoder.limits.max_collection_length {
return Err(
BinError::new(
LimitExceeded,
decoder.absolute_offset(),
decoder.path(),
"repeat count exceeds collection limit",
),
)
}
let values : Array[T] = []
for index = 0; index < count; index = index + 1 {
match item.named(index.to_string()).decode_from(decoder) {
Err(error) => return Err(error)
Ok(value) => values.push(value)
}
}
Ok(values)
},
fn(encoder, values) {
if count > encoder.max_collection_length() {
return Err(
BinError::new(
LimitExceeded,
encoder.length(),
encoder.path(),
"repeat count exceeds collection limit",
),
)
}
if values.length() != count {
return Err(
BinError::new(
InvalidValue,
encoder.length(),
encoder.path(),
"array length does not match fixed repeat count",
),
)
}
for value in values {
match item.encode_into(encoder, value) {
Err(error) => return Err(error)
Ok(_) => ()
}
}
Ok(())
},
kind="array",
render=fn(values) { "[\{values.length()} items]" },
schema=SchemaNode::node("array", [item.schema], constraints=[
"count=" + count.to_string(),
]),
)
}
///|
/// 为 Codec 增加语义约束。
pub fn[T] Codec::validate(
self : Codec[T],
predicate : (T) -> Bool,
message : String,
) -> Codec[T] {
let inner = self
Codec::make(
fn(decoder) {
match inner.decode_from(decoder) {
Err(error) => Err(error)
Ok(value) =>
if predicate(value) {
Ok(value)
} else {
Err(
BinError::new(
InvalidValue,
decoder.absolute_offset(),
decoder.path(),
message,
),
)
}
}
},
fn(encoder, value) {
if predicate(value) {
inner.encode_into(encoder, value)
} else {
Err(
BinError::new(InvalidValue, encoder.length(), encoder.path(), message),
)
}
},
kind=inner.kind,
render=inner.render,
schema=inner.schema.with_constraint("validate: " + message),
)
}
///|
/// 将一个 Codec 限制在固定长度的子区域中。
pub fn[T] bounded(
length : Int,
body : Codec[T],
require_eof? : Bool = true,
) -> Codec[T] {
Codec::make(
fn(decoder) {
if decoder.depth() >= decoder.limits.max_depth {
return Err(
BinError::new(
LimitExceeded,
decoder.absolute_offset(),
decoder.path(),
"maximum decoder nesting depth exceeded",
),
)
}
let start = decoder.absolute_offset()
match decoder.take_view(length) {
Err(error) => Err(error)
Ok(bytes) => {
let child = Decoder::new_child(
bytes,
start,
decoder.path(),
decoder.depth() + 1,
decoder.limits,
)
match body.decode_from(child) {
Err(error) => Err(error)
Ok(value) => {
if require_eof {
match child.finish_decode(true) {
Err(error) => return Err(error)
Ok(_) => ()
}
}
match decoder.append_trace(child.trace()) {
Err(error) => Err(error)
Ok(_) => Ok(value)
}
}
}
}
}
},
fn(encoder, value) {
let child = Encoder::new(encoder.limits)
match body.encode_into(child, value) {
Err(error) => Err(error)
Ok(_) =>
match child.finish() {
Err(error) => Err(error)
Ok(bytes) =>
if bytes.length() != length {
Err(
BinError::new(
InvalidValue,
encoder.length(),
encoder.path(),
"encoded value does not match bounded length",
),
)
} else {
encoder.write_bytes(bytes[:])
}
}
}
},
kind="bounded[\{length}]",
render=body.render,
schema=SchemaNode::node("bounded", [body.schema], constraints=[
"length=" + length.to_string(),
"require_eof=" + (if require_eof { "true" } else { "false" }),
]),
)
}
///|
/// 使用无符号长度字段限定后续值。
pub fn[T] length_prefixed(
length_codec : Codec[UInt],
body : Codec[T],
max_length? : Int = 16 * 1024 * 1024,
) -> Codec[T] {
Codec::make(
fn(decoder) {
match length_codec.named("length").decode_from(decoder) {
Err(error) => Err(error)
Ok(raw_length) => {
if raw_length > 0x7fffffffU {
return Err(
BinError::new(
LimitExceeded,
decoder.absolute_offset(),
decoder.path(),
"length cannot be represented by this runtime",
),
)
}
let length = raw_length.reinterpret_as_int()
if length > max_length {
return Err(
BinError::new(
LimitExceeded,
decoder.absolute_offset(),
decoder.path(),
"length-prefixed field exceeds configured limit",
),
)
}
bounded(length, body).decode_from(decoder)
}
}
},
fn(encoder, value) {
let child = Encoder::new(encoder.limits)
match body.encode_into(child, value) {
Err(error) => Err(error)
Ok(_) =>
match child.finish() {
Err(error) => Err(error)
Ok(bytes) => {
if bytes.length() > max_length {
return Err(
BinError::new(
LimitExceeded,
encoder.length(),
encoder.path(),
"length-prefixed field exceeds configured limit",
),
)
}
match
length_codec.encode_into(
encoder,
bytes.length().reinterpret_as_uint(),
) {
Err(error) => Err(error)
Ok(_) => encoder.write_bytes(bytes[:])
}
}
}
}
},
kind="length-prefixed",
render=body.render,
schema=SchemaNode::node(
"length-prefixed",
[length_codec.schema.with_name("length"), body.schema],
constraints=["max_length=" + max_length.to_string()],
),
)
}
///|
/// 编解码带显式存在标记的可选字段。
pub fn[T] optional_if(flag : Codec[Bool], item : Codec[T]) -> Codec[T?] {
Codec::make(
fn(decoder) {
match flag.named("present").decode_from(decoder) {
Err(error) => Err(error)
Ok(false) => Ok(None)
Ok(true) =>
match item.named("value").decode_from(decoder) {
Err(error) => Err(error)
Ok(value) => Ok(Some(value))
}
}
},
fn(encoder, value) {
match value {
None => flag.encode_into(encoder, false)
Some(actual) =>
match flag.encode_into(encoder, true) {
Err(error) => Err(error)
Ok(_) => item.encode_into(encoder, actual)
}
}
},
kind="optional",
render=fn(value) {
match value {
None => "none"
Some(actual) => (item.render)(actual)
}
},
schema=SchemaNode::node("optional", [
flag.schema.with_name("present"),
item.schema.with_name("value"),
]),
)
}
///|
/// 为任意字节对齐的主体增加尾随校验值。
pub fn[T] checksum_suffix_view(
body : Codec[T],
checksum_codec : Codec[UInt],
calculate : (BytesView) -> UInt,
) -> Codec[T] {
Codec::make(
fn(decoder) {
if decoder.bit_offset != 0 {
return Err(
BinError::new(
Misaligned,
decoder.absolute_offset(),
decoder.path(),
"checksum body must start on a byte boundary",
),
)
}
let start = decoder.offset()
match body.decode_from(decoder) {
Err(error) => Err(error)
Ok(value) => {
if decoder.bit_offset != 0 {
return Err(
BinError::new(
Misaligned,
decoder.absolute_offset(),
decoder.path(),
"checksum body must end on a byte boundary",
),
)
}
let body_bytes = decoder.input[start:decoder.offset()]
match checksum_codec.named("checksum").decode_from(decoder) {
Err(error) => Err(error)
Ok(expected) => {
let actual = calculate(body_bytes)
if actual == expected {
Ok(value)
} else {
Err(
BinError::new(
ChecksumMismatch,
decoder.absolute_offset(),
decoder.path(),
"checksum mismatch: expected \{expected}, calculated \{actual}",
),
)
}
}
}
}
}
},
fn(encoder, value) {
let child = Encoder::new(encoder.limits)
match body.encode_into(child, value) {
Err(error) => Err(error)
Ok(_) =>
match child.finish() {
Err(error) => Err(error)
Ok(bytes) =>
match encoder.write_bytes(bytes[:]) {
Err(error) => Err(error)
Ok(_) =>
checksum_codec.encode_into(encoder, calculate(bytes[:]))
}
}
}
},
kind="checksummed",
render=body.render,
schema=SchemaNode::node("checksummed", [
body.schema,
checksum_codec.schema.with_name("checksum"),
]),
)
}
///|
/// 兼容原有拥有型校验和回调。新代码优先使用 `checksum_suffix_view` 避免解码时复制。
pub fn[T] checksum_suffix(
body : Codec[T],
checksum_codec : Codec[UInt],
calculate : (Bytes) -> UInt,
) -> Codec[T] {
checksum_suffix_view(body, checksum_codec, fn(view) {
calculate(view.to_owned())
})
}
///|
/// 使用前置计数字段编解码数组。
pub fn[T] count_prefixed(
count_codec : Codec[UInt],
item : Codec[T],
max_count? : Int = 1_000_000,
) -> Codec[Array[T]] {
Codec::make(
fn(decoder) {
let raw_count = match count_codec.named("count").decode_from(decoder) {
Err(error) => return Err(error)
Ok(value) => value
}
if raw_count > 0x7fffffffU {
return Err(
BinError::new(
LimitExceeded,
decoder.absolute_offset(),
decoder.path(),
"collection count cannot be represented by this runtime",
),
)
}
let count = raw_count.reinterpret_as_int()
let effective_limit = if max_count < decoder.max_collection_length() {
max_count
} else {
decoder.max_collection_length()
}
if count > effective_limit {
return Err(
BinError::new(
LimitExceeded,
decoder.absolute_offset(),
decoder.path(),
"collection count exceeds configured limit",
),
)
}
repeat(count, item).decode_from(decoder)
},
fn(encoder, values) {
let effective_limit = if max_count < encoder.max_collection_length() {
max_count
} else {
encoder.max_collection_length()
}
if values.length() > effective_limit {
return Err(
BinError::new(
LimitExceeded,
encoder.length(),
encoder.path(),
"collection count exceeds configured limit",
),
)
}
match
count_codec.encode_into(encoder, values.length().reinterpret_as_uint()) {
Err(error) => Err(error)
Ok(_) => repeat(values.length(), item).encode_into(encoder, values)
}
},
kind="count-prefixed-array",
render=fn(values) { "[\{values.length()} items]" },
schema=SchemaNode::node(
"count-prefixed-array",
[count_codec.schema.with_name("count"), item.schema],
constraints=["max_count=" + max_count.to_string()],
),
)
}
///|
/// 重复解码元素直到当前输入区域耗尽。每个元素必须至少消费一个字节。
pub fn[T] until_eof(item : Codec[T]) -> Codec[Array[T]] {
Codec::make(
fn(decoder) {
let values : Array[T] = []
while decoder.remaining() > 0 {
if values.length() >= decoder.max_collection_length() {
return Err(
BinError::new(
LimitExceeded,
decoder.absolute_offset(),
decoder.path(),
"collection count exceeds configured limit",
),
)
}
let before = decoder.offset()
match item.named(values.length().to_string()).decode_from(decoder) {
Err(error) => return Err(error)
Ok(value) => values.push(value)
}
if decoder.offset() == before {
return Err(
BinError::new(
InvalidValue,
decoder.absolute_offset(),
decoder.path(),
"until_eof item codec must consume input",
),
)
}
}
Ok(values)
},
fn(encoder, values) {
if values.length() > encoder.max_collection_length() {
return Err(
BinError::new(
LimitExceeded,
encoder.length(),
encoder.path(),
"collection count exceeds configured limit",
),
)
}
for value in values {
match item.encode_into(encoder, value) {
Err(error) => return Err(error)
Ok(_) => ()
}
}
Ok(())
},
kind="until-eof-array",
render=fn(values) { "[\{values.length()} items]" },
schema=SchemaNode::node("until-eof-array", [item.schema], constraints=[
"terminates=current-region-eof",
]),
)
}
///|
/// 由标签值动态选择后续 Codec,适合 tagged union / switch 协议。
pub fn[T] tagged(
tag_codec : Codec[UInt],
select : (UInt) -> Result[Codec[T], String],
tag_of : (T) -> Result[UInt, String],
render~ : (T) -> String,
) -> Codec[T] {
Codec::make(
fn(decoder) {
let tag = match tag_codec.named("tag").decode_from(decoder) {
Err(error) => return Err(error)
Ok(value) => value
}
match select(tag) {
Err(message) =>
Err(
BinError::new(
InvalidValue,
decoder.absolute_offset(),
decoder.path(),
message,
),
)
Ok(body) => body.named("value").decode_from(decoder)
}
},
fn(encoder, value) {
let tag = match tag_of(value) {
Err(message) =>
return Err(
BinError::new(
InvalidValue,
encoder.length(),
encoder.path(),
message,
),
)
Ok(value) => value
}
let body = match select(tag) {
Err(message) =>
return Err(
BinError::new(
InvalidValue,
encoder.length(),
encoder.path(),
message,
),
)
Ok(codec) => codec
}
match tag_codec.encode_into(encoder, tag) {
Err(error) => Err(error)
Ok(_) => body.encode_into(encoder, value)
}
},
kind="tagged",
render~,
schema=SchemaNode::node("tagged", [tag_codec.schema.with_name("tag")], constraints=[
"variants=dynamic",
]),
)
}