consensus_encoding: add ArrayRefEncoder
What changed, and why it matters
This commit adds a new helper type that lets the library encode a borrowed fixed-size byte array without copying it. It is a straightforward performance and API improvement with no security-relevant behavior change.
No security action required; review as normal code-quality/API change.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch introduces ArrayRefEncoder<’e, N>, an ExactSizeEncoder implementation that borrows a &[u8; N] and exposes it as a single encoding chunk. It mirrors the existing owned ArrayEncoder but avoids a copy. The change is additive, only re-exports the new type in lib.rs, and includes unit tests for populated and empty arrays.
Changed components
consensus_encoding/src/encode/encoders.rsconsensus_encoding/src/lib.rsInspect captured patch +62 / −2
diff --git a/consensus_encoding/src/encode/encoders.rs b/consensus_encoding/src/encode/encoders.rs
index 2197a812..e0dccec7 100644
--- a/consensus_encoding/src/encode/encoders.rs
+++ b/consensus_encoding/src/encode/encoders.rs
@@ -69,6 +69,41 @@ impl<const N: usize> ExactSizeEncoder for ArrayEncoder<N> {
fn len(&self) -> usize { self.arr.map_or(0, |a| a.len()) }
}
+/// An encoder for a reference to an array.
+///
+/// This encoder borrows the array instead of taking ownership, avoiding a copy
+/// when the array is already available by reference (e.g., as a struct field).
+pub struct ArrayRefEncoder<'e, const N: usize> {
+ arr: Option<&'e [u8; N]>,
+}
+
+impl<'e, const N: usize> ArrayRefEncoder<'e, N> {
+ /// Constructs an encoder which encodes the array reference with no length prefix.
+ pub const fn without_length_prefix(arr: &'e [u8; N]) -> Self {
+ Self { arr: Some(arr) }
+ }
+}
+
+impl<const N: usize> Encoder for ArrayRefEncoder<'_, N> {
+ #[inline]
+ fn current_chunk(&self) -> &[u8] {
+ self.arr.map(|x| &x[..]).unwrap_or_default()
+ }
+
+ #[inline]
+ fn advance(&mut self) -> bool {
+ self.arr = None;
+ false
+ }
+}
+
+impl<const N: usize> ExactSizeEncoder for ArrayRefEncoder<'_, N> {
+ #[inline]
+ fn len(&self) -> usize {
+ self.arr.map_or(0, |a| a.len())
+ }
+}
+
/// An encoder for a list of encodable types.
pub struct SliceEncoder<'e, T: Encodable> {
/// The list of references to the objects we are encoding.
@@ -398,6 +433,31 @@ mod tests {
assert!(encoder.current_chunk().is_empty());
}
+ #[test]
+ fn encode_array_ref_with_data() {
+ // Should have one chunk with the array data, then exhausted.
+ let data = [1u8, 2, 3, 4];
+ let mut encoder = ArrayRefEncoder::without_length_prefix(&data);
+ assert_eq!(encoder.len(), 4);
+ assert!(!encoder.is_empty());
+ assert_eq!(encoder.current_chunk(), &[1u8, 2, 3, 4][..]);
+ assert!(!encoder.advance());
+ assert!(encoder.current_chunk().is_empty());
+ assert_eq!(encoder.len(), 0);
+ }
+
+ #[test]
+ fn encode_empty_array_ref() {
+ // Empty array should have one empty chunk, then exhausted.
+ let data = [];
+ let mut encoder = ArrayRefEncoder::without_length_prefix(&data);
+ assert_eq!(encoder.len(), 0);
+ assert!(encoder.is_empty());
+ assert!(encoder.current_chunk().is_empty());
+ assert!(!encoder.advance());
+ assert!(encoder.current_chunk().is_empty());
+ }
+
#[test]
fn encode_byte_slice_without_prefix() {
// Should have one chunk with the byte data, then exhausted.
diff --git a/consensus_encoding/src/lib.rs b/consensus_encoding/src/lib.rs
index 78189a58..0212e317 100644
--- a/consensus_encoding/src/lib.rs
+++ b/consensus_encoding/src/lib.rs
@@ -34,8 +34,8 @@ pub use self::decode::{
};
pub use self::decode::{decode_from_slice, Decodable, Decoder};
pub use self::encode::encoders::{
- ArrayEncoder, BytesEncoder, CompactSizeEncoder, Encoder2, Encoder3, Encoder4, Encoder6,
- SliceEncoder,
+ ArrayEncoder, ArrayRefEncoder, BytesEncoder, CompactSizeEncoder, Encoder2, Encoder3, Encoder4,
+ Encoder6, SliceEncoder,
};
#[cfg(feature = "alloc")]
pub use self::encode::{encode_to_vec, flush_to_vec};
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.