flatr_core/read.rs
1//! # flatr/core/src/read.rs
2//!
3//! The read-side of the flatr format: zero-copy access to serialized data.
4//!
5//! ## Design
6//!
7//! No data is copied or allocated during reading. All accessor methods return
8//! either a primitive value (scalars, bools) or a view struct that borrows
9//! directly from the input byte slice with lifetime `'a`.
10//!
11//! ## Key types
12//!
13//! | Type | Role |
14//! |------|------|
15//! | [`ReadAt`] | Trait: given `buf` and `offset`, decode a value of type `T` |
16//! | [`ListView`] | Zero-copy iterator + random-access view over an array field |
17//! | [`RawView`] | Low-level table accessor: holds `buf`, `t_pos`, `v_pos` |
18//! | [`HasRawView`] | Marker trait on generated `XxxView` types for `merge_table_list` |
19//!
20//! ## Slot vs absolute position
21//!
22//! The write side uses **slots** (distance from the *end* of the buffer) to
23//! identify objects. The read side uses **absolute byte positions** within
24//! the finished buffer slice. `RawView::from_slot` converts between the two.
25
26use std::marker::PhantomData;
27use crate::{BitMask, DataType};
28
29// ── ReadAt ────────────────────────────────────────────────────────────────────
30
31/// Decode a value of type `Self` from a byte buffer at a given absolute offset.
32///
33/// `MODE` determines how the parent table locates this field's data:
34/// - `Inline`: the bytes at `field_position` ARE the value.
35/// - `Offset`: the bytes at `field_position` are a u32 forward offset; the
36/// actual data starts at `field_position + forward_offset`.
37///
38/// The lifetime `'a` ties the decoded value to the input buffer so that
39/// zero-copy reads (string slices, list views) compile correctly.
40pub trait ReadAt<'a> {
41 /// Whether the parent table stores this value inline or via a forward offset.
42 const MODE: DataType;
43
44 /// The type returned by `read_at`. For scalars this is `Self`; for strings
45 /// it is `&'a str`; for arrays it is `ListView<'a, T>`.
46 type ReadOutput;
47
48 /// Decode a value from `buf` at absolute byte position `offset`.
49 fn read_at(buf: &'a [u8], offset: usize) -> Self::ReadOutput;
50
51 // Specialized read method for enums/unions
52 #[inline(always)]
53 fn read_with_tag_at(buf: &'a [u8], offset: usize, _tag:u8) -> Self::ReadOutput {
54 Self::read_at(buf, offset)
55 }
56
57 /// Return the zero/empty default for this type's `ReadOutput`.
58 ///
59 /// Called by [`ListView::get`] when the forward-offset entry for an element
60 /// is `0`, which signals that the element slot is absent. This only fires
61 /// for `Offset`- and `Union`-mode types; `Inline` types (scalars, `bool`,
62 /// structs) are always present in their list slot, so their `get` path
63 /// never reaches this method.
64 ///
65 /// The default implementation is `unreachable!()` so that `Inline`-mode
66 /// impls pay no code-size cost. Every `Offset`/`Union` impl must override
67 /// this with the appropriate zero/empty value:
68 ///
69 /// | Type | Default |
70 /// |------------------|----------------|
71 /// | `&str` / `String`| `""` |
72 /// | `Vec<T>` | empty `ListView`|
73 /// | `FileBlob<T>` | empty slice view|
74 /// | generated Tables | `View::default()`|
75 /// | generated Unions | `None` variant |
76 fn default_output() -> Self::ReadOutput;
77
78 /// Returns the exclusive end address of the value whose payload starts at
79 /// `pos`. Overridden by Table (→ view.block_end()), Struct (→ pos +
80 /// size_of), and String (→ pos + 4 + length). Default returns `pos`
81 /// (safe conservative fallback for scalars/unknown types).
82 #[inline(always)]
83 fn payload_block_end(_buf: &'a [u8], pos: usize) -> usize
84 where Self: Sized { pos + size_of::<Self>() }
85}
86
87// ── Scalars ───────────────────────────────────────────────────────────────────
88
89/// Generates `ReadAt` for all integer types.
90///
91/// Uses `read_unaligned` + `from_le` to safely read a possibly-unaligned
92/// little-endian integer from an arbitrary byte position. The unsafe block
93/// is sound because `buf.as_ptr().add(offset)` is valid as long as
94/// `offset + size_of::<T>() <= buf.len()`, which callers guarantee.
95macro_rules! impl_read_scalar {
96 ($($t:ty),*) => {$(
97 impl<'a> ReadAt<'a> for $t {
98 const MODE: DataType = DataType::Inline;
99 type ReadOutput = Self;
100 #[inline(always)]
101 fn read_at(buf: &[u8], offset: usize) -> Self {
102 unsafe {
103 <$t>::from_le(
104 (buf.as_ptr().add(offset) as *const $t).read_unaligned()
105 )
106 }
107 }
108
109 #[inline(always)]
110 fn default_output() -> Self {
111 0
112 }
113 }
114 )*};
115}
116
117impl_read_scalar!(u8, u16, u32, u64, i8, i16, i32, i64, u128, i128);
118
119impl<'a> ReadAt<'a> for f32 {
120 const MODE: DataType = DataType::Inline;
121 type ReadOutput = Self;
122 /// Reads the bit pattern as u32 then reinterprets as f32.
123 #[inline(always)]
124 fn read_at(buf: &[u8], offset: usize) -> Self {
125 Self::from_bits(u32::read_at(buf, offset))
126 }
127 #[inline(always)]
128 fn default_output() -> Self {
129 0.
130 }
131}
132
133impl<'a> ReadAt<'a> for f64 {
134 const MODE: DataType = DataType::Inline;
135 type ReadOutput = Self;
136 #[inline(always)]
137 fn read_at(buf: &[u8], offset: usize) -> Self {
138 Self::from_bits(u64::read_at(buf, offset))
139 }
140 #[inline(always)]
141 fn default_output() -> Self {
142 0.
143 }
144}
145
146impl<'a> ReadAt<'a> for bool {
147 const MODE: DataType = DataType::Inline;
148 type ReadOutput = bool;
149 #[inline(always)]
150 fn read_at(buf: &[u8], offset: usize) -> bool {
151 u8::read_at(buf, offset) != 0
152 }
153 #[inline(always)]
154 fn default_output() -> Self {
155 false
156 }
157}
158
159// ── Strings ───────────────────────────────────────────────────────────────────
160
161/// String layout: `[u32 length][UTF-8 bytes...]`
162///
163/// `read_at` is called with the absolute position of the length prefix.
164/// Returns a `&'a str` that borrows directly from the input buffer — no copy.
165///
166/// # Safety
167/// The bytes are assumed to be valid UTF-8 (enforced at write time by
168/// `str::as_bytes`). Using `from_utf8_unchecked` avoids a redundant
169/// validation scan on every read.
170impl<'a> ReadAt<'a> for &str {
171 const MODE: DataType = DataType::Offset;
172 type ReadOutput = &'a str;
173 #[inline]
174 fn read_at(buf: &'a [u8], offset: usize) -> &'a str {
175 let len = u32::read_at(buf, offset) as usize;
176 unsafe {
177 let bytes = std::slice::from_raw_parts(
178 buf.as_ptr().add(offset + 4), len
179 );
180 std::str::from_utf8_unchecked(bytes)
181 }
182 }
183
184 #[inline(always)]
185 fn default_output() -> &'a str { "" }
186
187 #[inline(always)]
188 fn payload_block_end(buf: &'a [u8], pos: usize) -> usize {
189 pos + 4 + u32::read_at(buf, pos) as usize
190 }
191}
192
193/// Identical to `&str`; exists so that `Vec<String>` fields use the same
194/// read path as `Vec<&str>` fields. Both return `&'a str`.
195impl<'a> ReadAt<'a> for String {
196 const MODE: DataType = DataType::Offset;
197 type ReadOutput = &'a str;
198 #[inline]
199 fn read_at(buf: &'a [u8], offset: usize) -> &'a str {
200 let len = u32::read_at(buf, offset) as usize;
201 unsafe {
202 let bytes = std::slice::from_raw_parts(
203 buf.as_ptr().add(offset + 4), len
204 );
205 std::str::from_utf8_unchecked(bytes)
206 }
207 }
208
209 #[inline(always)]
210 fn default_output() -> &'a str { "" }
211
212 #[inline(always)]
213 fn payload_block_end(buf: &'a [u8], pos: usize) -> usize {
214 pos + 4 + u32::read_at(buf, pos) as usize
215 }
216}
217
218// ── Vec<T> ────────────────────────────────────────────────────────────────────
219
220/// Reads an array whose length is stored as a u32 length prefix immediately
221/// before the element data (or offset table for indirect elements).
222///
223/// Returns a [`ListView`] that borrows from `buf`. The ListView's `offset`
224/// points to the first element (or first offset-table entry) — i.e. 4 bytes
225/// past the length prefix.
226impl<'a, T: ReadAt<'a>> ReadAt<'a> for Vec<T> {
227 const MODE: DataType = DataType::Offset;
228 type ReadOutput = ListView<'a, T>;
229
230 #[inline(always)]
231 fn read_at(buf: &'a [u8], offset: usize) -> ListView<'a, T> {
232 ListView::new(buf, offset + 4, u32::read_at(buf, offset) as usize)
233 }
234
235 #[inline(always)]
236 fn default_output() -> ListView<'a, T> { ListView::default() }
237}
238
239/// Reads a fixed-size array `[T; N]` without a length prefix.
240/// Used for const-size inline buffers where `N` is known at compile time.
241impl<'a, T: ReadAt<'a>, const N: usize> ReadAt<'a> for [T; N] {
242 const MODE: DataType = DataType::Offset;
243 type ReadOutput = ListView<'a, T>;
244 #[inline(always)]
245 fn read_at(buf: &'a [u8], offset: usize) -> ListView<'a, T> {
246 ListView::new(buf, offset, N)
247 }
248
249 #[inline(always)]
250 fn default_output() -> ListView<'a, T> { ListView::default() }
251}
252
253// ── ListView ──────────────────────────────────────────────────────────────────
254
255/// A zero-copy, double-ended iterator and random-access view over an array
256/// stored in a flatbuffer.
257///
258/// # Memory layout
259///
260/// For inline elements (`T::MODE == Inline`):
261/// ```text
262/// offset → [elem_0_bytes][elem_1_bytes]...[elem_{len-1}_bytes]
263/// ```
264///
265/// For offset elements (`T::MODE == Offset`):
266/// ```text
267/// offset → [fwd_off_0: u32][fwd_off_1: u32]...[fwd_off_{n-1}: u32]
268/// ↓ ↓
269/// [elem_0 data] [elem_1 data] ...
270/// ```
271/// Each forward offset is relative to its own position:
272/// `abs_pos(i) = (offset + i*4) + forward_offset[i]`.
273///
274/// # Fields
275///
276/// - `buf` — the buffer this view borrows from.
277/// - `offset` — absolute byte position of the first element (or offset table).
278/// - `len` — total number of elements including any skipped ones.
279/// - `next` / `back` — iterator cursors for `Iterator` and `DoubleEndedIterator`.
280/// Both are **indices** into the list (0-based), starting at 0 / len and
281/// converging as elements are consumed.
282/// - `skip` — unordered set of indices to omit during iteration. Empty
283/// (`&[]`) when no rows are skipped; the fast path is a single `is_empty()`
284/// check so the non-skipping case is unchanged.
285///
286/// # Skip list
287///
288/// Call [`with_skip`](Self::with_skip) to attach a skip list after construction.
289/// The slice does not need to be sorted. `get` and `total_len` / `is_empty`
290/// are unaffected — they operate on the raw list. Only the iterator methods
291/// (`next`, `next_back`) honour the skip list.
292///
293/// When the skip list is active `ExactSizeIterator` is not available because
294/// the exact remaining count depends on how many skip indices fall within
295/// `[next, back)`, which is O(k) to compute. `size_hint` returns a
296/// conservative upper bound of `back - next`.
297pub struct ListView<'a, T> {
298 pub buf: &'a [u8],
299 pub offset: usize,
300 pub len: usize,
301 pub back: usize,
302 pub next: usize,
303 /// Unordered indices to skip during iteration. `&[]` when not skipping.
304 pub skip: &'a BitMask,
305 _marker: PhantomData<T>,
306}
307static EMPTY_MASK: BitMask = BitMask{
308 bits: Vec::new(),
309 len: 0,
310 count:0,
311};
312impl<'a, T: ReadAt<'a>> ListView<'a, T> {
313 /// Construct a new ListView anchored at `offset` with `len` elements.
314 /// No rows are skipped by default; call [`with_skip`](Self::with_skip) to
315 /// attach a skip list.
316 #[inline(always)]
317 pub fn new(buf: &'a [u8], offset: usize, len: usize) -> Self {
318 Self { buf, offset, len, back: len, next: 0, skip: &EMPTY_MASK, _marker: PhantomData }
319 }
320
321 /// Attach an unordered skip list to this view.
322 ///
323 /// Elements whose index appears anywhere in `skip` are silently omitted
324 /// by `next()` and `next_back()`. The slice does not need to be sorted.
325 /// `get`, `total_len`, and `is_empty` are not affected.
326 ///
327 /// Replaces any previously attached skip list.
328 #[inline(always)]
329 pub fn with_skip(mut self, skip: &'a BitMask) -> Self {
330 self.skip = skip;
331 self
332 }
333
334
335 /// Total number of elements in this list, including skipped and already
336 /// iterated ones.
337 #[inline(always)]
338 pub fn total_len(&self) -> usize { self.len }
339
340 /// Return `true` if the list contains no elements at all (ignores skip list).
341 #[inline(always)]
342 pub fn is_empty(&self) -> bool { self.len == 0 }
343
344 /// Compute the absolute byte position of element `index`.
345 ///
346 /// For inline types: `offset + index * size_of::<T>()`.
347 /// For offset types: follows the forward-offset entry at
348 /// `offset + index * 4` to get the element's absolute position.
349 pub fn abs_pos(&self, index: usize) -> usize {
350 if T::MODE.is_inline_flag() {
351 self.offset + index * std::mem::size_of::<T>()
352 } else {
353 let ep = self.offset + index * 4;
354 let jump = u32::read_at(self.buf, ep) as usize;
355 if jump == 0 { 0 } else { ep + jump }
356 }
357 }
358
359 /// Random access to element at `index`. Panics if `index >= len`.
360 /// Ignores the skip list — always returns the element at that raw index.
361 ///
362 /// For `Offset`- and `Union`-mode types a forward-offset entry of `0`
363 /// signals an absent element; this returns [`T::default_output()`] with no
364 /// buffer read beyond the entry itself. `Inline` types (scalars, structs)
365 /// are always present and skip this check entirely — the branch folds away
366 /// at compile time because `T::MODE` is a `const`.
367 #[inline]
368 pub fn get(&self, index: usize) -> T::ReadOutput {
369 let offset = self.abs_pos(index);
370 if offset == 0 {return T::default_output()}
371 let tag = u8::read_at(self.buf,self.offset+ 4*self.total_len() + index);
372 T::read_with_tag_at(self.buf, offset, tag)
373 }
374
375 /// Absolute position of the **last** element.
376 ///
377 /// Used by `merge_string_list` to compute the end of the string pool:
378 /// `last_offset() + 4 + u32::read_at(buf, last_offset())` gives the
379 /// exclusive end of the last string's bytes.
380 #[inline]
381 pub fn last_offset(&self) -> usize {
382 self.abs_pos(self.total_len() - 1)
383 }
384
385 /// Decode and return the last element without advancing the iterator.
386 /// Ignores the skip list.
387 #[inline]
388 pub fn read_last(&self) -> T::ReadOutput {
389 self.get(self.total_len()-1)
390 }
391}
392
393impl<'a, T: ReadAt<'a>> Iterator for ListView<'a, T> {
394 type Item = T::ReadOutput;
395
396 /// Yield the next element from the front (lowest index), honouring the
397 /// skip list.
398 #[inline]
399 fn next(&mut self) -> Option<T::ReadOutput> {
400 if !self.skip.is_empty() {
401 while self.next < self.back && self.skip.is_set(&self.next) {
402 self.next += 1;
403 }
404 }
405 if self.next >= self.back { return None; }
406 let item = self.get(self.next);
407 self.next += 1;
408 Some(item)
409 }
410
411 /// Upper bound is `back - next`; may be an overcount when a skip list is
412 /// active because some of those indices will be silently stepped over.
413 #[inline(always)]
414 fn size_hint(&self) -> (usize, Option<usize>) {
415 let upper = self.back - self.next;
416 if self.skip.is_empty() {
417 (upper, Some(upper))
418 } else {
419 (0, Some(upper))
420 }
421 }
422}
423
424impl<'a, T: ReadAt<'a>> DoubleEndedIterator for ListView<'a, T> {
425 #[inline]
426 fn next_back(&mut self) -> Option<T::ReadOutput> {
427 if !self.skip.is_empty() {
428 while self.next < self.back && self.skip.is_set(&(self.back - 1)) {
429 self.back -= 1;
430 }
431 }
432 if self.next >= self.back { return None; }
433 self.back -= 1;
434 Some(self.get(self.back))
435 }
436}
437/// `ExactSizeIterator` is only implemented when no skip list is active, because
438/// once skips are present the exact remaining count requires an O(k) scan over
439/// the skip slice to count how many entries fall within `[next, back)`.
440///
441/// If you need the precise remaining count with an active skip list, compute it
442/// as: `(back - next) - skip.iter().filter(|&&i| i >= next && i < back).count()`
443impl<'a, T: ReadAt<'a>> ExactSizeIterator for ListView<'a, T> {
444 #[inline(always)]
445 fn len(&self) -> usize {
446 debug_assert!(
447 self.skip.is_empty(),
448 "ExactSizeIterator::len called on a ListView with an active skip list — \
449 result is an overcount; use size_hint or compute manually"
450 );
451 self.back - self.next
452 }
453}
454
455/// `ReadAt` for `ListView` itself — allows nested arrays (`Vec<Vec<T>>`).
456impl<'a, T: ReadAt<'a>> ReadAt<'a> for ListView<'a, T> {
457 const MODE: DataType = DataType::Offset;
458 type ReadOutput = ListView<'a, T>;
459 #[inline]
460 fn read_at(buf: &'a [u8], offset: usize) -> Self::ReadOutput {
461 ListView::new(buf, offset + 4, u32::read_at(buf, offset) as usize)
462 }
463 #[inline(always)]
464 fn default_output() -> ListView<'a, T> { ListView::default() }
465}
466
467/// Collect a `ListView<String>` into an owned `Vec<String>`.
468impl<'a> From<ListView<'a, String>> for Vec<String> {
469 fn from(view: ListView<'a, String>) -> Vec<String> {
470 view.map(|s| s.to_string()).collect()
471 }
472}
473
474/// Collect a `ListView<T>` into an owned `Vec<T>` for scalar/Pod types.
475impl<'a, T: ReadAt<'a, ReadOutput = T> + Clone> From<ListView<'a, T>> for Vec<T> {
476 fn from(view: ListView<'a, T>) -> Vec<T> {
477 view.collect()
478 }
479}
480
481impl<'a, T> Default for ListView<'a, T> {
482 fn default() -> Self {
483 Self { buf: &[], offset: 0, len: 0, back: 0, next: 0, skip: &EMPTY_MASK, _marker: PhantomData }
484 }
485}
486
487// ── RawView ───────────────────────────────────────────────────────────────────
488
489/// Low-level, type-erased view of a single table object.
490///
491/// Holds the three positions needed to navigate any table:
492/// - `buf` — the entire buffer the table lives in.
493/// - `t_pos` — absolute position of the table object (where the vtable jump is).
494/// - `v_pos` — absolute position of the vtable (computed from the jump).
495///
496/// All generated `XxxView` structs wrap a `RawView` as their inner field and
497/// delegate field lookups to its methods.
498///
499/// # Vtable navigation
500///
501/// ```text
502/// t_pos → [vtable_jump: i32][field_0_data]...[field_n_data]
503///
504/// v_pos = t_pos - vtable_jump (jump is negative when vtable is before table)
505///
506/// v_pos → [vtable_size: u16][object_size: u16]
507/// [voff_0: u16][voff_1: u16]...[voff_n: u16]
508/// ```
509///
510/// `voff(i) == 0` means field `i` is absent (default value); non-zero means
511/// the field data starts at `t_pos + voff(i)`.
512#[derive(Clone, Copy, Default,)]
513pub struct RawView<'a> {
514 pub buf: &'a [u8],
515 /// Absolute position of the table object's first byte (the vtable jump).
516 pub t_pos: usize,
517 /// Absolute position of the vtable's first byte.
518 pub v_pos: usize,
519}
520
521impl<'a> RawView<'a> {
522 pub const EMPTY: RawView<'static> = RawView { buf: &[], t_pos: 0, v_pos: 0 };
523 /// Construct a `RawView` given the absolute position of the table object.
524 ///
525 /// The vtable position is computed by reading the signed 32-bit jump stored
526 /// at `table_pos`: `v_pos = table_pos - jump`.
527 #[inline(always)]
528 pub fn new(buf: &'a [u8], table_pos: usize) -> Self {
529 let v_pos = (table_pos as i32 - i32::read_at(buf, table_pos)) as usize;
530 Self { buf, t_pos: table_pos, v_pos }
531 }
532
533 /// Construct from a **slot** (distance from the end of `buf`).
534 ///
535 /// Converts `slot → absolute position` via `buf.len() - slot`, then
536 /// delegates to `new`.
537 #[inline(always)]
538 pub fn from_slot(buf: &'a [u8], slot: usize) -> Self {
539 Self::new(buf, buf.len() - slot)
540 }
541
542 /// Read the vtable offset for field `field_idx`.
543 ///
544 /// The vtable stores one `u16` per field starting at `v_pos + 4` (after
545 /// the 2-byte vtable size and 2-byte object size). A value of 0 means
546 /// the field is absent.
547 #[inline(always)]
548 pub fn voff(&self, field_idx: usize) -> usize {
549 u16::read_at(self.buf, self.v_pos + 4 + field_idx * 2) as usize
550 }
551
552 /// Return `true` if field `field_idx` is present (vtable offset != 0).
553 #[inline(always)]
554 pub fn is_present(&self, field_idx: usize) -> bool {
555 self.voff(field_idx) != 0
556 }
557
558 /// Follow an `Offset`-mode field: read the forward offset at
559 /// `t_pos + voff(field_idx)` and return the absolute position of the
560 /// pointed-to data.
561 #[inline]
562 pub fn indirect_idx(&self, field_idx: usize) -> usize {
563 let field_pos = self.t_pos + self.voff(field_idx);
564 field_pos + u32::read_at(self.buf, field_pos) as usize
565 }
566
567 #[inline(always)]
568 pub fn vtable_bytes(&self) -> &'a [u8] {
569 let vt_size = u16::read_at(self.buf, self.v_pos) as usize;
570 &self.buf[self.v_pos..self.v_pos + vt_size]
571 }
572}
573
574// ── HasRawView ────────────────────────────────────────────────────────────────
575
576/// Implemented by every generated `XxxView<'a>` type.
577///
578/// This trait exists so that [`merge_table_list`](crate::merge_table_list) can
579/// access the vtable position and block boundaries of a table element view
580/// without knowing its concrete type at compile time. Without this trait the
581/// generic function would require a type parameter for each table type, causing
582/// a separate monomorphization per list field per table — exactly the
583/// compile-time cost we are trying to avoid.
584///
585/// Both methods are `#[inline(always)]` in the generated impls so the
586/// indirection has no runtime cost.
587pub trait HasRawView<'a> {
588 /// Return a reference to the inner `RawView` for this view.
589 fn raw_view(&self) -> &RawView<'a>;
590
591 /// Return the exclusive end byte position of this table's data block in
592 /// the source buffer. Equivalent to the generated `block_end()` method.
593 ///
594 /// Used by `merge_table_list` to determine the size of the block to copy
595 /// when merging: `block_size = block_end() - t_pos`.
596 fn block_end_dyn(&self) -> usize;
597}
598
599impl<'a, T: ReadAt<'a>> PartialEq for ListView<'a, T>
600where
601 T::ReadOutput: PartialEq,
602{
603 fn eq(&self, other: &Self) -> bool {
604 if self.total_len() != other.total_len() { return false; }
605 (0..self.total_len()).all(|i| self.get(i) == other.get(i))
606 }
607}
608
609impl<'a, T: ReadAt<'a>> std::fmt::Debug for ListView<'a, T>
610where
611 T::ReadOutput: std::fmt::Debug,
612{
613 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
614 let mut list = f.debug_list();
615 for i in 0..self.total_len() {
616 list.entry(&self.get(i));
617 }
618 list.finish()
619 }
620}
621
622// ── Vec<T> ↔ ListView<T> PartialEq ───────────────────────────────────────────
623
624impl<'a, T: ReadAt<'a>> PartialEq<ListView<'a, T>> for Vec<T>
625where
626 T: PartialEq<T::ReadOutput>,
627{
628 fn eq(&self, other: &ListView<'a, T>) -> bool {
629 if self.len() != other.total_len() { return false; }
630 self.iter().enumerate().all(|(i, v)| *v == other.get(i))
631 }
632}
633
634impl<'a, T: ReadAt<'a>> PartialEq<Vec<T>> for ListView<'a, T>
635where
636 T: PartialEq<T::ReadOutput>,
637{
638 fn eq(&self, other: &Vec<T>) -> bool { other == self }
639}