Coverage Report

Created: 2026-11-03 08:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/revision-0.30.0/src/optimised/envelope.rs
Line
Count
Source
1
//! Tagged-value envelope encoders and decoders.
2
//!
3
//! These helpers are the runtime side of `optimised` codegen. The
4
//! macro emits calls into them; user code reaches them only indirectly.
5
6
use std::io::{Read, Write};
7
8
use crate::Error;
9
use crate::optimised::tag::{SizeClass, Tag, read_tag, write_tag};
10
use crate::slice_reader::{BorrowedReader, advance_read};
11
12
/// Encode an inline value: just the tag byte, no payload.
13
#[doc(hidden)]
14
#[inline]
15
0
pub fn encode_inline<W: Write>(w: &mut W, variant_id: u8) -> Result<(), Error> {
16
0
  write_tag(w, Tag::new(variant_id, SizeClass::Inline))
17
0
}
18
19
/// Encode a fixed-size value: tag byte plus the writer-supplied static-size payload.
20
///
21
/// The macro emits a `debug_assert_eq!(__bytes_written, expected_size)` after the
22
/// payload write so encoding mismatches surface in debug builds.
23
#[doc(hidden)]
24
#[inline]
25
0
pub fn encode_fixed<W: Write, F>(w: &mut W, variant_id: u8, body: F) -> Result<(), Error>
26
0
where
27
0
  F: FnOnce(&mut W) -> Result<(), Error>,
28
{
29
0
  write_tag(w, Tag::new(variant_id, SizeClass::Fixed))?;
30
0
  body(w)
31
0
}
32
33
/// Encode a varlen value: tag byte, then `u32_le byte_length`, then payload.
34
///
35
/// Implementation note: the body is written into a scratch `Vec<u8>` first so the
36
/// length is known before the prefix is emitted. This trades one allocation per
37
/// varlen value for a simple, single-pass macro expansion (no measure-then-write
38
/// dance on the writer side, no in-place patching of the length prefix).
39
#[doc(hidden)]
40
0
pub fn encode_varlen<W: Write, F>(w: &mut W, variant_id: u8, body: F) -> Result<(), Error>
41
0
where
42
0
  F: FnOnce(&mut Vec<u8>) -> Result<(), Error>,
