Coverage Report

Created: 2026-08-14 08:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}