Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changes/unreleased/changed-20261003-003250.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
kind: Changed
body: The table codec now reserves packed repeated enum capacity from the payload length.
time: 2026-10-03T00:32:50.399909+02:00
23 changes: 22 additions & 1 deletion buffa/src/table/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,14 @@ unsafe fn merge_enum<const C: u8>(
let wire = tag.wire_type();
if wire == WireType::LengthDelimited {
let mut payload = take_len_delimited(buf)?;
while !payload.is_empty() {
let raw = types::decode_int32_packed(&mut payload)?;
let stored = (vt.set_packed)(slot, raw, payload);
enum_store_result(table, e, base, raw, stored, ctx)?;
if stored {
break;
}
}
while !payload.is_empty() {
let raw = types::decode_int32_packed(&mut payload)?;
enum_store(table, e, base, vt, slot, raw, ctx)?;
Expand Down Expand Up @@ -427,7 +435,20 @@ unsafe fn enum_store(
ctx: DecodeContext<'_>,
) -> Result<(), DecodeError> {
// SAFETY: `slot` matches the shape `vt` was built for.
if unsafe { (vt.set)(slot, raw) } || table.unknown == NO_UNKNOWN {
let stored = unsafe { (vt.set)(slot, raw) };
enum_store_result(table, e, base, raw, stored, ctx)
}

#[inline]
fn enum_store_result(
table: &MessageTable,
e: &Entry,
base: *mut u8,
raw: i32,
stored: bool,
ctx: DecodeContext<'_>,
) -> Result<(), DecodeError> {
if stored || table.unknown == NO_UNKNOWN {
return Ok(());
}
ctx.register_unknown_field()?;
Expand Down
58 changes: 57 additions & 1 deletion buffa/src/table/shape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ pub struct EnumVt {
/// The argument points to a live slot of the shape the descriptor was
/// built for.
pub(super) set: unsafe fn(*mut u8, i32) -> bool,
pub(super) set_packed: unsafe fn(*mut u8, i32, &[u8]) -> bool,
/// The value at `idx` (ignored for singular shapes), or `None` if unset.
///
/// # Safety
Expand Down Expand Up @@ -212,6 +213,17 @@ pub unsafe trait EnumShape {
/// `slot` points to a live [`Slot`](Self::Slot).
unsafe fn set(slot: *mut u8, raw: i32) -> bool;

/// Store a packed value the same way as [`set`](Self::set), using the
/// remaining payload as a capacity hint.
///
/// # Safety
///
/// `slot` points to a live [`Slot`](Self::Slot).
unsafe fn set_packed(slot: *mut u8, raw: i32, _remaining_payload: &[u8]) -> bool {
// SAFETY: this method has the same slot contract as `set`.
unsafe { Self::set(slot, raw) }
}

/// The value at `idx` (ignored for singular shapes), or `None` if unset.
///
/// # Safety
Expand All @@ -236,14 +248,18 @@ impl EnumVt {
Self {
card: S::CARD,
set: S::set,
set_packed: S::set_packed,
get: S::get,
len: S::len,
}
}
}

const MAX_OPEN_ENUM_RESERVE_BYTES: usize = 64 * 1024;
const MAX_CLOSED_ENUM_RESERVE_VALUES: usize = 16;

macro_rules! enum_shape {
($(#[$m:meta])* $name:ident, $card:ident, $slot:ty, $set:expr, $get:expr, $len:expr) => {
($(#[$m:meta])* $name:ident, $card:ident, $slot:ty, $set:expr, $set_packed:expr, $get:expr, $len:expr) => {
$(#[$m])*
pub struct $name<E>(PhantomData<E>);

Expand All @@ -260,6 +276,13 @@ macro_rules! enum_shape {
($set)(s, raw)
}

#[inline]
unsafe fn set_packed(slot: *mut u8, raw: i32, remaining_payload: &[u8]) -> bool {
// SAFETY: as above.
let s = unsafe { &mut *slot.cast::<$slot>() };
($set_packed)(s, raw, remaining_payload)
}

#[inline]
unsafe fn get(slot: *const u8, idx: usize) -> Option<i32> {
// SAFETY: as above.
Expand All @@ -273,6 +296,7 @@ macro_rules! enum_shape {
let s = unsafe { &*slot.cast::<$slot>() };
($len)(s)
}

}
};
}
Expand All @@ -281,41 +305,73 @@ enum_shape!(
/// An open enum with implicit presence: `EnumValue<E>`.
ImplicitOpen, IMPLICIT, EnumValue<E>,
|s: &mut EnumValue<E>, raw| { *s = EnumValue::from(raw); true },
|s: &mut EnumValue<E>, raw, _| { *s = EnumValue::from(raw); true },
|s: &EnumValue<E>, _| Some(s.to_i32()),
|_: &EnumValue<E>| 0
);
enum_shape!(
/// A closed enum with implicit presence: `E`.
ImplicitClosed, IMPLICIT, E,
|s: &mut E, raw| match E::from_i32(raw) { Some(v) => { *s = v; true } None => false },
|s: &mut E, raw, _| match E::from_i32(raw) { Some(v) => { *s = v; true } None => false },
|s: &E, _| Some(s.to_i32()),
|_: &E| 0
);
enum_shape!(
/// An open enum with explicit presence: `Option<EnumValue<E>>`.
OptionalOpen, OPTIONAL, Option<EnumValue<E>>,
|s: &mut Option<EnumValue<E>>, raw| { *s = Some(EnumValue::from(raw)); true },
|s: &mut Option<EnumValue<E>>, raw, _| { *s = Some(EnumValue::from(raw)); true },
|s: &Option<EnumValue<E>>, _| s.as_ref().map(EnumValue::to_i32),
|_: &Option<EnumValue<E>>| 0
);
enum_shape!(
/// A closed enum with explicit presence: `Option<E>`.
OptionalClosed, OPTIONAL, Option<E>,
|s: &mut Option<E>, raw| match E::from_i32(raw) { Some(v) => { *s = Some(v); true } None => false },
|s: &mut Option<E>, raw, _| match E::from_i32(raw) { Some(v) => { *s = Some(v); true } None => false },
|s: &Option<E>, _| s.as_ref().map(Enumeration::to_i32),
|_: &Option<E>| 0
);
enum_shape!(
/// A repeated open enum: `Vec<EnumValue<E>>`.
RepeatedOpen, REPEATED, Vec<EnumValue<E>>,
|s: &mut Vec<EnumValue<E>>, raw| { s.push(EnumValue::from(raw)); true },
|s: &mut Vec<EnumValue<E>>, raw, remaining: &[u8]| {
let requested = remaining
.len()
.saturating_add(1)
.min(MAX_OPEN_ENUM_RESERVE_BYTES / core::mem::size_of::<EnumValue<E>>().max(1));
let available = s.capacity().saturating_sub(s.len());
if available == 0 && requested > 0 {
s.reserve_exact(requested);
}
s.push(EnumValue::from(raw));
true
},
|s: &Vec<EnumValue<E>>, i| s.get(i).map(EnumValue::to_i32),
|s: &Vec<EnumValue<E>>| s.len()
);
enum_shape!(
/// A repeated closed enum: `Vec<E>`.
RepeatedClosed, REPEATED, Vec<E>,
|s: &mut Vec<E>, raw| match E::from_i32(raw) { Some(v) => { s.push(v); true } None => false },
|s: &mut Vec<E>, raw, remaining: &[u8]| match E::from_i32(raw) {
Some(v) => {
let requested = remaining
.len()
.saturating_add(1)
.min(MAX_CLOSED_ENUM_RESERVE_VALUES)
.min(MAX_OPEN_ENUM_RESERVE_BYTES / core::mem::size_of::<E>().max(1));
let available = s.capacity().saturating_sub(s.len());
if available == 0 && requested > 0 {
s.reserve_exact(requested);
}
s.push(v);
true
}
None => false,
},
|s: &Vec<E>, i| s.get(i).map(Enumeration::to_i32),
|s: &Vec<E>| s.len()
);
86 changes: 86 additions & 0 deletions buffa/src/table/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,92 @@ fn a_closed_enum_value_it_does_not_know_becomes_an_unknown_field() {
);
}

#[test]
fn a_packed_repeated_open_enum_appends_values_to_existing_storage() {
let mut wire = vec![0x42, 0x40];
for _ in 0..32 {
wire.extend_from_slice(&[0x80, 0x01]);
}

let mut msg = Wide {
rep_enum_open: vec![EnumValue::from(42)],
..Wide::default()
};
msg.merge_from_slice(&wire).unwrap();

assert_eq!(msg.rep_enum_open.len(), 33);
assert_eq!(msg.rep_enum_open[0].to_i32(), 42);
assert!(msg.rep_enum_open[1..]
.iter()
.all(|value| value.to_i32() == 128));
}

#[test]
fn an_empty_packed_repeated_enum_payload_does_not_reserve() {
let msg = Wide::decode_from_slice(&[0x42, 0x00]).unwrap();

assert!(msg.rep_enum_open.is_empty());
assert_eq!(msg.rep_enum_open.capacity(), 0);
}

#[test]
fn a_packed_repeated_closed_enum_preserves_unknown_values() {
let mut wire = vec![0x5a, 0x41];
wire.push(0x00);
for _ in 0..32 {
wire.extend_from_slice(&[0x80, 0x01]);
}

let msg = Outer::decode_from_slice(&wire).unwrap();

assert_eq!(msg.packed_closed, [Color::Red]);
assert_eq!(msg.unknown.len(), 32);
}

#[test]
fn an_all_unknown_packed_closed_enum_does_not_reserve() {
let mut wire = vec![0x5a, 0x40];
for _ in 0..32 {
wire.extend_from_slice(&[0x80, 0x01]);
}

let msg = Outer::decode_from_slice(&wire).unwrap();

assert!(msg.packed_closed.is_empty());
assert_eq!(msg.unknown.len(), 32);
assert_eq!(msg.packed_closed.capacity(), 0);
}

#[test]
fn a_malformed_packed_open_enum_does_not_reserve_from_later_bytes() {
let mut wire = vec![0x42, 0x4b];
wire.extend_from_slice(&[0x80; 11]);
wire.extend_from_slice(&[0x00; 64]);

let mut msg = Wide::default();
assert_eq!(msg.merge_from_slice(&wire), Err(DecodeError::VarintTooLong));
assert!(msg.rep_enum_open.is_empty());
assert_eq!(msg.rep_enum_open.capacity(), 0);
}

#[test]
fn a_malformed_packed_open_enum_tail_keeps_existing_spare_capacity() {
let mut wire = vec![0x42, 0x70];
wire.push(0x00);
wire.extend_from_slice(&[0x80; 11]);
wire.extend_from_slice(&[0x00; 100]);

let mut msg = Wide {
rep_enum_open: Vec::with_capacity(2),
..Wide::default()
};
msg.rep_enum_open.push(EnumValue::from(42));
let capacity = msg.rep_enum_open.capacity();
assert_eq!(msg.merge_from_slice(&wire), Err(DecodeError::VarintTooLong));
assert_eq!(msg.rep_enum_open.len(), 2);
assert_eq!(msg.rep_enum_open.capacity(), capacity);
}

#[test]
fn a_closed_enum_value_is_dropped_by_a_message_that_drops_unknown_fields() {
// rep_enum_closed (20) = [1, 7, 2] unpacked; optional closed (9) = 7.
Expand Down
Loading