43
{
44
0
  write_tag(w, Tag::new(variant_id, SizeClass::Varlen))?;
45
0
  let mut scratch: Vec<u8> = Vec::new();
46
0
  body(&mut scratch)?;
47
0
  let len: u32 = scratch
48
0
    .len()
49
0
    .try_into()
50
0
    .map_err(|_| Error::Serialize("optimised varlen payload exceeds u32::MAX bytes".into()))?;
51
0
  w.write_all(&len.to_le_bytes()).map_err(Error::Io)?;
52
0
  w.write_all(&scratch).map_err(Error::Io)
53
0
}
54
55
/// Read just the tag of an optimised value, validating the size class.
56
#[doc(hidden)]
57
#[inline]
58
0
pub fn read_optimised_tag<R: Read>(r: &mut R) -> Result<(Tag, SizeClass), Error> {
59
0
  let tag = read_tag(r)?;
60
0
  let sc = tag.size_class()?;
61
0
  Ok((tag, sc))
62
0
}
63
64
/// Read the `u32_le` byte length of a varlen value. The caller is expected to have
65
/// already consumed the tag byte.
66
#[doc(hidden)]
67
#[inline]
68
0
pub fn read_varlen_len<R: Read>(r: &mut R) -> Result<u32, Error> {
69
0
  let mut buf = [0u8; 4];
70
0
  r.read_exact(&mut buf).map_err(Error::Io)?;
71
0
  Ok(u32::from_le_bytes(buf))
72
0
}
73
74
/// Read a varlen value's payload as a borrowed slice.
75
///
76
/// Tag must have already been consumed. Returns the payload bytes and advances the
77
/// reader past them. The returned slice's lifetime is tied to the reader's input.
78
#[doc(hidden)]
79
#[inline]
80
0
pub fn read_varlen_slice<R: BorrowedReader>(r: &mut R) -> Result<&[u8], Error> {
81
0
  let len = read_varlen_len(r)? as usize;
82
0
  crate::slice_reader::read_borrowed_bytes(r, len)
83
0
}
84
85
/// Skip past a varlen value's payload (tag already consumed). Streaming-reader friendly.
86
#[doc(hidden)]
87
#[inline]
88
0
pub fn skip_varlen<R: Read>(r: &mut R) -> Result<(), Error> {
89
0
  let len = read_varlen_len(r)? as usize;
90
0
  advance_read(r, len)
91
0
}
92
93
/// Skip past a varlen value's payload using a [`BorrowedReader`]; cheaper than `skip_varlen`.
94
#[doc(hidden)]
95
#[inline]
96
0
pub fn skip_varlen_borrowed<R: BorrowedReader>(r: &mut R) -> Result<(), Error> {
97
0
  let len = read_varlen_len(r)? as usize;
98
0
  r.advance(len)
99
0
}
100
101
#[cfg(test)]
102
mod tests {
103
  use super::*;
104
  use crate::slice_reader::SliceReader;
105
106
  #[test]
107
  fn inline_round_trip() {
108
    let mut buf = Vec::new();
109
    encode_inline(&mut buf, 3).unwrap();
110
    assert_eq!(buf.len(), 1);
111
    let mut r: &[u8] = &buf;
112
    let (tag, sc) = read_optimised_tag(&mut r).unwrap();
113
    assert_eq!(tag.variant_id(), 3);
114
    assert_eq!(sc, SizeClass::Inline);
115
    assert_eq!(r.len(), 0);
116
  }
117
118
  #[test]
119
  fn fixed_round_trip() {
120
    let mut buf = Vec::new();
121
    encode_fixed(&mut buf, 7, |w| {
122
      w.write_all(&42i64.to_le_bytes()).map_err(Error::Io)?;
123
      Ok(())
124
    })
125
    .unwrap();
126
    assert_eq!(buf.len(), 1 + 8);
127
    let mut r: &[u8] = &buf;
128
    let (tag, sc) = read_optimised_tag(&mut r).unwrap();
129
    assert_eq!(tag.variant_id(), 7);
130
    assert_eq!(sc, SizeClass::Fixed);
131
    let mut payload = [0u8; 8];
132
    r.read_exact(&mut payload).unwrap();
133
    assert_eq!(i64::from_le_bytes(payload), 42);
134
  }
135
136
  #[test]
137
  fn varlen_round_trip() {
138
    let mut buf = Vec::new();
139
    encode_varlen(&mut buf, 12, |scratch| {
140
      scratch.extend_from_slice(b"hello world");
141
      Ok(())
142
    })
143
    .unwrap();
144
    assert_eq!(buf.len(), 1 + 4 + 11);
145
    let mut r = SliceReader::new(&buf);
146
    let (tag, sc) = read_optimised_tag(&mut r).unwrap();
147
    assert_eq!(tag.variant_id(), 12);
148
    assert_eq!(sc, SizeClass::Varlen);
149
    let payload = read_varlen_slice(&mut r).unwrap();
150
    assert_eq!(payload, b"hello world");
151
  }
152
153
  #[test]
154
  fn skip_varlen_advances_full_length() {
155
    let mut buf = Vec::new();
156
    encode_varlen(&mut buf, 1, |s| {
157
      s.extend_from_slice(&[0xAA; 100]);
158
      Ok(())
159
    })
160
    .unwrap();
161
    let mut r: &[u8] = &buf;
162
    let (_, sc) = read_optimised_tag(&mut r).unwrap();
163
    assert_eq!(sc, SizeClass::Varlen);
164
    skip_varlen(&mut r).unwrap();
165
    assert_eq!(r.len(), 0);
166
  }
167
168
  #[test]
169
  fn skip_varlen_borrowed_matches_skip_varlen() {
170
    let mut buf = Vec::new();
171
    encode_varlen(&mut buf, 1, |s| {
172
      s.extend_from_slice(&[0xAA; 100]);
173
      Ok(())
174
    })
175
    .unwrap();
176
    let mut r = SliceReader::new(&buf);
177
    let (_, _) = read_optimised_tag(&mut r).unwrap();
178
    skip_varlen_borrowed(&mut r).unwrap();
179
    assert!(r.remaining().is_empty());
180
  }
181
182
  #[test]
183
  fn read_optimised_tag_errors_on_reserved_size_class() {
184
    // Hand-craft a tag with size_class = 0b11.
185
    let bad = 0b0110_0000;
186
    let mut r: &[u8] = &[bad];
187
    let err = read_optimised_tag(&mut r).unwrap_err();
188
    assert!(matches!(err, Error::InvalidOptimisedTag(b) if b == bad));
189
  }
190
191
  #[test]
192
  fn read_varlen_slice_overrun_errors() {
193
    // Tag + length = 100 but only 4 bytes of payload follow.
194
    let mut buf = Vec::new();
195
    buf.push(Tag::new(0, SizeClass::Varlen).0);
196
    buf.extend_from_slice(&100u32.to_le_bytes());
197
    buf.extend_from_slice(&[0u8; 4]);
198
    let mut r = SliceReader::new(&buf);
199
    let _ = read_optimised_tag(&mut r).unwrap();
200
    assert!(read_varlen_slice(&mut r).is_err());
201
  }
202
}