1use std::{
3 cmp,
4 hash::{Hash, Hasher},
5 mem,
6 sync::Mutex,
7};
8
9use crate::{
10 common::IndexMap,
11 numeric_id::{IdVec, NumericId, define_id},
12};
13use egglog_concurrency::{ReadOptimizedLock, ThreadPool};
14use hashbrown::HashTable;
15use indexmap::map::Entry;
16use once_cell::sync::Lazy;
17use rustc_hash::FxHasher;
18use smallvec::SmallVec;
19
20use crate::{
21 OffsetRange, Subset,
22 common::{ShardData, ShardId, Value},
23 offsets::{RowId, SortedOffsetSlice, SubsetRef},
24 parallel,
25 parallel_heuristics::parallelize_index_construction,
26 pool::{Pooled, with_pool_set},
27 row_buffer::{RowBuffer, TaggedRowBuffer},
28 table_spec::{ColumnId, Generation, Offset, TableVersion, WrappedTableRef},
29};
30
31#[cfg(test)]
32mod tests;
33
34#[doc(hidden)]
35pub mod bench_support;
36
37#[derive(Clone)]
38pub(crate) struct TableEntry<T> {
39 hash: u64,
40 key: RowId,
42 vals: T,
43}
44
45#[derive(Clone)]
46pub(crate) struct Index<TI> {
47 key: Vec<ColumnId>,
48 updated_to: TableVersion,
49 table: TI,
50}
51
52impl<TI: IndexBase> Index<TI> {
53 pub(crate) fn new(key: Vec<ColumnId>, table: TI) -> Self {
54 Index {
55 key,
56 updated_to: TableVersion {
57 major: Generation::new(0),
58 minor: Offset::new(0),
59 },
60 table,
61 }
62 }
63
64 pub(crate) fn get_subset<'a>(&'a self, key: &'a TI::Key) -> Option<SubsetRef<'a>> {
67 self.table.get_subset(key)
68 }
69
70 pub(crate) fn needs_refresh(&self, table: WrappedTableRef) -> bool {
71 table.version() != self.updated_to
72 }
73
74 pub(crate) fn refresh(&mut self, table: WrappedTableRef) {
75 let cur_version = table.version();
76 if cur_version == self.updated_to {
77 return;
78 }
79 let is_full = cur_version.major != self.updated_to.major;
80 let subset = if is_full {
81 self.table.clear();
82 table.all()
83 } else {
84 table.updates_since(self.updated_to.minor)
85 };
86 if parallelize_index_construction(subset.size()) {
100 self.table.merge_parallel(&self.key, table, subset.as_ref());
101 } else if is_full {
102 self.table.rebuild_full(&self.key, table, subset.as_ref());
103 } else {
104 self.refresh_serial(table, subset);
105 }
106
107 self.updated_to = cur_version;
108 }
109
110 pub(crate) fn refresh_serial(&mut self, table: WrappedTableRef, subset: Subset) {
115 let mut buf = TaggedRowBuffer::new(self.key.len());
116 let mut cur = Offset::new(0);
117 loop {
118 buf.clear();
119 if let Some(next) =
120 table.scan_project(subset.as_ref(), &self.key, cur, 1024, &[], &mut buf)
121 {
122 cur = next;
123 self.table.merge_rows(&buf);
124 } else {
125 self.table.merge_rows(&buf);
126 break;
127 }
128 }
129 }
130
131 pub(crate) fn for_each(&self, f: impl FnMut(&TI::Key, SubsetRef)) {
132 self.table.for_each(f);
133 }
134
135 pub(crate) fn len(&self) -> usize {
136 self.table.len()
137 }
138}
139
140pub(crate) struct SubsetTable {
141 keys: RowBuffer,
142 hash: Pooled<HashTable<TableEntry<BufferedSubset>>>,
143}
144
145impl Clone for SubsetTable {
146 fn clone(&self) -> Self {
147 SubsetTable {
148 keys: self.keys.clone(),
149 hash: Pooled::cloned(&self.hash),
150 }
151 }
152}
153
154impl SubsetTable {
155 fn new(key_arity: usize) -> SubsetTable {
156 SubsetTable {
157 keys: RowBuffer::new(key_arity),
158 hash: with_pool_set(|ps| ps.get()),
159 }
160 }
161}
162
163pub(crate) trait IndexBase {
164 type Key: ?Sized;
168
169 type WriteKey: ?Sized;
173
174 fn clear(&mut self);
176 fn get_subset(&self, key: &Self::Key) -> Option<SubsetRef<'_>>;
178 fn add_row(&mut self, key: &Self::WriteKey, row: RowId);
180 fn merge_rows(&mut self, buf: &TaggedRowBuffer);
182 fn for_each(&self, f: impl FnMut(&Self::Key, SubsetRef));
184 fn len(&self) -> usize;
186
187 fn merge_parallel(&mut self, cols: &[ColumnId], table: WrappedTableRef, subset: SubsetRef);
188
189 fn rebuild_full(&mut self, cols: &[ColumnId], table: WrappedTableRef, subset: SubsetRef) {
193 let mut buf = TaggedRowBuffer::new(cols.len());
194 let mut cur = Offset::new(0);
195 loop {
196 buf.clear();
197 if let Some(next) = table.scan_project(subset, cols, cur, 1024, &[], &mut buf) {
198 cur = next;
199 self.merge_rows(&buf);
200 } else {
201 self.merge_rows(&buf);
202 break;
203 }
204 }
205 }
206}
207
208struct ColumnIndexShard {
209 table: Pooled<IndexMap<Value, BufferedSubset>>,
212 subsets: SubsetBuffer,
213}
214
215impl Clone for ColumnIndexShard {
216 fn clone(&self) -> Self {
217 ColumnIndexShard {
218 table: Pooled::cloned(&self.table),
219 subsets: self.subsets.clone(),
220 }
221 }
222}
223
224#[derive(Clone)]
225pub struct ColumnIndex {
226 shard_data: ShardData,
228 shards: IdVec<ShardId, ColumnIndexShard>,
229}
230
231impl IndexBase for ColumnIndex {
232 type Key = Value;
233 type WriteKey = [Value];
234 fn clear(&mut self) {
235 for (_, shard) in self.shards.iter_mut() {
236 for (_, subset) in shard.table.drain(..) {
237 match subset {
238 BufferedSubset::Dense(_) => {}
239 BufferedSubset::Sparse(buffered_vec) => {
240 shard.subsets.return_vec(buffered_vec);
241 }
242 }
243 }
244 }
245 }
246
247 fn get_subset<'a>(&'a self, key: &Value) -> Option<SubsetRef<'a>> {
248 let shard = self.shard_data.get_shard(key, &self.shards);
249 shard.table.get(key).map(|x| x.as_ref(&shard.subsets))
250 }
251 fn add_row(&mut self, vals: &[Value], row: RowId) {
252 for (i, key) in vals.iter().enumerate() {
253 if vals[..i].contains(key) {
255 continue;
256 }
257 let shard = self.shard_data.get_shard_mut(key, &mut self.shards);
258 unsafe {
260 shard
261 .table
262 .entry(*key)
263 .or_insert_with(BufferedSubset::empty)
264 .add_row_sorted(row, &mut shard.subsets);
265 }
266 }
267 }
268 fn merge_rows(&mut self, buf: &TaggedRowBuffer) {
269 for (src_id, key) in buf.iter() {
270 self.add_row(key, src_id);
271 }
272 }
273
274 fn for_each(&self, mut f: impl FnMut(&Self::Key, SubsetRef)) {
275 for (subsets, (k, v)) in self
276 .shards
277 .iter()
278 .flat_map(|(_, shard)| shard.table.iter().map(|x| (&shard.subsets, x)))
279 {
280 f(k, v.as_ref(subsets));
281 }
282 }
283
284 fn len(&self) -> usize {
285 self.shards.iter().map(|(_, shard)| shard.table.len()).sum()
286 }
287
288 fn merge_parallel(&mut self, cols: &[ColumnId], table: WrappedTableRef, subset: SubsetRef) {
289 const BATCH_SIZE: usize = 1024;
290 let shard_data = self.shard_data;
291 let mut queues = IdVec::<ShardId, Mutex<Vec<(RowId, TaggedRowBuffer)>>>::with_capacity(
292 shard_data.n_shards(),
293 );
294 queues.resize_with(shard_data.n_shards(), || {
295 Mutex::new(Vec::with_capacity((subset.size() / BATCH_SIZE) + 1))
296 });
297 let split_buf = |buf: TaggedRowBuffer| {
298 let mut split = IdVec::<ShardId, TaggedRowBuffer>::default();
299 split.resize_with(shard_data.n_shards(), || TaggedRowBuffer::new(1));
300 for (row_id, keys) in buf.iter() {
301 for (i, key) in keys.iter().enumerate() {
302 if keys[..i].contains(key) {
305 continue;
306 }
307 shard_data
308 .get_shard_mut(*key, &mut split)
309 .add_row(row_id, &[*key]);
310 }
311 }
312 for (shard_id, buf) in split.drain() {
313 if buf.is_empty() {
314 continue;
315 }
316 let first = buf.get_row(RowId::new(0)).0;
317 queues[shard_id].lock().unwrap().push((first, buf));
318 }
319 };
320
321 run_in_index_thread_pool(|| {
322 egglog_concurrency::scope(|inner| {
323 let mut cur = Offset::new(0);
324 loop {
325 let mut buf = TaggedRowBuffer::new(cols.len());
326 if let Some(next) =
327 table.scan_project(subset, cols, cur, BATCH_SIZE, &[], &mut buf)
328 {
329 cur = next;
330 inner.spawn(move |_| split_buf(buf));
331 } else {
332 inner.spawn(move |_| split_buf(buf));
333 break;
334 }
335 }
336 });
337
338 parallel::for_each_id_vec_mut(&mut self.shards, |shard_id, shard| {
339 let mut vec = queues[shard_id].lock().unwrap();
341 vec.sort_by_key(|(start, _)| *start);
342 for (_, buf) in vec.drain(..) {
343 for (row_id, key) in buf.iter() {
344 debug_assert_eq!(key.len(), 1);
345 match shard.table.entry(key[0]) {
346 Entry::Occupied(mut occ) => {
347 unsafe {
349 occ.get_mut().add_row_sorted(row_id, &mut shard.subsets);
350 }
351 }
352 Entry::Vacant(v) => {
353 v.insert(BufferedSubset::singleton(row_id));
354 }
355 }
356 }
357 }
358 });
359 });
360 }
361
362 fn rebuild_full(&mut self, cols: &[ColumnId], table: WrappedTableRef, subset: SubsetRef) {
369 let rows = subset.size();
373 let mut pairs: Vec<(Value, RowId)> = Vec::with_capacity(rows * cols.len());
374 let mut bounds: SmallVec<[usize; 8]> = SmallVec::new();
375 bounds.push(0);
376 for &col in cols {
377 table.collect_col_pairs(subset, col, &mut pairs);
378 bounds.push(pairs.len());
379 }
380
381 let mut scratch: Vec<(Value, RowId)> =
384 vec![(Value::new_const(0), RowId::new_const(0)); rows];
385 for b in 0..cols.len() {
386 radix_sort_slice_by_value(&mut pairs[bounds[b]..bounds[b + 1]], &mut scratch);
387 }
388
389 if cols.len() == 1 {
390 self.build_subsets_from_sorted(&pairs);
393 return;
394 }
395
396 let merged = merge_sorted_blocks_dedup(pairs, &bounds);
403 self.build_subsets_from_sorted(&merged);
404 }
405}
406
407fn radix_passes_for(max: u32) -> u32 {
409 if max < 256 {
410 1
411 } else if max < 65_536 {
412 2
413 } else if max < (1 << 24) {
414 3
415 } else {
416 4
417 }
418}
419
420pub(crate) fn radix_sort_slice_by_value(
427 data: &mut [(Value, RowId)],
428 scratch: &mut [(Value, RowId)],
429) {
430 let n = data.len();
431 if n < 64 {
432 data.sort_unstable();
433 return;
434 }
435
436 let mut max_val = 0u32;
440 let mut sorted = true;
441 let mut prev = 0u32;
442 for &(v, _) in data.iter() {
443 let rep = v.rep();
444 max_val = max_val.max(rep);
445 sorted &= prev <= rep;
446 prev = rep;
447 }
448 if sorted {
449 return;
450 }
451 let n_passes = radix_passes_for(max_val);
452
453 let mut src: &mut [(Value, RowId)] = data;
454 let mut dst: &mut [(Value, RowId)] = &mut scratch[..n];
455
456 for pass in 0..n_passes {
457 let shift = pass * 8;
458 let mut count = [0u32; 256];
459
460 for pair in src.iter() {
462 let bucket = (pair.0.rep() >> shift) & 0xFF;
463 count[bucket as usize] += 1;
464 }
465
466 let mut prefix = 0u32;
468 for c in &mut count {
469 let prev = *c;
470 *c = prefix;
471 prefix += prev;
472 }
473
474 for &pair in src.iter() {
476 let bucket = ((pair.0.rep() >> shift) & 0xFF) as usize;
477 dst[count[bucket] as usize] = pair;
478 count[bucket] += 1;
479 }
480
481 core::mem::swap(&mut src, &mut dst);
482 }
483
484 if n_passes % 2 == 1 {
487 dst.copy_from_slice(src);
488 }
489}
490
491fn merge2_into(a: &[(Value, RowId)], b: &[(Value, RowId)], out: &mut Vec<(Value, RowId)>) {
498 let start = out.len();
499 let push = |out: &mut Vec<(Value, RowId)>, next: (Value, RowId)| {
500 if out.len() == start || *out.last().unwrap() != next {
501 out.push(next);
502 }
503 };
504 let (mut i, mut j) = (0, 0);
505 while i < a.len() && j < b.len() {
506 if a[i] <= b[j] {
507 push(out, a[i]);
508 i += 1;
509 } else {
510 push(out, b[j]);
511 j += 1;
512 }
513 }
514 for &next in &a[i..] {
515 push(out, next);
516 }
517 for &next in &b[j..] {
518 push(out, next);
519 }
520}
521
522fn merge_sorted_blocks_dedup(
535 mut src: Vec<(Value, RowId)>,
536 bounds: &[usize],
537) -> Vec<(Value, RowId)> {
538 let n = src.len();
539 debug_assert!(bounds.len() >= 2);
540
541 let mut dst: Vec<(Value, RowId)> = Vec::with_capacity(n);
543 let mut cur: SmallVec<[usize; 8]> = bounds.iter().copied().collect();
544
545 while cur.len() > 2 {
547 dst.clear();
548 let mut next: SmallVec<[usize; 8]> = SmallVec::new();
549 next.push(0);
550 let runs = cur.len() - 1;
551 let mut r = 0;
552 while r < runs {
553 if r + 1 < runs {
554 merge2_into(
555 &src[cur[r]..cur[r + 1]],
556 &src[cur[r + 1]..cur[r + 2]],
557 &mut dst,
558 );
559 r += 2;
560 } else {
561 dst.extend_from_slice(&src[cur[r]..cur[r + 1]]);
563 r += 1;
564 }
565 next.push(dst.len());
566 }
567 mem::swap(&mut src, &mut dst);
568 cur = next;
569 }
570
571 src.truncate(cur[1]);
573 src
574}
575
576impl ColumnIndex {
577 pub(crate) fn new() -> ColumnIndex {
578 with_pool_set(|ps| {
579 let shard_data = ShardData::new(num_shards());
580 let mut shards = IdVec::with_capacity(shard_data.n_shards());
581 shards.resize_with(shard_data.n_shards(), || ColumnIndexShard {
582 table: ps.get(),
583 subsets: SubsetBuffer::default(),
584 });
585 ColumnIndex { shard_data, shards }
586 })
587 }
588
589 fn build_subsets_from_sorted(&mut self, pairs: &[(Value, RowId)]) {
593 let mut i = 0;
594 while i < pairs.len() {
595 let key = pairs[i].0;
596 let start = i;
597 let mut first = pairs[i].1;
598 let mut last = pairs[i].1;
599 while i < pairs.len() && pairs[i].0 == key {
600 last = cmp::max(last, pairs[i].1);
601 first = cmp::min(first, pairs[i].1);
602 i += 1;
603 }
604 let shard = self.shard_data.get_shard_mut(key, &mut self.shards);
605 let count = i - start;
606 let buffered = if last.rep() - first.rep() == (count - 1) as u32 {
607 BufferedSubset::Dense(OffsetRange::new(first, last.inc()))
610 } else {
611 let bv = shard
612 .subsets
613 .new_vec(pairs[start..i].iter().map(|&(_, r)| r));
614 BufferedSubset::Sparse(bv)
615 };
616 shard.table.insert(key, buffered);
617 }
618 }
619}
620
621#[derive(Clone)]
622struct TupleIndexShard {
623 table: SubsetTable,
624 subsets: SubsetBuffer,
625}
626
627#[derive(Clone)]
629pub struct TupleIndex {
630 shard_data: ShardData,
633 shards: IdVec<ShardId, TupleIndexShard>,
634}
635
636impl TupleIndex {
637 pub(crate) fn new(key_arity: usize) -> TupleIndex {
638 let shard_data = ShardData::new(num_shards());
639 let mut shards = IdVec::with_capacity(shard_data.n_shards());
640 shards.resize_with(shard_data.n_shards(), || TupleIndexShard {
641 table: SubsetTable::new(key_arity),
642 subsets: SubsetBuffer::default(),
643 });
644 TupleIndex { shard_data, shards }
645 }
646}
647
648impl IndexBase for TupleIndex {
649 type Key = [Value];
650 type WriteKey = Self::Key;
651
652 fn clear(&mut self) {
653 for (_, shard) in self.shards.iter_mut() {
654 shard.table.keys.clear();
655 for entry in shard.table.hash.drain() {
656 match entry.vals {
657 BufferedSubset::Dense(_) => {}
658 BufferedSubset::Sparse(v) => {
659 shard.subsets.return_vec(v);
660 }
661 }
662 }
663 }
664 }
665
666 fn get_subset<'a>(&'a self, key: &[Value]) -> Option<SubsetRef<'a>> {
667 let hash = hash_key(key);
668 let shard = &self.shards[self.shard_data.shard_id(hash)];
669 let entry = shard.table.hash.find(hash, |entry| {
670 entry.hash == hash && unsafe { shard.table.keys.get_row_unchecked(entry.key) } == key
672 })?;
673 Some(entry.vals.as_ref(&shard.subsets))
674 }
675
676 fn add_row(&mut self, key: &[Value], row: RowId) {
677 use hashbrown::hash_table::Entry;
678 let hash = hash_key(key);
679 let shard = &mut self.shards[self.shard_data.shard_id(hash)];
680 let table_entry = shard.table.hash.entry(
681 hash,
682 |entry| {
684 entry.hash == hash
685 && unsafe { shard.table.keys.get_row_unchecked(entry.key) } == key
686 },
687 |ent| ent.hash,
688 );
689 match table_entry {
690 Entry::Occupied(mut occ) => {
691 unsafe {
693 occ.get_mut().vals.add_row_sorted(row, &mut shard.subsets);
694 }
695 }
696 Entry::Vacant(v) => {
697 let key_id = shard.table.keys.add_row(key);
698 let subset = BufferedSubset::singleton(row);
699 v.insert(TableEntry {
700 hash,
701 key: key_id,
702 vals: subset,
703 });
704 }
705 }
706 }
707
708 fn merge_rows(&mut self, buf: &TaggedRowBuffer) {
709 for (src_id, key) in buf.iter() {
710 self.add_row(key, src_id);
711 }
712 }
713 fn for_each(&self, mut f: impl FnMut(&Self::Key, SubsetRef)) {
714 for (_, shard) in self.shards.iter() {
715 for entry in shard.table.hash.iter() {
716 let key = unsafe { shard.table.keys.get_row_unchecked(entry.key) };
718 f(key, entry.vals.as_ref(&shard.subsets));
719 }
720 }
721 }
722
723 fn len(&self) -> usize {
724 self.shards
725 .iter()
726 .map(|(_, shard)| shard.table.hash.len())
727 .sum()
728 }
729
730 fn merge_parallel(&mut self, cols: &[ColumnId], table: WrappedTableRef, subset: SubsetRef) {
731 const BATCH_SIZE: usize = 1024;
735 let shard_data = self.shard_data;
736 let mut queues = IdVec::<ShardId, Mutex<Vec<(RowId, TaggedRowBuffer)>>>::with_capacity(
737 shard_data.n_shards(),
738 );
739 queues.resize_with(shard_data.n_shards(), || {
740 Mutex::new(Vec::with_capacity((subset.size() / BATCH_SIZE) + 1))
741 });
742 let split_buf = |buf: TaggedRowBuffer| {
743 let mut split = IdVec::<ShardId, TaggedRowBuffer>::default();
744 split.resize_with(shard_data.n_shards(), || TaggedRowBuffer::new(cols.len()));
745 for (row_id, key) in buf.iter() {
746 shard_data
747 .get_shard_mut(key, &mut split)
748 .add_row(row_id, key);
749 }
750 for (shard_id, buf) in split.drain() {
751 if buf.is_empty() {
752 continue;
753 }
754 let first = buf.get_row(RowId::new(0)).0;
755 queues[shard_id].lock().unwrap().push((first, buf));
756 }
757 };
758 run_in_index_thread_pool(|| {
759 egglog_concurrency::scope(|scope| {
760 let mut cur = Offset::new(0);
761 loop {
762 let mut buf = TaggedRowBuffer::new(cols.len());
763 if let Some(next) =
764 table.scan_project(subset, cols, cur, BATCH_SIZE, &[], &mut buf)
765 {
766 cur = next;
767 scope.spawn(move |_| split_buf(buf));
768 } else {
769 scope.spawn(move |_| split_buf(buf));
770 break;
771 }
772 }
773 });
774 parallel::for_each_id_vec_mut(&mut self.shards, |shard_id, shard| {
775 use hashbrown::hash_table::Entry;
776 let mut vec = queues[shard_id].lock().unwrap();
778 vec.sort_by_key(|(start, _)| *start);
779 for (_, buf) in vec.drain(..) {
780 for (row_id, key) in buf.iter() {
781 let hash = hash_key(key);
782 let table_entry = shard.table.hash.entry(
783 hash,
784 |entry| {
786 entry.hash == hash
787 && unsafe { shard.table.keys.get_row_unchecked(entry.key) }
788 == key
789 },
790 |ent| ent.hash,
791 );
792 match table_entry {
793 Entry::Occupied(mut occ) => {
794 unsafe {
796 occ.get_mut()
797 .vals
798 .add_row_sorted(row_id, &mut shard.subsets);
799 }
800 }
801 Entry::Vacant(v) => {
802 let key_id = shard.table.keys.add_row(key);
803 let subset = BufferedSubset::singleton(row_id);
804 v.insert(TableEntry {
805 hash,
806 key: key_id,
807 vals: subset,
808 });
809 }
810 }
811 }
812 }
813 });
814 });
815 }
816}
817
818fn hash_key(key: &[Value]) -> u64 {
819 let mut hasher = FxHasher::default();
820 key.hash(&mut hasher);
821 hasher.finish()
822}
823
824#[derive(Default)]
831pub struct IndexCatalog<K: Clone + std::hash::Hash + Eq, I: Clone> {
832 data: ReadOptimizedLock<Vec<(K, I)>>,
833}
834
835impl<K, I: Clone> IndexCatalog<K, I>
836where
837 K: Clone + std::hash::Hash + Eq,
838{
839 pub fn new() -> Self {
840 IndexCatalog {
841 data: ReadOptimizedLock::new(Vec::new()),
842 }
843 }
844
845 pub fn map(&self, f: impl Fn(&(K, I)) -> (K, I)) -> Self {
846 let vec = self.data.read().iter().map(f).collect();
847 IndexCatalog {
848 data: ReadOptimizedLock::new(vec),
849 }
850 }
851
852 pub fn update(&mut self, f: impl Fn(&K, &mut I)) {
853 for (k, i) in self.data.as_mut_ref() {
854 f(k, i)
855 }
856 }
857
858 pub fn get_or_insert(&self, k: K, init: impl FnOnce() -> I) -> I {
859 let data = self.data.read();
860 let entry = data.iter().find(|(k1, _)| k1 == &k);
861 if let Some(entry) = entry {
862 entry.1.clone()
863 } else {
864 drop(data);
865 let mut data = self.data.lock();
866 if let Some(entry) = data.iter().find(|(k1, _)| k1 == &k) {
867 entry.1.clone()
868 } else {
869 let index = init();
870 data.push((k, index.clone()));
871 index
872 }
873 }
874 }
875}
876
877define_id!(BufferIndex, u32, "an index into a subset buffer");
878
879struct SubsetBuffer {
889 buf: Pooled<Vec<RowId>>,
890 free_list: FreeList,
891}
892
893impl Clone for SubsetBuffer {
894 fn clone(&self) -> Self {
895 SubsetBuffer {
896 buf: Pooled::cloned(&self.buf),
897 free_list: self.free_list.clone(),
898 }
899 }
900}
901
902impl Default for SubsetBuffer {
903 fn default() -> SubsetBuffer {
904 with_pool_set(|ps| SubsetBuffer {
905 buf: ps.get(),
906 free_list: Default::default(),
907 })
908 }
909}
910
911impl SubsetBuffer {
912 fn new_vec(&mut self, rows: impl ExactSizeIterator<Item = RowId>) -> BufferedVec {
913 let len = rows.len();
914 if let Some(v) = self.free_list.get_size_class(len).pop() {
915 return self.fill_at(v, rows);
916 }
917 let start = BufferIndex::from_usize(self.buf.len());
918 self.buf.resize(
919 start.index() + len.next_power_of_two(),
920 RowId::new(u32::MAX),
921 );
922 self.fill_at(start, rows)
923 }
924
925 fn fill_at(
926 &mut self,
927 start: BufferIndex,
928 rows: impl ExactSizeIterator<Item = RowId>,
929 ) -> BufferedVec {
930 let mut cur = start;
931 for i in rows {
932 self.buf[cur.index()] = i;
933 cur = cur.inc();
934 }
935 BufferedVec(start, cur)
936 }
937
938 fn return_vec(&mut self, vec: BufferedVec) {
939 self.free_list.get_size_class(vec.len()).push(vec.0);
940 }
941
942 fn push_vec(&mut self, vec: BufferedVec, row: RowId) -> BufferedVec {
943 debug_assert!(
944 vec.is_empty() || self.buf[vec.1.index() - 1] <= row,
945 "vec={vec:?}, row={row:?}, last_elt={:?}",
946 self.buf[vec.1.index() - 1]
947 );
948 if !vec.len().is_power_of_two() {
949 self.buf[vec.1.index()] = row;
950 return BufferedVec(vec.0, vec.1.inc());
951 }
952
953 let res = if let Some(v) = self.free_list.get_size_class(vec.len() + 1).pop() {
954 self.buf
955 .copy_within(vec.0.index()..vec.1.index(), v.index());
956 self.buf[v.index() + vec.len()] = row;
957 BufferedVec(v, BufferIndex::from_usize(v.index() + vec.len() + 1))
958 } else {
959 let start = self.buf.len();
960 self.buf.resize(
961 start + (vec.len() + 1).next_power_of_two(),
962 RowId::new(u32::MAX),
963 );
964 self.buf.copy_within(vec.0.index()..vec.1.index(), start);
965 self.buf[start + vec.len()] = row;
966 let end = start + vec.len() + 1;
967 BufferedVec(BufferIndex::from_usize(start), BufferIndex::from_usize(end))
968 };
969 self.return_vec(vec);
970 res
971 }
972
973 fn make_ref<'a>(&'a self, vec: &BufferedVec) -> SubsetRef<'a> {
974 let res = SubsetRef::Sparse(unsafe {
979 SortedOffsetSlice::new_unchecked(&self.buf[vec.0.index()..vec.1.index()])
980 });
981 #[cfg(debug_assertions)]
982 {
983 use crate::offsets::Offsets;
984 res.offsets(|x| assert_ne!(x.rep(), u32::MAX))
985 }
986 res
987 }
988}
989
990#[derive(Debug, Clone)]
1000pub(crate) struct BufferedVec(BufferIndex, BufferIndex);
1001
1002impl Default for BufferedVec {
1003 fn default() -> Self {
1004 BufferedVec(BufferIndex::new(0), BufferIndex::new(0))
1005 }
1006}
1007
1008impl BufferedVec {
1009 fn is_empty(&self) -> bool {
1010 self.0 == self.1
1011 }
1012 fn len(&self) -> usize {
1013 self.1.index() - self.0.index()
1014 }
1015}
1016
1017#[derive(Clone)]
1018pub(crate) enum BufferedSubset {
1019 Dense(OffsetRange),
1020 Sparse(BufferedVec),
1021}
1022
1023impl BufferedSubset {
1024 unsafe fn add_row_sorted(&mut self, row: RowId, buf: &mut SubsetBuffer) {
1026 match self {
1027 BufferedSubset::Dense(range) => {
1028 if range.end == range.start {
1029 range.start = row;
1030 range.end = row.inc();
1031 return;
1032 }
1033 if range.end == row {
1034 range.end = row.inc();
1035 return;
1036 }
1037 let mut v = buf.new_vec((range.start.rep()..range.end.rep()).map(RowId::new));
1038 v = buf.push_vec(v, row);
1039 *self = BufferedSubset::Sparse(v);
1040 }
1041 BufferedSubset::Sparse(vec) => *vec = buf.push_vec(mem::take(vec), row),
1042 }
1043 }
1044
1045 fn empty() -> Self {
1046 BufferedSubset::Dense(OffsetRange::new(RowId::new(0), RowId::new(0)))
1047 }
1048
1049 fn singleton(row: RowId) -> Self {
1050 BufferedSubset::Dense(OffsetRange::new(row, row.inc()))
1051 }
1052
1053 fn as_ref<'a>(&self, buf: &'a SubsetBuffer) -> SubsetRef<'a> {
1054 match self {
1055 BufferedSubset::Dense(range) => SubsetRef::Dense(*range),
1056 BufferedSubset::Sparse(vec) => buf.make_ref(vec),
1057 }
1058 }
1059}
1060
1061fn num_shards() -> usize {
1062 let n_threads = parallel::current_num_threads();
1063 if n_threads == 1 { 1 } else { n_threads * 2 }
1064}
1065
1066static INDEX_THREAD_POOL: Lazy<ThreadPool> =
1072 Lazy::new(|| ThreadPool::new(parallel::current_num_threads().max(1)));
1073
1074fn run_in_index_thread_pool<R>(f: impl FnOnce() -> R) -> R {
1075 INDEX_THREAD_POOL.install(f)
1076}
1077
1078#[derive(Clone, Default)]
1087pub(super) struct FreeList {
1088 data: [Vec<BufferIndex>; 32],
1089}
1090
1091impl FreeList {
1092 fn get_size_class(&mut self, size: usize) -> &mut Vec<BufferIndex> {
1093 let size_class = size.next_power_of_two();
1094 let idx = size_class.trailing_zeros() as usize;
1095 &mut self.data[idx]
1096 }
1097}