Skip to main content

rudb_vector/
buffer.rs

1//! The run of values behind a flat vector, and the seam the buffer manager arrives through.
2//!
3//! `spec/engine/03-data-plane.md` section 3.8. Today every vector owns its payload and every scan
4//! allocates, which is the right place to start and is not where this ends. At layer three the scan
5//! reads a page out of the buffer manager and the vector wants to point into that page rather than
6//! copy out of it, and the copy it avoids is the largest single copy in the system, because it is
7//! every byte of every column every query reads.
8//!
9//! The type that supports both is one enum holding either an owned run or a borrowed one, and the
10//! part that has to be decided correctly the first time is how the borrow is expressed, because that
11//! shows up in every signature that mentions a vector.
12//!
13//! # Why the pin is a handle and not a lifetime
14//!
15//! The obvious way to express a borrow in Rust is a lifetime parameter, and it is the wrong one
16//! here. A lifetime on [`Buffer`] is a lifetime on [`Data`](crate::Data), which is a lifetime on
17//! [`Vector`](crate::Vector), which is a lifetime on [`Chunk`](crate::Chunk), which is a lifetime on
18//! every operator's state, on every trait object in the pipeline, and on every queue a chunk is put
19//! into for another thread to pick up. The scheduler is exactly that last thing, so the borrow would
20//! have to outlive a hand off between threads that the compiler has no way to see the end of. The
21//! two ways out of that are unsafe code and a copy at the boundary, and the copy at the boundary is
22//! the thing the borrow existed to avoid.
23//!
24//! So the pin is a [`Pin`], a reference counted handle the buffer holds, and the page stays alive
25//! because the handle is alive rather than because a region ends. It costs one atomic increment per
26//! vector construction, which is not measurable next to reading the page it is protecting, and the
27//! ownership story stays uniform: a chunk is `Send`, always, whatever its columns are pointing at.
28//!
29//! # Why it landed with one variant
30//!
31//! There was no buffer manager, so there was nothing to borrow from, and writing the borrowed
32//! variant then would have been writing an interface against an imaginary caller. What landed was
33//! the enum, with only the owned variant in it, so that adding a variant later is a change inside
34//! this crate rather than a change to every signature in the workspace. The reader side of that
35//! migration was already done by the [`Deref`] below: everything outside this crate reads a slice,
36//! and a slice is what every variant hands back. The section after this one is that bet being
37//! collected, and it cost two functions.
38//!
39//! The writer side is [`Buffer::to_mut`], which is the one function that has to grow a case. A write
40//! through a borrowed buffer has to copy the page into an owned run first, which is what `Cow` does
41//! and for the same reason, and having the call site named now means that day is a change to one
42//! function rather than a search for every `push`.
43//!
44//! # The second variant arrived early, and from the other direction
45//!
46//! The shared variant is here before the buffer manager is, because the Parquet reader needed the
47//! same thing for a different reason. A string column is built over the page it was decoded from
48//! rather than copying out of it, so the page becomes the column's arena and goes downstream with
49//! it, and the reader never gets the allocation back. On `hits` that is ten and a half megabytes a
50//! row group, freed and taken again for every row group, and glibc hands a block that size back to
51//! the kernel when it is freed, so the next one faults in every page of it. Measured on the URL
52//! column of `hits-1m-snappy.parquet`, running with `MALLOC_MMAP_THRESHOLD_` and
53//! `MALLOC_TRIM_THRESHOLD_` both raised so that nothing is ever handed back took the query from
54//! 113.08 milliseconds to 104.08, and the decode stage moved as well as the decompress one, which
55//! is what says it is page faults rather than anything about the codec.
56//!
57//! So the arena is held by an [`Arc`] and the reader keeps a handle to it. When every column built
58//! over that page has been dropped the reader is the only holder left, takes the run back out and
59//! decompresses the next page into it. That is a page pool with two entries and no eviction policy,
60//! which is not the buffer manager, but it is the same shape and it is the first caller that will
61//! want one.
62//!
63//! It is [`Arc<Vec<T>>`] rather than [`Pin`] because this caller knows exactly what its page is and
64//! can say so in the type. The [`Pin`] variant is still coming and is still opaque, because the
65//! buffer manager's page is a frame in a pool that a vector has no business knowing the shape of.
66//! Two variants for two situations is the honest answer here: one of them can name its page and the
67//! other cannot.
68//!
69//! # A shared buffer is a window, not a whole page
70//!
71//! The shared variant carries an offset and a length, so a buffer can be a run inside a page rather
72//! than the whole of one. That is what makes [`Buffer::slice`] pointer arithmetic and a reference
73//! count bump instead of an allocation and a copy.
74//!
75//! It was a whole page for one release and the cost of that shows up everywhere a column is cut.
76//! Every other form of a vector could already be cut for nothing, because a bit packed body moves an
77//! offset, a dictionary body shares its values, a string body shares its arena and a run length body
78//! keeps the runs it touches. The flat body was the exception, and it was the exception because this
79//! type could not name a piece of itself, so `spec/perf/12-the-chunk-and-the-page.md` measured 1,803
80//! instructions a chunk of memcpy and 1,869 of malloc and free on a `sum` over an in memory table,
81//! which together were 27 percent of the chunk. None of that was the query's work.
82//!
83//! The owned variant does not grow a window. A `Vec<T>` is the thing a writer appends to and an
84//! offset on it would mean every `push` had to think about where the run starts, for no gain: a
85//! caller that means its run to be cut many times says so once, with [`Buffer::into_page`], and
86//! after that the cuts are free. Cutting an owned run still copies, which is the same answer `Cow`
87//! gives and is why [`Buffer::slice`] is where the decision is made rather than at each call site.
88
89use std::any::Any;
90use std::ops::Deref;
91use std::sync::Arc;
92
93/// What keeps a page alive for as long as a buffer points into it.
94///
95/// Opaque on purpose. The vector does not know what a page is and has no business looking inside
96/// one, it only has to hold the thing that stops the page being evicted, and the buffer manager at
97/// layer three decides what that thing is. The bounds are the load bearing part and they are here
98/// now: `Send` and `Sync`, because a chunk carrying one of these crosses a thread boundary every
99/// time the scheduler moves a pipeline, and `'static`, because a handle with a lifetime on it would
100/// have put the lifetime back on [`Vector`](crate::Vector) by another route.
101pub type Pin = Arc<dyn Any + Send + Sync>;
102
103/// A run of values of one physical type.
104///
105/// Derefs to a slice, which is how every reader in the workspace gets at it, so a reader does not
106/// know or care which variant it is holding. Writers go through [`Self::push`] and
107/// [`Self::to_mut`].
108#[derive(Debug, Clone)]
109pub struct Buffer<T> {
110    store: Store<T>,
111}
112
113/// Where the values actually are.
114///
115/// The third is a run inside a page, carrying the [`Pin`] that keeps the page where it is, and it
116/// arrives with the buffer manager because that is the only thing that can hand one out. It is an
117/// enum rather than a newtype around `Vec<T>` because the shape is what the rest of the workspace
118/// is compiled against, and a newtype that turns into an enum later is the flag day this exists to
119/// avoid.
120#[derive(Debug, Clone)]
121enum Store<T> {
122    /// The vector owns the values.
123    Owned(Vec<T>),
124    /// The values are a run inside a page somebody else is holding a handle to as well.
125    ///
126    /// `from` and `len` name the run, and the invariant is that `from + len <= page.len()`, which
127    /// every constructor below clamps to rather than asserting, because the callers that cut a
128    /// buffer are cutting it to a row count that a short body is allowed to fall short of.
129    ///
130    /// Read only, which is not enforced and does not need to be: the only way to write is
131    /// [`Buffer::to_mut`] and that copies out first, so a shared page is never written through even
132    /// by a caller that has forgotten what it is holding.
133    Shared { page: Arc<Vec<T>>, from: usize, len: usize },
134}
135
136impl<T> Buffer<T> {
137    /// An empty buffer.
138    #[must_use]
139    pub fn new() -> Self {
140        Self { store: Store::Owned(Vec::new()) }
141    }
142
143    /// An empty buffer with room for `capacity` values.
144    #[must_use]
145    pub fn with_capacity(capacity: usize) -> Self {
146        Self { store: Store::Owned(Vec::with_capacity(capacity)) }
147    }
148
149    /// A buffer owning `values`.
150    #[must_use]
151    pub fn from_vec(values: Vec<T>) -> Self {
152        Self { store: Store::Owned(values) }
153    }
154
155    /// A buffer over a whole page that somebody else is holding a handle to as well.
156    ///
157    /// The caller that wants this is one that decoded the page and is going to want the allocation
158    /// back when the last reader of it is gone, which it gets by keeping its own handle and waiting
159    /// for [`Arc::get_mut`] to start answering. Nothing here enforces that, and a caller that drops
160    /// its handle has simply built an owned buffer with an extra indirection.
161    #[must_use]
162    pub fn from_arc(page: Arc<Vec<T>>) -> Self {
163        let len = page.len();
164        Self { store: Store::Shared { page, from: 0, len } }
165    }
166
167    /// A buffer over a run inside a page that somebody else is holding a handle to as well.
168    ///
169    /// Clamped to the page, so a run that starts or ends past it comes back shorter rather than
170    /// panicking. The caller asking for this is a scan that knows where in the page its chunk
171    /// starts, and it is the constructor [`Self::slice`] reaches for once the buffer is shared.
172    #[must_use]
173    pub fn window(page: Arc<Vec<T>>, from: usize, len: usize) -> Self {
174        let from = from.min(page.len());
175        let len = len.min(page.len() - from);
176        Self { store: Store::Shared { page, from, len } }
177    }
178
179    /// Whether this buffer is a run inside a page it shares rather than one it owns.
180    ///
181    /// For a caller deciding whether a write is about to cost a copy of the page, and for the tests
182    /// that assert the reader did not quietly stop sharing.
183    #[must_use]
184    pub fn is_shared(&self) -> bool {
185        matches!(self.store, Store::Shared { .. })
186    }
187
188    /// This run as a page, so that every cut of it after this one is free.
189    ///
190    /// For a producer that has built a run it means to hand out in pieces. It costs one allocation
191    /// for the [`Arc`] and moves the run into it without copying the values, and after it the buffer
192    /// is read only in the sense that a write copies out. A buffer that is already a window comes
193    /// back unchanged, including its offset, so calling this twice is not two pages.
194    #[must_use]
195    pub fn into_page(self) -> Self {
196        match self.store {
197            Store::Owned(values) => Self::from_arc(Arc::new(values)),
198            shared @ Store::Shared { .. } => Self { store: shared },
199        }
200    }
201
202    /// How many bytes of memory this buffer is holding.
203    ///
204    /// Capacity rather than length, because capacity is what was taken from the allocator.
205    ///
206    /// A shared page is charged to the buffers over it in equal parts, which is an approximation
207    /// and is worth being plain about. The exact answer needs to know who else is holding the page
208    /// and what they are charging, and no buffer can see that. Splitting it means the live buffers
209    /// over one page add up to slightly less than the page, never to several times it, and that is
210    /// the direction to be wrong in: a scan that emits fifty chunks over one ten megabyte page
211    /// would otherwise report half a gigabyte and trip a memory limit that nothing came close to.
212    ///
213    /// It is the page and not the window, because the page is what is resident. A window over a
214    /// tenth of a page that nobody else is reading holds the whole page down, and reporting the
215    /// window would say a scan of one chunk out of a hundred megabyte page costs a megabyte, which
216    /// is the direction that lets a memory limit be passed by something that has not freed anything.
217    #[must_use]
218    pub fn footprint(&self) -> usize {
219        match &self.store {
220            Store::Owned(values) => values.capacity() * size_of::<T>(),
221            Store::Shared { page, .. } => {
222                page.capacity() * size_of::<T>() / Arc::strong_count(page)
223            }
224        }
225    }
226
227    /// The values.
228    #[must_use]
229    #[inline]
230    pub fn as_slice(&self) -> &[T] {
231        match &self.store {
232            Store::Owned(values) => values,
233            // The invariant on the variant is that this range is inside the page, and every
234            // constructor clamps to it, so the index cannot be out of bounds.
235            Store::Shared { page, from, len } => &page[*from..*from + *len],
236        }
237    }
238}
239
240impl<T: Clone> Buffer<T> {
241    /// The values as an owned run, copying only if they were not owned already.
242    ///
243    /// One of the two places that can copy a page, and it is spelled as a method rather than a
244    /// field access so that it is greppable. The copy is skipped when the caller turns out to hold
245    /// the last handle to the whole page, which is the common case for a buffer that was shared only
246    /// so that its producer could get the allocation back. A window over part of a page always
247    /// copies, because the run it names is not a `Vec` and cannot become one for free.
248    #[must_use]
249    pub fn into_vec(self) -> Vec<T> {
250        match self.store {
251            Store::Owned(values) => values,
252            Store::Shared { page, from, len } if from == 0 && len == page.len() => {
253                Arc::try_unwrap(page).unwrap_or_else(|page| page.to_vec())
254            }
255            Store::Shared { page, from, len } => page[from..from + len].to_vec(),
256        }
257    }
258
259    /// A run of `len` values starting at `from`, clamped to what is there.
260    ///
261    /// Free on a shared buffer, which is the whole point: the page is the same page, the offsets add
262    /// up, and the cost is one atomic increment. A copy on an owned one, because an owned run is a
263    /// `Vec` and a piece of a `Vec` is not one. A producer that means its run to be cut many times
264    /// calls [`Self::into_page`] first and then pays nothing per cut.
265    #[must_use]
266    pub fn slice(&self, from: usize, len: usize) -> Self {
267        match &self.store {
268            Store::Owned(values) => {
269                let from = from.min(values.len());
270                let len = len.min(values.len() - from);
271                Self::from_vec(values[from..from + len].to_vec())
272            }
273            Store::Shared { page, from: start, len: held } => {
274                let from = from.min(*held);
275                let len = len.min(*held - from);
276                Self { store: Store::Shared { page: Arc::clone(page), from: start + from, len } }
277            }
278        }
279    }
280
281    /// The values, writable, copying them out of the page first if they are not owned.
282    ///
283    /// The copy on write point, and the only one. Everything that mutates a buffer goes through
284    /// here, so a variant that is not owned needs a case in this function and in nothing else.
285    #[inline]
286    pub fn to_mut(&mut self) -> &mut Vec<T> {
287        if let Store::Shared { page, from, len } = &self.store {
288            self.store = Store::Owned(page[*from..*from + *len].to_vec());
289        }
290        match &mut self.store {
291            Store::Owned(values) => values,
292            // A page cannot be here: the line above just replaced it.
293            Store::Shared { .. } => unreachable!("a shared page was copied out one statement ago"),
294        }
295    }
296
297    /// Appends one value.
298    #[inline]
299    pub fn push(&mut self, value: T) {
300        self.to_mut().push(value);
301    }
302
303    /// Room for `additional` more values, taken in one allocation.
304    pub fn reserve(&mut self, additional: usize) {
305        self.to_mut().reserve(additional);
306    }
307
308    /// Appends a run of values.
309    pub fn extend_from_slice(&mut self, values: &[T]) {
310        self.to_mut().extend_from_slice(values);
311    }
312}
313
314/// Equality is the values and not where they live.
315///
316/// Derived equality would call an owned run different from a page holding the same values, which
317/// would make every test in the workspace that compares two vectors assert on how the vector was
318/// built. [`crate::StringColumn`] settles the same question the same way and for the same reason.
319impl<T: PartialEq> PartialEq for Buffer<T> {
320    fn eq(&self, other: &Self) -> bool {
321        self.as_slice() == other.as_slice()
322    }
323}
324
325impl<T: Eq> Eq for Buffer<T> {}
326
327impl<T> Default for Buffer<T> {
328    fn default() -> Self {
329        Self::new()
330    }
331}
332
333impl<T> Deref for Buffer<T> {
334    type Target = [T];
335
336    #[inline]
337    fn deref(&self) -> &[T] {
338        self.as_slice()
339    }
340}
341
342impl<T> From<Vec<T>> for Buffer<T> {
343    fn from(values: Vec<T>) -> Self {
344        Self::from_vec(values)
345    }
346}
347
348impl<T> FromIterator<T> for Buffer<T> {
349    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
350        Self::from_vec(iter.into_iter().collect())
351    }
352}
353
354impl<T: Clone> Extend<T> for Buffer<T> {
355    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
356        self.to_mut().extend(iter);
357    }
358}
359
360impl<T: Clone> IntoIterator for Buffer<T> {
361    type Item = T;
362    type IntoIter = std::vec::IntoIter<T>;
363
364    fn into_iter(self) -> Self::IntoIter {
365        self.into_vec().into_iter()
366    }
367}
368
369#[cfg(test)]
370mod tests {
371    use std::sync::Arc;
372
373    use super::{Buffer, Pin};
374
375    #[test]
376    fn a_buffer_reads_back_as_a_slice() {
377        let buffer: Buffer<i32> = vec![1, 2, 3].into();
378        assert_eq!(buffer.as_slice(), &[1, 2, 3]);
379        assert_eq!(buffer.len(), 3);
380        assert_eq!(buffer[1], 2);
381        assert_eq!(buffer.iter().sum::<i32>(), 6);
382        assert_eq!(buffer.clone().into_vec(), vec![1, 2, 3]);
383    }
384
385    #[test]
386    fn writing_goes_through_one_function() {
387        let mut buffer = Buffer::with_capacity(4);
388        buffer.push(1u8);
389        buffer.extend_from_slice(&[2, 3]);
390        buffer.extend([4u8]);
391        buffer.to_mut().sort_unstable_by(|a, b| b.cmp(a));
392        assert_eq!(buffer.as_slice(), &[4, 3, 2, 1]);
393    }
394
395    #[test]
396    fn an_empty_buffer_is_the_default_and_collects_like_a_vector() {
397        assert!(Buffer::<u64>::default().is_empty());
398        assert!(Buffer::<u64>::new().is_empty());
399        let collected: Buffer<u64> = (0..4).collect();
400        assert_eq!(collected.as_slice(), &[0, 1, 2, 3]);
401        assert_eq!(collected.into_iter().count(), 4);
402    }
403
404    #[test]
405    fn a_shared_page_reads_like_an_owned_run_and_compares_equal_to_one() {
406        let page = Arc::new(vec![1u8, 2, 3]);
407        let buffer = Buffer::from_arc(Arc::clone(&page));
408        assert!(buffer.is_shared());
409        assert_eq!(buffer.as_slice(), &[1, 2, 3]);
410        assert_eq!(buffer[2], 3);
411        assert_eq!(buffer, Buffer::from_vec(vec![1u8, 2, 3]));
412        assert_eq!(Buffer::from_vec(vec![1u8, 2, 3]), buffer);
413        assert_ne!(buffer, Buffer::from_vec(vec![1u8, 2]));
414    }
415
416    /// The point of the variant. The producer keeps a handle, the reader drops its buffer, and the
417    /// producer gets the allocation back rather than the allocator getting it.
418    #[test]
419    fn the_producer_gets_the_page_back_once_the_last_buffer_over_it_is_gone() {
420        let mut page = Arc::new(vec![0u8; 64]);
421        let address = page.as_ptr();
422        let first = Buffer::from_arc(Arc::clone(&page));
423        let second = Buffer::from_arc(Arc::clone(&page));
424        assert!(Arc::get_mut(&mut page).is_none());
425        drop(first);
426        assert!(Arc::get_mut(&mut page).is_none());
427        drop(second);
428        let run = Arc::get_mut(&mut page).expect("the last handle");
429        assert_eq!(run.as_ptr(), address, "the page was reallocated rather than reused");
430    }
431
432    /// Writing through a shared page copies it out, and the page the producer is holding is left
433    /// exactly as it was. The one case where getting this wrong would corrupt another reader.
434    #[test]
435    fn writing_through_a_shared_page_copies_it_and_leaves_the_page_alone() {
436        let page = Arc::new(vec![1u8, 2, 3]);
437        let mut buffer = Buffer::from_arc(Arc::clone(&page));
438        buffer.push(4);
439        assert!(!buffer.is_shared());
440        assert_eq!(buffer.as_slice(), &[1, 2, 3, 4]);
441        assert_eq!(page.as_slice(), &[1, 2, 3]);
442        assert_eq!(Buffer::from_arc(Arc::clone(&page)).into_vec(), vec![1, 2, 3]);
443    }
444
445    /// Taking the run out of the last handle to a page does not copy it, which is what makes the
446    /// shared variant free for a caller that ends up being the only reader after all.
447    #[test]
448    fn the_last_buffer_over_a_page_takes_the_run_without_copying_it() {
449        let page = Arc::new(vec![5u8; 32]);
450        let address = page.as_ptr();
451        let run = Buffer::from_arc(page).into_vec();
452        assert_eq!(run.as_ptr(), address);
453    }
454
455    /// The accounting rule from [`Buffer::footprint`], which is that the buffers over a page add up
456    /// to at most the page rather than to a multiple of it.
457    #[test]
458    fn a_shared_page_is_charged_once_across_the_buffers_over_it() {
459        let page = Arc::new(vec![0u64; 100]);
460        let over: Vec<_> = (0..4).map(|_| Buffer::from_arc(Arc::clone(&page))).collect();
461        let charged: usize = over.iter().map(Buffer::footprint).sum();
462        assert!(
463            charged <= page.capacity() * 8,
464            "{charged} charged for a {} byte page",
465            page.len() * 8
466        );
467        assert!(charged > 0);
468        assert_eq!(Buffer::from_vec(vec![0u64; 100]).footprint(), 800);
469    }
470
471    /// A window reads as its own run and nothing else, so nobody above the buffer can tell it is
472    /// looking at the middle of a page.
473    #[test]
474    fn a_window_reads_as_its_own_run() {
475        let page = Arc::new((0u8..10).collect::<Vec<_>>());
476        let window = Buffer::window(Arc::clone(&page), 3, 4);
477        assert!(window.is_shared());
478        assert_eq!(window.as_slice(), &[3, 4, 5, 6]);
479        assert_eq!(window.len(), 4);
480        assert_eq!(window[0], 3);
481        assert_eq!(window, Buffer::from_vec(vec![3u8, 4, 5, 6]));
482        assert_eq!(window.iter().sum::<u8>(), 18);
483        assert_eq!(window.clone().into_vec(), vec![3, 4, 5, 6]);
484    }
485
486    /// The whole point of the offset. Cutting a shared buffer moves a pointer and does not touch a
487    /// byte, which is asserted on the address rather than on the values, because the values would be
488    /// the same either way.
489    #[test]
490    fn cutting_a_shared_buffer_points_into_the_same_page() {
491        let page = Arc::new((0u64..100).collect::<Vec<_>>());
492        let address = page.as_ptr() as usize;
493        let whole = Buffer::from_arc(Arc::clone(&page));
494        let run = whole.slice(64, 16);
495        assert!(run.is_shared());
496        assert_eq!(run.as_slice().as_ptr() as usize, address + 64 * 8);
497        assert_eq!(run.as_slice(), &(64u64..80).collect::<Vec<_>>()[..]);
498        // And cutting a cut adds the offsets up rather than losing the first one.
499        let inner = run.slice(4, 2);
500        assert_eq!(inner.as_slice().as_ptr() as usize, address + 68 * 8);
501        assert_eq!(inner.as_slice(), &[68, 69]);
502    }
503
504    /// Cutting an owned run copies, which is the honest answer for a `Vec`, and the way out is to say
505    /// once that the run is a page.
506    #[test]
507    fn cutting_an_owned_run_copies_and_a_page_is_how_a_producer_avoids_that() {
508        let owned = Buffer::from_vec((0u32..16).collect());
509        let run = owned.slice(4, 4);
510        assert!(!run.is_shared());
511        assert_eq!(run.as_slice(), &[4, 5, 6, 7]);
512
513        let page = Buffer::from_vec((0u32..16).collect()).into_page();
514        let address = page.as_slice().as_ptr() as usize;
515        assert!(page.is_shared(), "into_page did not share the run");
516        let run = page.slice(4, 4);
517        assert!(run.is_shared());
518        assert_eq!(run.as_slice().as_ptr() as usize, address + 4 * 4);
519        assert_eq!(run.as_slice(), &[4, 5, 6, 7]);
520        // Twice is not two pages, and the second call does not lose an offset either.
521        let again = run.into_page();
522        assert_eq!(again.as_slice().as_ptr() as usize, address + 4 * 4);
523    }
524
525    /// A cut that runs off the end comes back short, because the callers that cut a buffer are
526    /// cutting it to a row count and a body is allowed to be shorter than the rows asked for.
527    #[test]
528    fn a_cut_past_the_end_comes_back_short_rather_than_panicking() {
529        let page = Arc::new(vec![1u16, 2, 3, 4]);
530        let whole = Buffer::from_arc(Arc::clone(&page));
531        assert_eq!(whole.slice(2, 10).as_slice(), &[3, 4]);
532        assert!(whole.slice(9, 1).is_empty());
533        assert_eq!(Buffer::window(Arc::clone(&page), 3, 9).as_slice(), &[4]);
534        assert!(Buffer::window(Arc::clone(&page), 7, 2).is_empty());
535        let owned = Buffer::from_vec(vec![1u16, 2, 3, 4]);
536        assert_eq!(owned.slice(2, 10).as_slice(), &[3, 4]);
537        assert!(owned.slice(9, 1).is_empty());
538        // A window of a window cannot see past the window it came from.
539        let middle = Buffer::window(Arc::clone(&page), 1, 2);
540        assert_eq!(middle.slice(0, 10).as_slice(), &[2, 3]);
541    }
542
543    /// Writing through a window copies the window and not the page, and leaves the page alone. The
544    /// case where getting it wrong would hand another reader somebody else's values.
545    #[test]
546    fn writing_through_a_window_copies_the_window_and_leaves_the_page_alone() {
547        let page = Arc::new(vec![1u8, 2, 3, 4, 5]);
548        let mut window = Buffer::window(Arc::clone(&page), 1, 3);
549        window.push(9);
550        assert!(!window.is_shared());
551        assert_eq!(window.as_slice(), &[2, 3, 4, 9]);
552        assert_eq!(page.as_slice(), &[1, 2, 3, 4, 5]);
553    }
554
555    /// Taking the run out of a window copies it, because the run it names is not a `Vec`, where the
556    /// whole page still comes out without a copy when this is the last handle to it.
557    #[test]
558    fn taking_the_run_out_of_a_window_copies_and_out_of_a_whole_page_does_not() {
559        let page = Arc::new(vec![7u8; 32]);
560        let address = page.as_ptr();
561        assert_eq!(Buffer::window(Arc::clone(&page), 8, 4).into_vec(), vec![7u8; 4]);
562        let whole = Buffer::from_arc(page).into_vec();
563        assert_eq!(whole.as_ptr(), address);
564    }
565
566    /// The accounting rule again, now that a buffer can be part of a page. The charge is the page's
567    /// share and not the window's, because the page is what is resident.
568    #[test]
569    fn a_window_is_charged_for_the_page_it_holds_down() {
570        let page = Arc::new(vec![0u64; 100]);
571        let windows: Vec<_> =
572            (0..4).map(|n| Buffer::window(Arc::clone(&page), n * 25, 25)).collect();
573        let charged: usize = windows.iter().map(Buffer::footprint).sum();
574        assert!(charged <= page.capacity() * 8, "{charged} charged for a {} byte page", 800);
575        assert!(charged > 100 * 8 / 4, "a window was charged less than its own share of the page");
576    }
577
578    /// The property the whole section 3.8 decision is about. A buffer of any payload can be sent to
579    /// another thread without a lifetime being involved, and so can a pin, which is what makes a
580    /// chunk `Send` once the borrowed variant exists. Asserted rather than assumed, because a pin
581    /// that was an `Rc` would compile everywhere else and fail here.
582    #[test]
583    fn a_buffer_and_a_pin_both_cross_a_thread_boundary() {
584        const fn assert_send<T: Send>() {}
585        assert_send::<Buffer<i64>>();
586        assert_send::<Pin>();
587        let pin: Pin = Arc::new(vec![0u8; 8]);
588        let buffer: Buffer<i64> = vec![7; 2].into();
589        let handle = std::thread::spawn(move || (buffer.len(), Arc::strong_count(&pin)));
590        assert_eq!(handle.join().expect("the thread"), (2, 1));
591    }
592}