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    /// This window and `next` as one window, when `next` starts in the same page where this ends.
282    ///
283    /// The way back from [`Self::slice`]. A producer that cut a page into chunks and a consumer
284    /// that lays those chunks end to end again would otherwise copy every value to rebuild a run
285    /// the page already holds. `None` for anything else, including an owned buffer, because two
286    /// `Vec`s are not one without a copy and the caller has a copying path for that.
287    #[must_use]
288    pub fn joined(&self, next: &Self) -> Option<Self> {
289        match (&self.store, &next.store) {
290            (
291                Store::Shared { page, from, len },
292                Store::Shared { page: other, from: start, len: more },
293            ) if Arc::ptr_eq(page, other) && from + len == *start => Some(Self {
294                store: Store::Shared { page: Arc::clone(page), from: *from, len: len + more },
295            }),
296            _ => None,
297        }
298    }
299
300    /// Whether this and `other` are the same window of the same page, which an owned run never is.
301    #[must_use]
302    pub fn same_window(&self, other: &Self) -> bool {
303        match (&self.store, &other.store) {
304            (
305                Store::Shared { page, from, len },
306                Store::Shared { page: theirs, from: start, len: held },
307            ) => Arc::ptr_eq(page, theirs) && from == start && len == held,
308            _ => false,
309        }
310    }
311
312    /// The values, writable, copying them out of the page first if they are not owned.
313    ///
314    /// The copy on write point, and the only one. Everything that mutates a buffer goes through
315    /// here, so a variant that is not owned needs a case in this function and in nothing else.
316    #[inline]
317    pub fn to_mut(&mut self) -> &mut Vec<T> {
318        if let Store::Shared { page, from, len } = &self.store {
319            self.store = Store::Owned(page[*from..*from + *len].to_vec());
320        }
321        match &mut self.store {
322            Store::Owned(values) => values,
323            // A page cannot be here: the line above just replaced it.
324            Store::Shared { .. } => unreachable!("a shared page was copied out one statement ago"),
325        }
326    }
327
328    /// Appends one value.
329    #[inline]
330    pub fn push(&mut self, value: T) {
331        self.to_mut().push(value);
332    }
333
334    /// Room for `additional` more values, taken in one allocation.
335    pub fn reserve(&mut self, additional: usize) {
336        self.to_mut().reserve(additional);
337    }
338
339    /// Appends a run of values.
340    pub fn extend_from_slice(&mut self, values: &[T]) {
341        self.to_mut().extend_from_slice(values);
342    }
343}
344
345/// Equality is the values and not where they live.
346///
347/// Derived equality would call an owned run different from a page holding the same values, which
348/// would make every test in the workspace that compares two vectors assert on how the vector was
349/// built. [`crate::StringColumn`] settles the same question the same way and for the same reason.
350impl<T: PartialEq> PartialEq for Buffer<T> {
351    fn eq(&self, other: &Self) -> bool {
352        self.as_slice() == other.as_slice()
353    }
354}
355
356impl<T: Eq> Eq for Buffer<T> {}
357
358impl<T> Default for Buffer<T> {
359    fn default() -> Self {
360        Self::new()
361    }
362}
363
364impl<T> Deref for Buffer<T> {
365    type Target = [T];
366
367    #[inline]
368    fn deref(&self) -> &[T] {
369        self.as_slice()
370    }
371}
372
373impl<T> From<Vec<T>> for Buffer<T> {
374    fn from(values: Vec<T>) -> Self {
375        Self::from_vec(values)
376    }
377}
378
379impl<T> FromIterator<T> for Buffer<T> {
380    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
381        Self::from_vec(iter.into_iter().collect())
382    }
383}
384
385impl<T: Clone> Extend<T> for Buffer<T> {
386    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
387        self.to_mut().extend(iter);
388    }
389}
390
391impl<T: Clone> IntoIterator for Buffer<T> {
392    type Item = T;
393    type IntoIter = std::vec::IntoIter<T>;
394
395    fn into_iter(self) -> Self::IntoIter {
396        self.into_vec().into_iter()
397    }
398}
399
400#[cfg(test)]
401mod tests {
402    use std::sync::Arc;
403
404    use super::{Buffer, Pin};
405
406    #[test]
407    fn a_buffer_reads_back_as_a_slice() {
408        let buffer: Buffer<i32> = vec![1, 2, 3].into();
409        assert_eq!(buffer.as_slice(), &[1, 2, 3]);
410        assert_eq!(buffer.len(), 3);
411        assert_eq!(buffer[1], 2);
412        assert_eq!(buffer.iter().sum::<i32>(), 6);
413        assert_eq!(buffer.clone().into_vec(), vec![1, 2, 3]);
414    }
415
416    #[test]
417    fn writing_goes_through_one_function() {
418        let mut buffer = Buffer::with_capacity(4);
419        buffer.push(1u8);
420        buffer.extend_from_slice(&[2, 3]);
421        buffer.extend([4u8]);
422        buffer.to_mut().sort_unstable_by(|a, b| b.cmp(a));
423        assert_eq!(buffer.as_slice(), &[4, 3, 2, 1]);
424    }
425
426    #[test]
427    fn an_empty_buffer_is_the_default_and_collects_like_a_vector() {
428        assert!(Buffer::<u64>::default().is_empty());
429        assert!(Buffer::<u64>::new().is_empty());
430        let collected: Buffer<u64> = (0..4).collect();
431        assert_eq!(collected.as_slice(), &[0, 1, 2, 3]);
432        assert_eq!(collected.into_iter().count(), 4);
433    }
434
435    #[test]
436    fn a_shared_page_reads_like_an_owned_run_and_compares_equal_to_one() {
437        let page = Arc::new(vec![1u8, 2, 3]);
438        let buffer = Buffer::from_arc(Arc::clone(&page));
439        assert!(buffer.is_shared());
440        assert_eq!(buffer.as_slice(), &[1, 2, 3]);
441        assert_eq!(buffer[2], 3);
442        assert_eq!(buffer, Buffer::from_vec(vec![1u8, 2, 3]));
443        assert_eq!(Buffer::from_vec(vec![1u8, 2, 3]), buffer);
444        assert_ne!(buffer, Buffer::from_vec(vec![1u8, 2]));
445    }
446
447    /// The point of the variant. The producer keeps a handle, the reader drops its buffer, and the
448    /// producer gets the allocation back rather than the allocator getting it.
449    #[test]
450    fn the_producer_gets_the_page_back_once_the_last_buffer_over_it_is_gone() {
451        let mut page = Arc::new(vec![0u8; 64]);
452        let address = page.as_ptr();
453        let first = Buffer::from_arc(Arc::clone(&page));
454        let second = Buffer::from_arc(Arc::clone(&page));
455        assert!(Arc::get_mut(&mut page).is_none());
456        drop(first);
457        assert!(Arc::get_mut(&mut page).is_none());
458        drop(second);
459        let run = Arc::get_mut(&mut page).expect("the last handle");
460        assert_eq!(run.as_ptr(), address, "the page was reallocated rather than reused");
461    }
462
463    /// Writing through a shared page copies it out, and the page the producer is holding is left
464    /// exactly as it was. The one case where getting this wrong would corrupt another reader.
465    #[test]
466    fn writing_through_a_shared_page_copies_it_and_leaves_the_page_alone() {
467        let page = Arc::new(vec![1u8, 2, 3]);
468        let mut buffer = Buffer::from_arc(Arc::clone(&page));
469        buffer.push(4);
470        assert!(!buffer.is_shared());
471        assert_eq!(buffer.as_slice(), &[1, 2, 3, 4]);
472        assert_eq!(page.as_slice(), &[1, 2, 3]);
473        assert_eq!(Buffer::from_arc(Arc::clone(&page)).into_vec(), vec![1, 2, 3]);
474    }
475
476    /// Taking the run out of the last handle to a page does not copy it, which is what makes the
477    /// shared variant free for a caller that ends up being the only reader after all.
478    #[test]
479    fn the_last_buffer_over_a_page_takes_the_run_without_copying_it() {
480        let page = Arc::new(vec![5u8; 32]);
481        let address = page.as_ptr();
482        let run = Buffer::from_arc(page).into_vec();
483        assert_eq!(run.as_ptr(), address);
484    }
485
486    /// The accounting rule from [`Buffer::footprint`], which is that the buffers over a page add up
487    /// to at most the page rather than to a multiple of it.
488    #[test]
489    fn a_shared_page_is_charged_once_across_the_buffers_over_it() {
490        let page = Arc::new(vec![0u64; 100]);
491        let over: Vec<_> = (0..4).map(|_| Buffer::from_arc(Arc::clone(&page))).collect();
492        let charged: usize = over.iter().map(Buffer::footprint).sum();
493        assert!(
494            charged <= page.capacity() * 8,
495            "{charged} charged for a {} byte page",
496            page.len() * 8
497        );
498        assert!(charged > 0);
499        assert_eq!(Buffer::from_vec(vec![0u64; 100]).footprint(), 800);
500    }
501
502    /// A window reads as its own run and nothing else, so nobody above the buffer can tell it is
503    /// looking at the middle of a page.
504    #[test]
505    fn a_window_reads_as_its_own_run() {
506        let page = Arc::new((0u8..10).collect::<Vec<_>>());
507        let window = Buffer::window(Arc::clone(&page), 3, 4);
508        assert!(window.is_shared());
509        assert_eq!(window.as_slice(), &[3, 4, 5, 6]);
510        assert_eq!(window.len(), 4);
511        assert_eq!(window[0], 3);
512        assert_eq!(window, Buffer::from_vec(vec![3u8, 4, 5, 6]));
513        assert_eq!(window.iter().sum::<u8>(), 18);
514        assert_eq!(window.clone().into_vec(), vec![3, 4, 5, 6]);
515    }
516
517    /// The whole point of the offset. Cutting a shared buffer moves a pointer and does not touch a
518    /// byte, which is asserted on the address rather than on the values, because the values would be
519    /// the same either way.
520    #[test]
521    fn cutting_a_shared_buffer_points_into_the_same_page() {
522        let page = Arc::new((0u64..100).collect::<Vec<_>>());
523        let address = page.as_ptr() as usize;
524        let whole = Buffer::from_arc(Arc::clone(&page));
525        let run = whole.slice(64, 16);
526        assert!(run.is_shared());
527        assert_eq!(run.as_slice().as_ptr() as usize, address + 64 * 8);
528        assert_eq!(run.as_slice(), &(64u64..80).collect::<Vec<_>>()[..]);
529        // And cutting a cut adds the offsets up rather than losing the first one.
530        let inner = run.slice(4, 2);
531        assert_eq!(inner.as_slice().as_ptr() as usize, address + 68 * 8);
532        assert_eq!(inner.as_slice(), &[68, 69]);
533    }
534
535    /// Cutting an owned run copies, which is the honest answer for a `Vec`, and the way out is to say
536    /// once that the run is a page.
537    #[test]
538    fn cutting_an_owned_run_copies_and_a_page_is_how_a_producer_avoids_that() {
539        let owned = Buffer::from_vec((0u32..16).collect());
540        let run = owned.slice(4, 4);
541        assert!(!run.is_shared());
542        assert_eq!(run.as_slice(), &[4, 5, 6, 7]);
543
544        let page = Buffer::from_vec((0u32..16).collect()).into_page();
545        let address = page.as_slice().as_ptr() as usize;
546        assert!(page.is_shared(), "into_page did not share the run");
547        let run = page.slice(4, 4);
548        assert!(run.is_shared());
549        assert_eq!(run.as_slice().as_ptr() as usize, address + 4 * 4);
550        assert_eq!(run.as_slice(), &[4, 5, 6, 7]);
551        // Twice is not two pages, and the second call does not lose an offset either.
552        let again = run.into_page();
553        assert_eq!(again.as_slice().as_ptr() as usize, address + 4 * 4);
554    }
555
556    /// Two cuts of one page that meet lay back as one window, and nothing else does.
557    #[test]
558    fn neighbouring_cuts_of_a_page_join_and_others_do_not() {
559        let page = Buffer::from_vec((0u32..16).collect()).into_page();
560        let joined = page.slice(2, 3).joined(&page.slice(5, 4)).expect("neighbours join");
561        assert_eq!(joined.as_slice(), &[2, 3, 4, 5, 6, 7, 8]);
562        assert_eq!(joined.as_slice().as_ptr(), page.as_slice()[2..].as_ptr());
563        assert!(page.slice(2, 3).joined(&page.slice(6, 2)).is_none(), "a gap joined");
564        assert!(page.slice(5, 2).joined(&page.slice(2, 3)).is_none(), "the wrong order joined");
565        let other = Buffer::from_vec((0u32..16).collect()).into_page();
566        assert!(page.slice(2, 3).joined(&other.slice(5, 2)).is_none(), "two pages joined");
567        let owned = Buffer::from_vec(vec![1u32, 2]);
568        assert!(owned.joined(&owned).is_none(), "an owned run joined");
569    }
570
571    /// A cut that runs off the end comes back short, because the callers that cut a buffer are
572    /// cutting it to a row count and a body is allowed to be shorter than the rows asked for.
573    #[test]
574    fn a_cut_past_the_end_comes_back_short_rather_than_panicking() {
575        let page = Arc::new(vec![1u16, 2, 3, 4]);
576        let whole = Buffer::from_arc(Arc::clone(&page));
577        assert_eq!(whole.slice(2, 10).as_slice(), &[3, 4]);
578        assert!(whole.slice(9, 1).is_empty());
579        assert_eq!(Buffer::window(Arc::clone(&page), 3, 9).as_slice(), &[4]);
580        assert!(Buffer::window(Arc::clone(&page), 7, 2).is_empty());
581        let owned = Buffer::from_vec(vec![1u16, 2, 3, 4]);
582        assert_eq!(owned.slice(2, 10).as_slice(), &[3, 4]);
583        assert!(owned.slice(9, 1).is_empty());
584        // A window of a window cannot see past the window it came from.
585        let middle = Buffer::window(Arc::clone(&page), 1, 2);
586        assert_eq!(middle.slice(0, 10).as_slice(), &[2, 3]);
587    }
588
589    /// Writing through a window copies the window and not the page, and leaves the page alone. The
590    /// case where getting it wrong would hand another reader somebody else's values.
591    #[test]
592    fn writing_through_a_window_copies_the_window_and_leaves_the_page_alone() {
593        let page = Arc::new(vec![1u8, 2, 3, 4, 5]);
594        let mut window = Buffer::window(Arc::clone(&page), 1, 3);
595        window.push(9);
596        assert!(!window.is_shared());
597        assert_eq!(window.as_slice(), &[2, 3, 4, 9]);
598        assert_eq!(page.as_slice(), &[1, 2, 3, 4, 5]);
599    }
600
601    /// Taking the run out of a window copies it, because the run it names is not a `Vec`, where the
602    /// whole page still comes out without a copy when this is the last handle to it.
603    #[test]
604    fn taking_the_run_out_of_a_window_copies_and_out_of_a_whole_page_does_not() {
605        let page = Arc::new(vec![7u8; 32]);
606        let address = page.as_ptr();
607        assert_eq!(Buffer::window(Arc::clone(&page), 8, 4).into_vec(), vec![7u8; 4]);
608        let whole = Buffer::from_arc(page).into_vec();
609        assert_eq!(whole.as_ptr(), address);
610    }
611
612    /// The accounting rule again, now that a buffer can be part of a page. The charge is the page's
613    /// share and not the window's, because the page is what is resident.
614    #[test]
615    fn a_window_is_charged_for_the_page_it_holds_down() {
616        let page = Arc::new(vec![0u64; 100]);
617        let windows: Vec<_> =
618            (0..4).map(|n| Buffer::window(Arc::clone(&page), n * 25, 25)).collect();
619        let charged: usize = windows.iter().map(Buffer::footprint).sum();
620        assert!(charged <= page.capacity() * 8, "{charged} charged for a {} byte page", 800);
621        assert!(charged > 100 * 8 / 4, "a window was charged less than its own share of the page");
622    }
623
624    /// The property the whole section 3.8 decision is about. A buffer of any payload can be sent to
625    /// another thread without a lifetime being involved, and so can a pin, which is what makes a
626    /// chunk `Send` once the borrowed variant exists. Asserted rather than assumed, because a pin
627    /// that was an `Rc` would compile everywhere else and fail here.
628    #[test]
629    fn a_buffer_and_a_pin_both_cross_a_thread_boundary() {
630        const fn assert_send<T: Send>() {}
631        assert_send::<Buffer<i64>>();
632        assert_send::<Pin>();
633        let pin: Pin = Arc::new(vec![0u8; 8]);
634        let buffer: Buffer<i64> = vec![7; 2].into();
635        let handle = std::thread::spawn(move || (buffer.len(), Arc::strong_count(&pin)));
636        assert_eq!(handle.join().expect("the thread"), (2, 1));
637    }
638}