use crate::db::data::structural_field::{
FieldDecodeError,
binary::{
TAG_BYTES, TAG_INT64, TAG_LIST, TAG_NAT64, TAG_NULL, parse_binary_head,
payload_bytes as binary_payload_bytes, push_binary_bytes, push_binary_int64,
push_binary_list_len, push_binary_nat64, push_binary_null, skip_binary_value,
},
primary_key_component::{
decode_primary_key_component_binary_value_bytes,
encode_primary_key_component_binary_value_bytes,
},
primitive::{decode_i64_payload_bytes, decode_u64_payload_bytes},
typed::{
decode_date_payload_days, decode_decimal_payload_parts, decode_duration_payload_millis,
encode_date_payload_days, encode_decimal_payload_parts, encode_duration_payload_millis,
},
};
use crate::{
error::InternalError,
model::field::FieldKind,
types::{Date, Decimal, Duration, IntBig, NatBig},
value::Value,
};
use num_bigint::{BigInt, BigUint, Sign as BigIntSign};
pub(super) fn decode_leaf_field_by_kind_bytes(
raw_bytes: &[u8],
kind: FieldKind,
) -> Result<Option<Value>, FieldDecodeError> {
let value = match kind {
FieldKind::Account
| FieldKind::Principal
| FieldKind::Subaccount
| FieldKind::Timestamp
| FieldKind::Unit => decode_primary_key_component_binary_value_bytes(raw_bytes, kind)?
.expect("storage-key-owned leaf kinds must return a value"),
FieldKind::Date => decode_date_value_bytes(raw_bytes)?,
FieldKind::Decimal { .. } => decode_decimal_value_bytes(raw_bytes)?,
FieldKind::Duration => decode_duration_value_bytes(raw_bytes)?,
FieldKind::IntBig { max_bytes } => decode_int_big_value_bytes(raw_bytes, max_bytes)?,
FieldKind::Structured { .. } => decode_structured_leaf_null_value_bytes(raw_bytes)?,
FieldKind::NatBig { max_bytes } => decode_nat_big_value_bytes(raw_bytes, max_bytes)?,
FieldKind::Blob { .. }
| FieldKind::Bool
| FieldKind::Float32
| FieldKind::Float64
| FieldKind::Int8
| FieldKind::Int16
| FieldKind::Int32
| FieldKind::Int64
| FieldKind::Int128
| FieldKind::Text { .. }
| FieldKind::Nat8
| FieldKind::Nat16
| FieldKind::Nat32
| FieldKind::Nat64
| FieldKind::Nat128
| FieldKind::Ulid => {
return Err(FieldDecodeError::new(
"scalar field unexpectedly bypassed byte-level fast path",
));
}
FieldKind::Enum { .. }
| FieldKind::List(_)
| FieldKind::Map { .. }
| FieldKind::Relation { .. }
| FieldKind::Set(_) => return Ok(None),
};
Ok(Some(value))
}
pub(super) fn encode_leaf_field_binary_bytes(
kind: FieldKind,
value: &Value,
field_name: &str,
) -> Result<Option<Vec<u8>>, InternalError> {
let encoded = match kind {
FieldKind::Account
| FieldKind::Principal
| FieldKind::Subaccount
| FieldKind::Timestamp
| FieldKind::Unit => {
encode_primary_key_component_binary_value_bytes(kind, value, field_name)?
}
FieldKind::Date => Some(encode_date_value_bytes(value, field_name)?),
FieldKind::Decimal { .. } => Some(encode_decimal_value_bytes(value, field_name)?),
FieldKind::Duration => Some(encode_duration_value_bytes(value, field_name)?),
FieldKind::IntBig { max_bytes } => {
Some(encode_int_big_value_bytes(value, max_bytes, field_name)?)
}
FieldKind::Structured { .. } => Some(encode_structured_leaf_null_bytes(value, field_name)?),
FieldKind::NatBig { max_bytes } => {
Some(encode_nat_big_value_bytes(value, max_bytes, field_name)?)
}
FieldKind::Blob { .. }
| FieldKind::Bool
| FieldKind::Float32
| FieldKind::Float64
| FieldKind::Int8
| FieldKind::Int16
| FieldKind::Int32
| FieldKind::Int64
| FieldKind::Int128
| FieldKind::Text { .. }
| FieldKind::Nat8
| FieldKind::Nat16
| FieldKind::Nat32
| FieldKind::Nat64
| FieldKind::Nat128
| FieldKind::Ulid
| FieldKind::Enum { .. }
| FieldKind::List(_)
| FieldKind::Map { .. }
| FieldKind::Relation { .. }
| FieldKind::Set(_) => None,
};
Ok(encoded)
}
fn decode_structured_leaf_null_value_bytes(raw_bytes: &[u8]) -> Result<Value, FieldDecodeError> {
decode_required_null_payload(raw_bytes, "structured")?;
Ok(Value::Null)
}
fn encode_structured_leaf_null_bytes(
value: &Value,
field_name: &str,
) -> Result<Vec<u8>, InternalError> {
let Value::Null = value else {
return Err(InternalError::persisted_row_field_encode_failed(
field_name,
"structured ByKind field encoding is unsupported",
));
};
let mut encoded = Vec::new();
push_binary_null(&mut encoded);
Ok(encoded)
}
fn decode_date_value_bytes(raw_bytes: &[u8]) -> Result<Value, FieldDecodeError> {
decode_date_payload_days(decode_required_i64_payload(raw_bytes, "date days")?).map(Value::Date)
}
fn decode_decimal_value_bytes(raw_bytes: &[u8]) -> Result<Value, FieldDecodeError> {
let items = split_binary_tuple_items(raw_bytes, 2, "decimal")?;
let mantissa_bytes: [u8; 16] = decode_required_bytes_payload(items[0], "decimal mantissa")?
.try_into()
.map_err(|_| {
FieldDecodeError::new(
"structural binary: invalid decimal mantissa length: 16 bytes expected",
)
})?;
let scale = decode_required_u32_payload(items[1], "decimal scale")?;
Ok(Value::Decimal(decode_decimal_payload_parts(
i128::from_be_bytes(mantissa_bytes),
scale,
)?))
}
fn decode_duration_value_bytes(raw_bytes: &[u8]) -> Result<Value, FieldDecodeError> {
Ok(Value::Duration(decode_duration_payload_millis(
decode_required_u64_payload(raw_bytes, "duration millis")?,
)))
}
fn decode_int_big_value_bytes(raw_bytes: &[u8], max_bytes: u32) -> Result<Value, FieldDecodeError> {
let items = split_binary_tuple_items(raw_bytes, 2, "int_big")?;
let sign = decode_big_integer_sign_payload(items[0])?;
let magnitude = decode_big_integer_magnitude_payload(items[1])?;
let value = IntBig::from_bigint(BigInt::from_biguint(sign, magnitude));
ensure_int_big_max_bytes(&value, max_bytes)?;
Ok(Value::IntBig(value))
}
fn decode_nat_big_value_bytes(raw_bytes: &[u8], max_bytes: u32) -> Result<Value, FieldDecodeError> {
let value = NatBig::from_biguint(decode_big_integer_magnitude_payload(raw_bytes)?);
ensure_nat_big_max_bytes(&value, max_bytes)?;
Ok(Value::NatBig(value))
}
fn encode_date_value_bytes(value: &Value, field_name: &str) -> Result<Vec<u8>, InternalError> {
let Value::Date(value) = value else {
return Err(InternalError::persisted_row_field_encode_failed(
field_name,
format!("field kind Date does not accept runtime value {value:?}"),
));
};
let mut encoded = Vec::new();
push_binary_int64(&mut encoded, encode_date_payload_days(*value));
Ok(encoded)
}
fn encode_decimal_value_bytes(value: &Value, field_name: &str) -> Result<Vec<u8>, InternalError> {
let Value::Decimal(value) = value else {
return Err(InternalError::persisted_row_field_encode_failed(
field_name,
format!("field kind Decimal does not accept runtime value {value:?}"),
));
};
let (mantissa, scale) = encode_decimal_payload_parts(*value);
let mut encoded = Vec::new();
push_binary_list_len(&mut encoded, 2);
push_binary_bytes(&mut encoded, &mantissa.to_be_bytes());
push_binary_nat64(&mut encoded, u64::from(scale));
Ok(encoded)
}
fn encode_duration_value_bytes(value: &Value, field_name: &str) -> Result<Vec<u8>, InternalError> {
let Value::Duration(value) = value else {
return Err(InternalError::persisted_row_field_encode_failed(
field_name,
format!("field kind Duration does not accept runtime value {value:?}"),
));
};
let mut encoded = Vec::new();
push_binary_nat64(&mut encoded, encode_duration_payload_millis(*value));
Ok(encoded)
}
fn encode_int_big_value_bytes(
value: &Value,
max_bytes: u32,
field_name: &str,
) -> Result<Vec<u8>, InternalError> {
let Value::IntBig(value) = value else {
return Err(InternalError::persisted_row_field_encode_failed(
field_name,
format!("field kind IntBig does not accept runtime value {value:?}"),
));
};
ensure_int_big_max_bytes(value, max_bytes).map_err(|err| {
InternalError::persisted_row_field_encode_failed(field_name, err.to_string())
})?;
let (is_negative, digits) = value.sign_and_u32_digits();
let sign = if digits.is_empty() {
0
} else if is_negative {
-1
} else {
1
};
let mut encoded = Vec::new();
push_binary_list_len(&mut encoded, 2);
push_binary_int64(&mut encoded, sign);
push_binary_u32_digit_list(&mut encoded, digits.as_slice());
Ok(encoded)
}
fn encode_nat_big_value_bytes(
value: &Value,
max_bytes: u32,
field_name: &str,
) -> Result<Vec<u8>, InternalError> {
let Value::NatBig(value) = value else {
return Err(InternalError::persisted_row_field_encode_failed(
field_name,
format!("field kind NatBig does not accept runtime value {value:?}"),
));
};
ensure_nat_big_max_bytes(value, max_bytes).map_err(|err| {
InternalError::persisted_row_field_encode_failed(field_name, err.to_string())
})?;
let mut encoded = Vec::new();
push_binary_u32_digit_list(&mut encoded, value.u32_digits().as_slice());
Ok(encoded)
}
fn ensure_int_big_max_bytes(value: &IntBig, max_bytes: u32) -> Result<(), FieldDecodeError> {
let len = value.to_leb128().len();
if len > max_bytes as usize {
return Err(FieldDecodeError::new(format!(
"structural binary: int_big payload exceeds max_bytes: expected at most {max_bytes}, found {len}"
)));
}
Ok(())
}
fn ensure_nat_big_max_bytes(value: &NatBig, max_bytes: u32) -> Result<(), FieldDecodeError> {
let len = value.to_leb128().len();
if len > max_bytes as usize {
return Err(FieldDecodeError::new(format!(
"structural binary: nat_big payload exceeds max_bytes: expected at most {max_bytes}, found {len}"
)));
}
Ok(())
}
fn push_binary_u32_digit_list(out: &mut Vec<u8>, digits: &[u32]) {
push_binary_list_len(out, digits.len());
for digit in digits {
push_binary_nat64(out, u64::from(*digit));
}
}
fn decode_big_integer_sign_payload(raw_bytes: &[u8]) -> Result<BigIntSign, FieldDecodeError> {
match decode_required_i64_payload(raw_bytes, "int_big sign")? {
-1 => Ok(BigIntSign::Minus),
0 => Ok(BigIntSign::NoSign),
1 => Ok(BigIntSign::Plus),
other => Err(FieldDecodeError::new(format!(
"structural binary: invalid int_big sign {other}"
))),
}
}
fn decode_big_integer_magnitude_payload(raw_bytes: &[u8]) -> Result<BigUint, FieldDecodeError> {
let Some((tag, len, payload_start)) = parse_binary_head(raw_bytes, 0)? else {
return Err(FieldDecodeError::new(
"structural binary: truncated big-integer magnitude payload",
));
};
if tag != TAG_LIST {
return Err(FieldDecodeError::new(
"structural binary: expected big-integer magnitude limb sequence",
));
}
let mut cursor = payload_start;
let mut limbs = Vec::with_capacity(len as usize);
for _ in 0..len {
let limb_start = cursor;
cursor = skip_binary_value(raw_bytes, cursor)?;
limbs.push(decode_required_u32_payload(
&raw_bytes[limb_start..cursor],
"big-integer magnitude limb",
)?);
}
if cursor != raw_bytes.len() {
return Err(FieldDecodeError::new(
"structural binary: trailing bytes after big-integer magnitude payload",
));
}
Ok(BigUint::new(limbs))
}
fn decode_required_null_payload(
raw_bytes: &[u8],
label: &'static str,
) -> Result<(), FieldDecodeError> {
let Some((tag, _, _)) = parse_binary_head(raw_bytes, 0)? else {
return Err(FieldDecodeError::new(format!(
"structural binary: truncated {label} payload"
)));
};
let end = skip_binary_value(raw_bytes, 0)?;
if end != raw_bytes.len() || tag != TAG_NULL {
return Err(FieldDecodeError::new(format!(
"structural binary: expected null for {label}"
)));
}
Ok(())
}
fn decode_required_bytes_payload<'a>(
raw_bytes: &'a [u8],
label: &'static str,
) -> Result<&'a [u8], FieldDecodeError> {
let Some((tag, len, payload_start)) = parse_binary_head(raw_bytes, 0)? else {
return Err(FieldDecodeError::new(format!(
"structural binary: truncated {label} payload"
)));
};
let end = skip_binary_value(raw_bytes, 0)?;
if end != raw_bytes.len() || tag != TAG_BYTES {
return Err(FieldDecodeError::new(format!(
"structural binary: expected bytes for {label}"
)));
}
binary_payload_bytes(raw_bytes, len, payload_start, label)
}
fn decode_required_u32_payload(
raw_bytes: &[u8],
label: &'static str,
) -> Result<u32, FieldDecodeError> {
u32::try_from(decode_required_u64_payload(raw_bytes, label)?)
.map_err(|_| FieldDecodeError::new(format!("structural binary: {label} out of u32 range")))
}
fn decode_required_u64_payload(
raw_bytes: &[u8],
label: &'static str,
) -> Result<u64, FieldDecodeError> {
let Some((tag, len, payload_start)) = parse_binary_head(raw_bytes, 0)? else {
return Err(FieldDecodeError::new(format!(
"structural binary: truncated {label} payload"
)));
};
let end = skip_binary_value(raw_bytes, 0)?;
if end != raw_bytes.len() || tag != TAG_NAT64 || len != 8 {
return Err(FieldDecodeError::new(format!(
"structural binary: expected u64 for {label}"
)));
}
decode_u64_payload_bytes(
binary_payload_bytes(raw_bytes, len, payload_start, label)?,
label,
)
}
fn decode_required_i64_payload(
raw_bytes: &[u8],
label: &'static str,
) -> Result<i64, FieldDecodeError> {
let Some((tag, len, payload_start)) = parse_binary_head(raw_bytes, 0)? else {
return Err(FieldDecodeError::new(format!(
"structural binary: truncated {label} payload"
)));
};
let end = skip_binary_value(raw_bytes, 0)?;
if end != raw_bytes.len() || tag != TAG_INT64 || len != 8 {
return Err(FieldDecodeError::new(format!(
"structural binary: expected i64 for {label}"
)));
}
decode_i64_payload_bytes(
binary_payload_bytes(raw_bytes, len, payload_start, label)?,
label,
)
}
fn split_binary_tuple_items<'a>(
raw_bytes: &'a [u8],
expected_len: u32,
label: &'static str,
) -> Result<Vec<&'a [u8]>, FieldDecodeError> {
let Some((tag, len, payload_start)) = parse_binary_head(raw_bytes, 0)? else {
return Err(FieldDecodeError::new(format!(
"structural binary: truncated {label} payload"
)));
};
if tag != TAG_LIST || len != expected_len {
return Err(FieldDecodeError::new(format!(
"structural binary: expected {label} tuple of length {expected_len}"
)));
}
let mut items = Vec::with_capacity(expected_len as usize);
let mut cursor = payload_start;
for _ in 0..expected_len {
let item_start = cursor;
cursor = skip_binary_value(raw_bytes, cursor)?;
items.push(&raw_bytes[item_start..cursor]);
}
if cursor != raw_bytes.len() {
return Err(FieldDecodeError::new(format!(
"structural binary: trailing bytes after {label} payload"
)));
}
Ok(items)
}
pub(super) fn encode_date_field_by_kind_bytes(
value: Date,
kind: FieldKind,
field_name: &str,
) -> Result<Vec<u8>, InternalError> {
if !matches!(kind, FieldKind::Date) {
return Err(InternalError::persisted_row_field_encode_failed(
field_name,
format!("field kind {kind:?} does not accept date"),
));
}
encode_date_value_bytes(&Value::Date(value), field_name)
}
pub(super) fn decode_date_field_by_kind_bytes(
raw_bytes: &[u8],
kind: FieldKind,
) -> Result<Option<Date>, FieldDecodeError> {
if !matches!(kind, FieldKind::Date) {
return Err(FieldDecodeError::new(
"field kind is not owned by the structural date leaf lane",
));
}
match decode_date_value_bytes(raw_bytes)? {
Value::Date(value) => Ok(Some(value)),
_ => Err(FieldDecodeError::new(
"structural date leaf unexpectedly decoded as non-date value",
)),
}
}
pub(super) fn encode_decimal_field_by_kind_bytes(
value: Decimal,
kind: FieldKind,
field_name: &str,
) -> Result<Vec<u8>, InternalError> {
if !matches!(kind, FieldKind::Decimal { .. }) {
return Err(InternalError::persisted_row_field_encode_failed(
field_name,
format!("field kind {kind:?} does not accept decimal"),
));
}
encode_decimal_value_bytes(&Value::Decimal(value), field_name)
}
pub(super) fn decode_decimal_field_by_kind_bytes(
raw_bytes: &[u8],
kind: FieldKind,
) -> Result<Option<Decimal>, FieldDecodeError> {
if !matches!(kind, FieldKind::Decimal { .. }) {
return Err(FieldDecodeError::new(
"field kind is not owned by the structural decimal leaf lane",
));
}
match decode_decimal_value_bytes(raw_bytes)? {
Value::Decimal(value) => Ok(Some(value)),
_ => Err(FieldDecodeError::new(
"structural decimal leaf unexpectedly decoded as non-decimal value",
)),
}
}
pub(super) fn encode_duration_field_by_kind_bytes(
value: Duration,
kind: FieldKind,
field_name: &str,
) -> Result<Vec<u8>, InternalError> {
if !matches!(kind, FieldKind::Duration) {
return Err(InternalError::persisted_row_field_encode_failed(
field_name,
format!("field kind {kind:?} does not accept duration"),
));
}
encode_duration_value_bytes(&Value::Duration(value), field_name)
}
pub(super) fn decode_duration_field_by_kind_bytes(
raw_bytes: &[u8],
kind: FieldKind,
) -> Result<Option<Duration>, FieldDecodeError> {
if !matches!(kind, FieldKind::Duration) {
return Err(FieldDecodeError::new(
"field kind is not owned by the structural duration leaf lane",
));
}
match decode_duration_value_bytes(raw_bytes)? {
Value::Duration(value) => Ok(Some(value)),
_ => Err(FieldDecodeError::new(
"structural duration leaf unexpectedly decoded as non-duration value",
)),
}
}
pub(super) fn encode_int_big_field_by_kind_bytes(
value: &IntBig,
kind: FieldKind,
field_name: &str,
) -> Result<Vec<u8>, InternalError> {
let FieldKind::IntBig { max_bytes } = kind else {
return Err(InternalError::persisted_row_field_encode_failed(
field_name,
format!("field kind {kind:?} does not accept int_big payload"),
));
};
encode_int_big_value_bytes(&Value::IntBig(value.clone()), max_bytes, field_name)
}
pub(super) fn decode_int_big_field_by_kind_bytes(
raw_bytes: &[u8],
kind: FieldKind,
) -> Result<Option<IntBig>, FieldDecodeError> {
let FieldKind::IntBig { max_bytes } = kind else {
return Err(FieldDecodeError::new(
"field kind is not owned by the structural int_big leaf lane",
));
};
match decode_int_big_value_bytes(raw_bytes, max_bytes)? {
Value::IntBig(value) => Ok(Some(value)),
_ => Err(FieldDecodeError::new(
"structural int_big leaf unexpectedly decoded as non-int_big value",
)),
}
}
pub(super) fn encode_nat_big_field_by_kind_bytes(
value: &NatBig,
kind: FieldKind,
field_name: &str,
) -> Result<Vec<u8>, InternalError> {
let FieldKind::NatBig { max_bytes } = kind else {
return Err(InternalError::persisted_row_field_encode_failed(
field_name,
format!("field kind {kind:?} does not accept nat_big payload"),
));
};
encode_nat_big_value_bytes(&Value::NatBig(value.clone()), max_bytes, field_name)
}
pub(super) fn decode_nat_big_field_by_kind_bytes(
raw_bytes: &[u8],
kind: FieldKind,
) -> Result<Option<NatBig>, FieldDecodeError> {
let FieldKind::NatBig { max_bytes } = kind else {
return Err(FieldDecodeError::new(
"field kind is not owned by the structural nat_big leaf lane",
));
};
match decode_nat_big_value_bytes(raw_bytes, max_bytes)? {
Value::NatBig(value) => Ok(Some(value)),
_ => Err(FieldDecodeError::new(
"structural nat_big leaf unexpectedly decoded as non-nat_big value",
)),
}
}
#[cfg(test)]
mod tests {
use super::{
TAG_NULL, decode_leaf_field_by_kind_bytes, encode_leaf_field_binary_bytes,
push_binary_bytes, push_binary_int64, push_binary_list_len, push_binary_nat64,
push_binary_null,
};
use crate::{
db::data::structural_field::{
binary::push_binary_text, validate_structural_field_by_kind_bytes,
},
model::field::{DEFAULT_BIG_INT_MAX_BYTES, FieldKind},
types::{Date, Decimal, Duration, IntBig, NatBig},
value::Value,
};
#[test]
fn leaf_field_binary_roundtrips_supported_leaf_wrappers() {
let cases = vec![
(
FieldKind::Date,
Value::Date(Date::new_checked(2025, 10, 19).expect("valid date")),
),
(
FieldKind::Decimal { scale: 2 },
Value::Decimal(Decimal::from_i128_with_scale(12_345, 2)),
),
(FieldKind::Duration, Value::Duration(Duration::from_secs(5))),
(
FieldKind::IntBig {
max_bytes: DEFAULT_BIG_INT_MAX_BYTES,
},
Value::IntBig(IntBig::from(123_456_789_i64)),
),
(
FieldKind::NatBig {
max_bytes: DEFAULT_BIG_INT_MAX_BYTES,
},
Value::NatBig(NatBig::from(987_654_321_u64)),
),
(FieldKind::Structured { queryable: false }, Value::Null),
];
for (kind, value) in cases {
let encoded = encode_leaf_field_binary_bytes(kind, &value, "field")
.expect("leaf payload should encode")
.expect("leaf kind should be owned by the leaf lane");
let decoded = decode_leaf_field_by_kind_bytes(encoded.as_slice(), kind)
.expect("leaf payload should decode")
.expect("leaf kind should decode through the leaf lane");
validate_structural_field_by_kind_bytes(encoded.as_slice(), kind)
.expect("leaf payload should validate");
assert_eq!(decoded, value, "leaf roundtrip mismatch for {kind:?}");
}
}
#[test]
fn leaf_field_binary_rejects_malformed_decimal_payload() {
let mut bytes = Vec::new();
push_binary_list_len(&mut bytes, 2);
push_binary_bytes(&mut bytes, &1_i128.to_be_bytes());
push_binary_nat64(&mut bytes, u64::from(Decimal::max_supported_scale() + 1));
let kind = FieldKind::Decimal { scale: 2 };
let decode = decode_leaf_field_by_kind_bytes(bytes.as_slice(), kind);
let validate = validate_structural_field_by_kind_bytes(bytes.as_slice(), kind);
assert!(
decode.is_err(),
"malformed decimal payload must fail decode"
);
assert!(
validate.is_err(),
"malformed decimal payload must fail validate"
);
}
#[test]
fn leaf_field_binary_rejects_invalid_int_big_sign() {
let mut bytes = Vec::new();
push_binary_list_len(&mut bytes, 2);
push_binary_int64(&mut bytes, 2);
push_binary_list_len(&mut bytes, 0);
let kind = FieldKind::IntBig {
max_bytes: DEFAULT_BIG_INT_MAX_BYTES,
};
let decode = decode_leaf_field_by_kind_bytes(bytes.as_slice(), kind);
let validate = validate_structural_field_by_kind_bytes(bytes.as_slice(), kind);
assert!(decode.is_err(), "invalid int_big sign must fail decode");
assert!(validate.is_err(), "invalid int_big sign must fail validate");
}
#[test]
fn leaf_field_binary_rejects_non_list_nat_big_payload() {
let mut bytes = Vec::new();
push_binary_text(&mut bytes, "not-a-limb-list");
let kind = FieldKind::NatBig {
max_bytes: DEFAULT_BIG_INT_MAX_BYTES,
};
let decode = decode_leaf_field_by_kind_bytes(bytes.as_slice(), kind);
let validate = validate_structural_field_by_kind_bytes(bytes.as_slice(), kind);
assert!(decode.is_err(), "non-list nat_big payload must fail decode");
assert!(
validate.is_err(),
"non-list nat_big payload must fail validate"
);
}
#[test]
fn leaf_field_binary_rejects_structured_non_null_payload() {
let mut bytes = Vec::new();
push_binary_null(&mut bytes);
bytes.push(TAG_NULL);
let kind = FieldKind::Structured { queryable: false };
let decode = decode_leaf_field_by_kind_bytes(bytes.as_slice(), kind);
let validate = validate_structural_field_by_kind_bytes(bytes.as_slice(), kind);
assert!(
decode.is_err(),
"structured leaf trailing bytes must fail decode"
);
assert!(
validate.is_err(),
"structured leaf trailing bytes must fail validate"
);
}
}