/rust/registry/src/index.crates.io-1949cf8c6b5b557f/revision-0.30.0/src/slice_reader.rs
Line | Count | Source |
1 | | //! Helpers for advancing through revisioned bytes without building values. |
2 | | //! |
3 | | //! Used by [`crate::SkipRevisioned`] implementations and [`crate::skip_slice`]. |
4 | | |
5 | | use std::io::{Read, Result as IoResult}; |
6 | | |
7 | | use crate::Error; |
8 | | |
9 | | /// Discards `len` bytes from `reader` using a fixed stack buffer (no large allocations). |
10 | | #[inline] |
11 | 0 | pub fn advance_read<R: Read + ?Sized>(reader: &mut R, mut len: usize) -> Result<(), Error> { |
12 | 0 | let mut buf = [0u8; 4096]; |
13 | 0 | while len > 0 { |
14 | 0 | let chunk = len.min(buf.len()); |
15 | 0 | reader.read_exact(&mut buf[..chunk]).map_err(Error::Io)?; |
16 | 0 | len -= chunk; |
17 | | } |
18 | 0 | Ok(()) |
19 | 0 | } Unexecuted instantiation: revision::slice_reader::advance_read::<revision::slice_reader::SliceReader> Unexecuted instantiation: revision::slice_reader::advance_read::<&[u8]> Unexecuted instantiation: revision::slice_reader::advance_read::<_> |
20 | | |
21 | | /// `Read` adapter over a byte slice, tracking how many bytes were consumed. |
22 | | /// |
23 | | /// This is optional; [`crate::skip_slice`] uses [`&[u8]`](std::slice) as a [`Read`] |
24 | | /// implementor instead. `SliceReader` is useful when you need the consumed length |
25 | | /// after partially reading with other APIs. |
26 | | #[derive(Clone, Copy, Debug)] |
27 | | pub struct SliceReader<'a> { |
28 | | inner: &'a [u8], |
29 | | original_len: usize, |
30 | | } |
31 | | |
32 | | impl<'a> SliceReader<'a> { |
33 | | #[inline] |
34 | 0 | pub fn new(slice: &'a [u8]) -> Self { |
35 | 0 | Self { |
36 | 0 | original_len: slice.len(), |
37 | 0 | inner: slice, |
38 | 0 | } |
39 | 0 | } |
40 | | |
41 | | /// Remaining unconsumed bytes. |
42 | | #[inline] |
43 | 0 | pub fn remaining(&self) -> &[u8] { |
44 | 0 | self.inner |
45 | 0 | } |
46 | | |
47 | | /// Number of bytes consumed since construction. |
48 | | #[inline] |
49 | 0 | pub fn consumed_len(&self) -> usize { |
50 | 0 | self.original_len - self.inner.len() |
51 | 0 | } |
52 | | |
53 | | /// Advance by `n` bytes without copying. |
54 | | #[inline] |
55 | 0 | pub fn consume(&mut self, n: usize) -> Result<(), Error> { |
56 | 0 | if n > self.inner.len() { |
57 | 0 | return Err(Error::Io(std::io::Error::new( |
58 | 0 | std::io::ErrorKind::UnexpectedEof, |
59 | 0 | "unexpected EOF while skipping", |
60 | 0 | ))); |
61 | 0 | } |
62 | 0 | self.inner = &self.inner[n..]; |
63 | 0 | Ok(()) |
64 | 0 | } Unexecuted instantiation: <revision::slice_reader::SliceReader>::consume Unexecuted instantiation: <revision::slice_reader::SliceReader>::consume |
65 | | |
66 | | /// Construct a new `SliceReader` over a sub-range of the original slice. |
67 | | /// |
68 | | /// `offset` is measured from the start of the slice this reader was originally |
69 | | /// constructed with — *not* the current cursor position. The cursor in the |
70 | | /// returned reader starts at the beginning of the sub-range. |
71 | | /// |
72 | | /// Used by optimised walkers to hand a child walker exactly one field's bytes |
73 | | /// without mutating the parent cursor. |
74 | | #[inline] |
75 | 0 | pub fn sub(&self, offset: usize, len: usize) -> Result<SliceReader<'a>, Error> { |
76 | | // `offset` is into the original slice; convert to an `inner`-relative offset. |
77 | 0 | let inner_offset = offset.checked_sub(self.consumed_len()).ok_or_else(|| { |
78 | 0 | Error::Io(std::io::Error::new( |
79 | 0 | std::io::ErrorKind::InvalidInput, |
80 | 0 | "SliceReader::sub: offset precedes current cursor", |
81 | 0 | )) |
82 | 0 | })?; |
83 | 0 | let end = inner_offset.checked_add(len).ok_or_else(|| { |
84 | 0 | Error::Io(std::io::Error::new( |
85 | 0 | std::io::ErrorKind::InvalidInput, |
86 | 0 | "SliceReader::sub: offset + len overflow", |
87 | 0 | )) |
88 | 0 | })?; |
89 | 0 | if end > self.inner.len() { |
90 | 0 | return Err(Error::OptimisedSubReaderOverrun); |
91 | 0 | } |
92 | 0 | Ok(SliceReader::new(&self.inner[inner_offset..end])) |
93 | 0 | } |
94 | | } |
95 | | |
96 | | impl Read for SliceReader<'_> { |
97 | | #[inline] |
98 | 0 | fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { |
99 | 0 | let n = buf.len().min(self.inner.len()); |
100 | 0 | buf[..n].copy_from_slice(&self.inner[..n]); |
101 | 0 | self.inner = &self.inner[n..]; |
102 | 0 | Ok(n) |
103 | 0 | } Unexecuted instantiation: <revision::slice_reader::SliceReader as std::io::Read>::read Unexecuted instantiation: <revision::slice_reader::SliceReader as std::io::Read>::read |
104 | | } |
105 | | |
106 | | /// A [`Read`] that can hand out borrowed slices of upcoming bytes. |
107 | | /// |
108 | | /// Implemented by `&[u8]` and [`SliceReader`]; used by walker methods |
109 | | /// (`LeafWalker::with_bytes`, `MapEntry::with_key_bytes`, etc.) to peek at |
110 | | /// length-prefixed payloads without materialising them into owned values. |
111 | | /// |
112 | | /// `Read` itself cannot be peeked — it might be a streaming source like |
113 | | /// `File` or a network socket whose bytes don't sit in an addressable |
114 | | /// buffer. `BorrowedReader` is the explicit "I am slice-backed" contract; |
115 | | /// methods that want zero-copy access opt into it via a trait bound. |
116 | | /// |
117 | | /// # Safety |
118 | | /// |
119 | | /// This trait is `unsafe` to implement because the crate's unsafe code |
120 | | /// (in particular [`crate::optimised::envelope::read_varlen_slice`], the |
121 | | /// `walk_<field>` Wire-fast-path in macro-generated walkers, and the |
122 | | /// optimised walker constructors more broadly) relies on a stronger |
123 | | /// contract than could be expressed in safe Rust: |
124 | | /// |
125 | | /// 1. **`peek_bytes(n)` and [`remaining`](Self::remaining) must return |
126 | | /// slices that point into stable, addressable memory** — not a |
127 | | /// transient buffer that could be moved, reallocated, or overwritten |
128 | | /// by a subsequent call. |
129 | | /// |
130 | | /// 2. **`advance(n)` must only move the cursor; it must not invalidate, |
131 | | /// move, or mutate the bytes already returned by `peek_bytes` or |
132 | | /// `remaining`.** In particular, an impl that holds bytes in an |
133 | | /// internal `Vec<u8>` and refills the `Vec` on `advance` (e.g. a |
134 | | /// buffered file reader) does **not** satisfy this contract, because |
135 | | /// previously-returned slice pointers would dangle after the refill. |
136 | | /// |
137 | | /// 3. **Bytes returned by `peek_bytes` or `remaining` must remain valid |
138 | | /// for the reader's lifetime** (i.e. for `'r` where the reader is |
139 | | /// borrowed as `&'r mut R`), regardless of how many `advance` calls |
140 | | /// happen in between, so the unsafe code extending peek lifetimes via |
141 | | /// [`read_borrowed_bytes`] and the macro-emitted `walk_<field>` Wire |
142 | | /// fast path do not create dangling pointers. |
143 | | /// |
144 | | /// 4. **`remaining().len()` is monotonically non-increasing under |
145 | | /// `advance`** — for any successful `advance` call, |
146 | | /// `remaining_after.len() <= remaining_before.len()` must hold. |
147 | | /// The macro-emitted Wire fast path computes the consumed byte |
148 | | /// count as `remaining_before.len() - remaining_after.len()` and |
149 | | /// relies on the result being non-negative; an impl that ever |
150 | | /// grows `remaining` across an `advance` would underflow this |
151 | | /// subtraction. (The macro guards against underflow with a |
152 | | /// `checked_sub` that returns a corrupt-impl error rather than |
153 | | /// triggering UB, but in-crate impls and any reasonable downstream |
154 | | /// impl must honour this invariant.) The bullet deliberately does |
155 | | /// *not* require that `advance(n)` reduces `remaining` by exactly |
156 | | /// `n` — an impl that coalesces, buffers, or otherwise chooses a |
157 | | /// larger reduction is still sound for the unsafe code that |
158 | | /// depends on this contract. |
159 | | /// |
160 | | /// *Semantic caveat:* the UB-safety promise above is **only** about |
161 | | /// memory safety. The macro-emitted `walk_<field>` Wire fast path |
162 | | /// additionally interprets `remaining_before.len() - |
163 | | /// remaining_after.len()` as "the number of bytes the |
164 | | /// `skip_indexed_*` call consumed", and hands those bytes to |
165 | | /// `IndexedMapView` / `IndexedSeqView` / `IndexedSetView` for |
166 | | /// parsing. An impl that reduces `remaining` by *more* than what |
167 | | /// `skip_*` actually visited (e.g. by coalescing reads or |
168 | | /// pre-fetching the next field) would still be UB-safe, but the |
169 | | /// view would see trailing bytes the skip didn't consume and |
170 | | /// misparse the field. The two in-crate impls (`&[u8]`, |
171 | | /// `SliceReader<'a>`) advance by exactly `n`, so this is dormant |
172 | | /// in practice — but a downstream impl that wants to be both |
173 | | /// UB-safe **and** semantically correct against the macro should |
174 | | /// advance by exactly `n` as well. |
175 | | /// |
176 | | /// The two impls in this crate — `&[u8]` and [`SliceReader`] — both |
177 | | /// trivially satisfy this contract: `peek_bytes` and `remaining` return |
178 | | /// slices into a caller-owned buffer that the reader never mutates, and |
179 | | /// `advance` is a pure cursor bump that only ever reduces `remaining`. |
180 | | /// |
181 | | /// Violating any of these is **undefined behaviour**, not just a logic |
182 | | /// bug; the crate's unsafe code is correct only under this contract. |
183 | | pub unsafe trait BorrowedReader: Read { |
184 | | /// Borrow the next `n` bytes without advancing the cursor. |
185 | | /// |
186 | | /// Returns the slice on success. The slice must point into stable |
187 | | /// memory that survives subsequent `advance` calls — see the trait's |
188 | | /// safety contract. |
189 | | fn peek_bytes(&self, n: usize) -> Result<&[u8], Error>; |
190 | | |
191 | | /// Advance the cursor past `n` bytes without copying them. |
192 | | /// |
193 | | /// Equivalent to reading `n` bytes and discarding them, but cheaper |
194 | | /// (the bytes are never touched). Returns an error if fewer than |
195 | | /// `n` bytes remain. |
196 | | /// |
197 | | /// **Safety**: must not invalidate or move bytes returned by previous |
198 | | /// `peek_bytes` calls — see the trait's safety contract. |
199 | | fn advance(&mut self, n: usize) -> Result<(), Error>; |
200 | | |
201 | | /// Bytes consumed since the reader was constructed. |
202 | | /// |
203 | | /// Used by optimised walkers and the encode side to compute offsets |
204 | | /// relative to the start of an optimised compound's payload. |
205 | | fn position(&self) -> usize; |
206 | | |
207 | | /// The unconsumed tail as a borrowed slice. |
208 | | /// |
209 | | /// Returns a view of the bytes the reader has yet to read, tied to the |
210 | | /// reader's borrow. Unlike [`peek_bytes`](Self::peek_bytes), the caller |
211 | | /// does not have to know the length in advance — and unlike `position`, |
212 | | /// the returned slice is concrete bytes, not a count. |
213 | | /// |
214 | | /// **Use case**: capture before/after slices around a `skip`-style call |
215 | | /// to recover the field's exact wire bytes without decoding them: |
216 | | /// |
217 | | /// ```ignore |
218 | | /// let before = reader.remaining(); // &[u8] of all unread bytes |
219 | | /// some_skip_routine(&mut reader)?; // advances past one logical value |
220 | | /// let after = reader.remaining(); |
221 | | /// let consumed_bytes = &before[..before.len() - after.len()]; |
222 | | /// // `consumed_bytes` is the just-skipped value's wire bytes, zero-copy. |
223 | | /// ``` |
224 | | /// |
225 | | /// Any impl that is used as the source for an optimised-walker's |
226 | | /// `walk_<field>` accessor **must** override this. The default |
227 | | /// implementation `debug_assert!`s on call (panicking in debug builds |
228 | | /// to surface the missing override) and returns `&[]` in release |
229 | | /// (which would produce silently-empty field views — equally bad, but |
230 | | /// at least not crashing). Both in-crate impls (`&[u8]` and |
231 | | /// `SliceReader<'a>`) override; any new downstream `BorrowedReader` |
232 | | /// impl should too, even if it only uses the legacy walker paths |
233 | | /// (which don't consult `remaining`) — the override is cheap and the |
234 | | /// foot-gun cost is much higher than the cost of writing it. |
235 | | #[inline] |
236 | 0 | fn remaining(&self) -> &[u8] { |
237 | 0 | debug_assert!( |
238 | 0 | false, |
239 | | "BorrowedReader::remaining() default impl invoked — your impl must \ |
240 | | override it (returns &[]; the macro-emitted walk_<field> Wire fast \ |
241 | | path will produce silently empty field views otherwise)" |
242 | | ); |
243 | 0 | &[] |
244 | 0 | } |
245 | | } |
246 | | |
247 | | // SAFETY: `&[u8]::peek_bytes` returns a slice into the caller-owned buffer the |
248 | | // reference points at; `advance` only updates the slice reference (cursor), |
249 | | // never touching the underlying bytes. Both invariants hold for any caller- |
250 | | // provided buffer. |
251 | | unsafe impl BorrowedReader for &[u8] { |
252 | | #[inline] |
253 | 3 | fn peek_bytes(&self, n: usize) -> Result<&[u8], Error> { |
254 | 3 | self.get(..n).ok_or_else(|| { |
255 | 0 | Error::Io(std::io::Error::new( |
256 | 0 | std::io::ErrorKind::UnexpectedEof, |
257 | 0 | "unexpected EOF while peeking borrowed bytes", |
258 | 0 | )) |
259 | 0 | }) Unexecuted instantiation: <&[u8] as revision::slice_reader::BorrowedReader>::peek_bytes::{closure#0}Unexecuted instantiation: <&[u8] as revision::slice_reader::BorrowedReader>::peek_bytes::{closure#0} |
260 | 3 | } <&[u8] as revision::slice_reader::BorrowedReader>::peek_bytes Line | Count | Source | 253 | 3 | fn peek_bytes(&self, n: usize) -> Result<&[u8], Error> { | 254 | 3 | self.get(..n).ok_or_else(|| { | 255 | | Error::Io(std::io::Error::new( | 256 | | std::io::ErrorKind::UnexpectedEof, | 257 | | "unexpected EOF while peeking borrowed bytes", | 258 | | )) | 259 | | }) | 260 | 3 | } |
Unexecuted instantiation: <&[u8] as revision::slice_reader::BorrowedReader>::peek_bytes |
261 | | |
262 | | #[inline] |
263 | 3 | fn advance(&mut self, n: usize) -> Result<(), Error> { |
264 | 3 | if n > self.len() { |
265 | 0 | return Err(Error::Io(std::io::Error::new( |
266 | 0 | std::io::ErrorKind::UnexpectedEof, |
267 | 0 | "unexpected EOF while advancing slice reader", |
268 | 0 | ))); |
269 | 3 | } |
270 | 3 | *self = &self[n..]; |
271 | 3 | Ok(()) |
272 | 3 | } <&[u8] as revision::slice_reader::BorrowedReader>::advance Line | Count | Source | 263 | 3 | fn advance(&mut self, n: usize) -> Result<(), Error> { | 264 | 3 | if n > self.len() { | 265 | 0 | return Err(Error::Io(std::io::Error::new( | 266 | 0 | std::io::ErrorKind::UnexpectedEof, | 267 | 0 | "unexpected EOF while advancing slice reader", | 268 | 0 | ))); | 269 | 3 | } | 270 | 3 | *self = &self[n..]; | 271 | 3 | Ok(()) | 272 | 3 | } |
Unexecuted instantiation: <&[u8] as revision::slice_reader::BorrowedReader>::advance |
273 | | |
274 | | #[inline] |
275 | 0 | fn position(&self) -> usize { |
276 | | // `&[u8]` has no original-length tracking, so we cannot report a meaningful |
277 | | // absolute position. Callers that need positions should use `SliceReader`. |
278 | 0 | 0 |
279 | 0 | } |
280 | | |
281 | | #[inline] |
282 | 0 | fn remaining(&self) -> &[u8] { |
283 | | // `*self` is `&[u8]` — the entire unread tail by definition (advancing |
284 | | // replaces the slice with its suffix). |
285 | 0 | self |
286 | 0 | } Unexecuted instantiation: <&[u8] as revision::slice_reader::BorrowedReader>::remaining Unexecuted instantiation: <&[u8] as revision::slice_reader::BorrowedReader>::remaining |
287 | | } |
288 | | |
289 | | // SAFETY: Forwarding impl. Every method delegates to the underlying `R`, |
290 | | // which is itself `BorrowedReader` and therefore obeys the trait's safety |
291 | | // contract (stable backing buffer, non-mutating `peek_bytes` / `remaining`, |
292 | | // monotonic non-increasing `remaining().len()` under `advance`). The |
293 | | // forwarding adds no state and cannot violate any invariant the inner impl |
294 | | // upholds. |
295 | | // |
296 | | // This impl is what lets the macro-emitted `walk_<field>` / `skip_<field>` |
297 | | // paths call `skip_indexed_*` with a `reader: &mut &'r mut R` binding |
298 | | // (extracted from the `Wire` repr variant) without an explicit reborrow. |
299 | | unsafe impl<R: BorrowedReader + ?Sized> BorrowedReader for &mut R { |
300 | | #[inline] |
301 | 3 | fn peek_bytes(&self, n: usize) -> Result<&[u8], Error> { |
302 | 3 | (**self).peek_bytes(n) |
303 | 3 | } <&mut &[u8] as revision::slice_reader::BorrowedReader>::peek_bytes Line | Count | Source | 301 | 3 | fn peek_bytes(&self, n: usize) -> Result<&[u8], Error> { | 302 | 3 | (**self).peek_bytes(n) | 303 | 3 | } |
Unexecuted instantiation: <&mut _ as revision::slice_reader::BorrowedReader>::peek_bytes |
304 | | |
305 | | #[inline] |
306 | 0 | fn advance(&mut self, n: usize) -> Result<(), Error> { |
307 | 0 | (**self).advance(n) |
308 | 0 | } |
309 | | |
310 | | #[inline] |
311 | 0 | fn position(&self) -> usize { |
312 | 0 | (**self).position() |
313 | 0 | } |
314 | | |
315 | | #[inline] |
316 | 0 | fn remaining(&self) -> &[u8] { |
317 | 0 | (**self).remaining() |
318 | 0 | } |
319 | | } |
320 | | |
321 | | // SAFETY: `SliceReader<'a>` borrows an external `&'a [u8]` it never modifies. |
322 | | // `peek_bytes` returns slices into that external buffer; `advance` only updates |
323 | | // the internal cursor (`inner`), never the buffer. Both invariants hold. |
324 | | unsafe impl<'a> BorrowedReader for SliceReader<'a> { |
325 | | #[inline] |
326 | 0 | fn peek_bytes(&self, n: usize) -> Result<&[u8], Error> { |
327 | 0 | self.inner.get(..n).ok_or_else(|| { |
328 | 0 | Error::Io(std::io::Error::new( |
329 | 0 | std::io::ErrorKind::UnexpectedEof, |
330 | 0 | "unexpected EOF while peeking borrowed bytes", |
331 | 0 | )) |
332 | 0 | }) |
333 | 0 | } |
334 | | |
335 | | #[inline] |
336 | 0 | fn advance(&mut self, n: usize) -> Result<(), Error> { |
337 | 0 | self.consume(n) |
338 | 0 | } |
339 | | |
340 | | #[inline] |
341 | 0 | fn position(&self) -> usize { |
342 | 0 | self.consumed_len() |
343 | 0 | } |
344 | | |
345 | | #[inline] |
346 | 0 | fn remaining(&self) -> &[u8] { |
347 | 0 | self.inner |
348 | 0 | } |
349 | | } |
350 | | |
351 | | /// Peek `n` bytes from `reader`, advance past them, and return the peeked |
352 | | /// slice with the reader's full `'r` lifetime. |
353 | | /// |
354 | | /// This is the canonical "borrow body bytes from a slice-backed reader" helper |
355 | | /// used by the optimised wire format's runtime and macro-emitted walkers. It |
356 | | /// replaces 4 copies of the same `peek_bytes + advance + slice::from_raw_parts` |
357 | | /// dance and is the single audit point for the unsafe lifetime extension. |
358 | | /// |
359 | | /// The unsafe block is sound because [`BorrowedReader`] is itself an `unsafe |
360 | | /// trait`: every conforming impl guarantees that the bytes returned by |
361 | | /// `peek_bytes` remain valid for the reader's lifetime regardless of how many |
362 | | /// `advance` calls happen in between. See the [`BorrowedReader`] safety |
363 | | /// contract for the full requirements. |
364 | | #[inline] |
365 | 3 | pub fn read_borrowed_bytes<'r, R: BorrowedReader + ?Sized>( |
366 | 3 | reader: &'r mut R, |
367 | 3 | n: usize, |
368 | 3 | ) -> Result<&'r [u8], Error> { |
369 | 3 | let peeked = reader.peek_bytes(n)?; |
370 | 3 | let ptr = peeked.as_ptr(); |
371 | 3 | reader.advance(n)?; |
372 | | // SAFETY: `peek_bytes(n)` returned a slice of length `n` from `reader`'s |
373 | | // underlying buffer. By the `unsafe trait BorrowedReader` contract, |
374 | | // `advance(n)` only moves the cursor and must not invalidate or move the |
375 | | // peeked bytes. Therefore the slice [ptr, ptr+n) remains valid for the |
376 | | // reader's lifetime `'r`. Reconstructing the slice with the extended |
377 | | // lifetime is sound because nothing between here and `'r`'s end can move |
378 | | // or free the underlying buffer. |
379 | 3 | let slice: &'r [u8] = unsafe { std::slice::from_raw_parts(ptr, n) }; |
380 | 3 | Ok(slice) |
381 | 3 | } revision::slice_reader::read_borrowed_bytes::<&[u8]> Line | Count | Source | 365 | 3 | pub fn read_borrowed_bytes<'r, R: BorrowedReader + ?Sized>( | 366 | 3 | reader: &'r mut R, | 367 | 3 | n: usize, | 368 | 3 | ) -> Result<&'r [u8], Error> { | 369 | 3 | let peeked = reader.peek_bytes(n)?; | 370 | 3 | let ptr = peeked.as_ptr(); | 371 | 3 | reader.advance(n)?; | 372 | | // SAFETY: `peek_bytes(n)` returned a slice of length `n` from `reader`'s | 373 | | // underlying buffer. By the `unsafe trait BorrowedReader` contract, | 374 | | // `advance(n)` only moves the cursor and must not invalidate or move the | 375 | | // peeked bytes. Therefore the slice [ptr, ptr+n) remains valid for the | 376 | | // reader's lifetime `'r`. Reconstructing the slice with the extended | 377 | | // lifetime is sound because nothing between here and `'r`'s end can move | 378 | | // or free the underlying buffer. | 379 | 3 | let slice: &'r [u8] = unsafe { std::slice::from_raw_parts(ptr, n) }; | 380 | 3 | Ok(slice) | 381 | 3 | } |
Unexecuted instantiation: revision::slice_reader::read_borrowed_bytes::<_> |
382 | | |
383 | | /// Borrow `n` bytes and advance past them in one step. |
384 | | /// |
385 | | /// `BorrowedReader::take_bytes` would be the natural place for this, but expressing |
386 | | /// "return a borrow whose lifetime survives the mutating `advance` call" as a trait |
387 | | /// default fights the borrow checker. Per-impl free functions sidestep the issue. |
388 | | #[inline] |
389 | 0 | pub fn take_bytes_slice<'a>(reader: &mut &'a [u8], n: usize) -> Result<&'a [u8], Error> { |
390 | 0 | if n > reader.len() { |
391 | 0 | return Err(Error::Io(std::io::Error::new( |
392 | 0 | std::io::ErrorKind::UnexpectedEof, |
393 | 0 | "unexpected EOF while taking borrowed bytes", |
394 | 0 | ))); |
395 | 0 | } |
396 | 0 | let (head, tail) = reader.split_at(n); |
397 | 0 | *reader = tail; |
398 | 0 | Ok(head) |
399 | 0 | } |
400 | | |
401 | | /// `take_bytes` for `SliceReader`. See [`take_bytes_slice`] for rationale. |
402 | | #[inline] |
403 | 0 | pub fn take_bytes_reader<'r, 'a: 'r>( |
404 | 0 | reader: &'r mut SliceReader<'a>, |
405 | 0 | n: usize, |
406 | 0 | ) -> Result<&'a [u8], Error> { |
407 | 0 | if n > reader.inner.len() { |
408 | 0 | return Err(Error::Io(std::io::Error::new( |
409 | 0 | std::io::ErrorKind::UnexpectedEof, |
410 | 0 | "unexpected EOF while taking borrowed bytes", |
411 | 0 | ))); |
412 | 0 | } |
413 | 0 | let (head, tail) = reader.inner.split_at(n); |
414 | 0 | reader.inner = tail; |
415 | 0 | Ok(head) |
416 | 0 | } |
417 | | |
418 | | #[cfg(test)] |
419 | | mod tests { |
420 | | use super::*; |
421 | | |
422 | | #[test] |
423 | | fn slice_reader_position_tracks_consumed() { |
424 | | let data = [0u8, 1, 2, 3, 4]; |
425 | | let mut r = SliceReader::new(&data); |
426 | | assert_eq!(r.position(), 0); |
427 | | r.consume(2).unwrap(); |
428 | | assert_eq!(r.position(), 2); |
429 | | r.consume(1).unwrap(); |
430 | | assert_eq!(r.position(), 3); |
431 | | } |
432 | | |
433 | | #[test] |
434 | | fn slice_reader_sub_carves_subrange() { |
435 | | let data = [0u8, 1, 2, 3, 4, 5]; |
436 | | let r = SliceReader::new(&data); |
437 | | let sub = r.sub(2, 3).unwrap(); |
438 | | assert_eq!(sub.remaining(), &[2, 3, 4]); |
439 | | } |
440 | | |
441 | | #[test] |
442 | | fn slice_reader_sub_rejects_overflow() { |
443 | | let data = [0u8, 1, 2, 3]; |
444 | | let r = SliceReader::new(&data); |
445 | | assert!(matches!(r.sub(2, 3), Err(Error::OptimisedSubReaderOverrun))); |
446 | | } |
447 | | |
448 | | #[test] |
449 | | fn slice_reader_sub_after_consume() { |
450 | | let data = [0u8, 1, 2, 3, 4, 5]; |
451 | | let mut r = SliceReader::new(&data); |
452 | | r.consume(2).unwrap(); |
453 | | // `offset` is absolute against the original slice. |
454 | | let sub = r.sub(3, 2).unwrap(); |
455 | | assert_eq!(sub.remaining(), &[3, 4]); |
456 | | } |
457 | | |
458 | | #[test] |
459 | | fn slice_reader_sub_rejects_offset_before_cursor() { |
460 | | let data = [0u8, 1, 2, 3]; |
461 | | let mut r = SliceReader::new(&data); |
462 | | r.consume(2).unwrap(); |
463 | | assert!(r.sub(1, 1).is_err()); |
464 | | } |
465 | | |
466 | | #[test] |
467 | | fn take_bytes_slice_advances_and_returns_borrow() { |
468 | | let data: &[u8] = &[1, 2, 3, 4]; |
469 | | let mut cursor = data; |
470 | | let taken = take_bytes_slice(&mut cursor, 2).unwrap(); |
471 | | assert_eq!(taken, &[1, 2]); |
472 | | assert_eq!(cursor, &[3, 4]); |
473 | | } |
474 | | |
475 | | #[test] |
476 | | fn take_bytes_reader_advances_and_returns_borrow() { |
477 | | let data = [1u8, 2, 3, 4]; |
478 | | let mut r = SliceReader::new(&data); |
479 | | let taken = take_bytes_reader(&mut r, 3).unwrap(); |
480 | | assert_eq!(taken, &[1, 2, 3]); |
481 | | assert_eq!(r.remaining(), &[4]); |
482 | | } |
483 | | |
484 | | /// The blanket impl `BorrowedReader for &mut R` is what lets the |
485 | | /// macro-emitted walker code call `skip_indexed_*<R: BorrowedReader>` |
486 | | /// with a `reader: &mut &'r mut R` binding. Exercising it through a |
487 | | /// generic helper confirms every method forwards correctly and that |
488 | | /// mutations on the `&mut R` reborrow are visible on the underlying |
489 | | /// reader. |
490 | | #[test] |
491 | | fn borrowed_reader_blanket_impl_forwards_to_underlying() { |
492 | | fn use_borrowed<R: BorrowedReader>(r: &mut R) -> (Vec<u8>, usize, usize, usize) { |
493 | | let peeked = r.peek_bytes(3).unwrap().to_vec(); |
494 | | let pos_before = r.position(); |
495 | | let remaining_before = r.remaining().len(); |
496 | | r.advance(3).unwrap(); |
497 | | (peeked, pos_before, remaining_before, r.remaining().len()) |
498 | | } |
499 | | |
500 | | let data = [10u8, 20, 30, 40, 50, 60]; |
501 | | let mut backing = SliceReader::new(&data); |
502 | | // Take a `&mut SliceReader`, then re-borrow as `&mut &mut SliceReader` |
503 | | // to exercise the blanket impl rather than the direct one. |
504 | | let mut reborrow: &mut SliceReader = &mut backing; |
505 | | let (peeked, pos_before, remaining_before, remaining_after) = use_borrowed(&mut reborrow); |
506 | | |
507 | | assert_eq!(peeked, &[10, 20, 30]); |
508 | | assert_eq!(pos_before, 0); |
509 | | assert_eq!(remaining_before, 6); |
510 | | assert_eq!(remaining_after, 3); |
511 | | // The advance through the blanket impl must be visible on the |
512 | | // original reader. |
513 | | assert_eq!(backing.position(), 3); |
514 | | assert_eq!(backing.remaining(), &[40, 50, 60]); |
515 | | } |
516 | | } |