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
28 changes: 28 additions & 0 deletions buffa-codegen/src/tests/view_codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,34 @@ fn test_view_repeated_message_field() {
);
}

#[test]
fn test_view_packed_scalar_reserves_capacity() {
let mut file = proto3_file("packed_view.proto");
file.message_type.push(DescriptorProto {
name: Some("PackedView".to_string()),
field: vec![
make_field("ids", 1, Label::LABEL_REPEATED, Type::TYPE_UINT32),
make_field("scores", 2, Label::LABEL_REPEATED, Type::TYPE_DOUBLE),
],
..Default::default()
});
let files = generate(
&[file],
&["packed_view.proto".to_string()],
&CodeGenConfig::default(),
)
.expect("should generate");
let content = &joined(&files);
assert!(
content.contains("view.ids.reserve(payload.len());"),
"varint packed view must reserve using the payload length: {content}"
);
assert!(
content.contains("view.scores.reserve(payload.len() / 8usize);"),
"fixed-width packed view must reserve the exact element count: {content}"
);
}

#[test]
fn test_view_oneof_with_message_variant() {
let mut file = proto3_file("oneof_msg.proto");
Expand Down
16 changes: 16 additions & 0 deletions buffa-codegen/src/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1229,6 +1229,21 @@ fn repeated_decode_arm(
let dfn = decode_fn_token(ty);
quote! { view.#ident.push(#dfn(&mut pcur)?); }
};

// Fixed-size types can reserve the exact element count. For varints and
// bools, every element occupies at least one byte, so the payload length
// is a safe upper bound.
let reserve_divisor: usize = match ty {
Type::TYPE_FIXED32 | Type::TYPE_SFIXED32 | Type::TYPE_FLOAT => 4,
Type::TYPE_FIXED64 | Type::TYPE_SFIXED64 | Type::TYPE_DOUBLE => 8,
_ => 1,
};
let reserve_stmt = if reserve_divisor > 1 {
quote! { view.#ident.reserve(payload.len() / #reserve_divisor); }
} else {
quote! { view.#ident.reserve(payload.len()); }
};

let unpacked_elem = if ty == Type::TYPE_ENUM {
if closed {
// Unpacked: each element has its own tag, so `before_tag` captures
Expand All @@ -1249,6 +1264,7 @@ fn repeated_decode_arm(
if tag.wire_type() == ::buffa::encoding::WireType::LengthDelimited {
// Packed: extract payload, decode elements via local cursor.
let payload = ::buffa::types::borrow_bytes(&mut cur)?;
#reserve_stmt
let mut pcur: &[u8] = payload;
while !pcur.is_empty() { #packed_elem }
} else if tag.wire_type() == #elem_wire_type {
Expand Down
6 changes: 6 additions & 0 deletions buffa/src/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,12 @@ impl<'a, T> RepeatedView<'a, T> {
self.elements.push(elem);
}

/// Reserve capacity for at least `additional` more elements.
#[doc(hidden)]
pub fn reserve(&mut self, additional: usize) {
self.elements.reserve(additional);
}

/// Returns an iterator over the elements.
pub fn iter(&self) -> core::slice::Iter<'_, T> {
self.elements.iter()
Expand Down