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}