1#![forbid(unsafe_code)]
2#![deny(clippy::all)]
3
4use std::cell::RefCell;
20
21const INITIAL_CHUNK_SIZE: usize = 8 * 1024;
28
29const MAX_CHUNK_SIZE: usize = 1024 * 1024;
31
32const MAX_POOL_SIZE: usize = 4;
34
35const SHRINK_THRESHOLD: usize = 64 * 1024;
37
38pub struct Arena {
55 chunks: Vec<Vec<u8>>,
56 prefix_sums: Vec<usize>,
59 current: usize,
60 offset: usize,
61}
62
63impl Arena {
64 pub fn new() -> Self {
66 let chunk = Vec::with_capacity(INITIAL_CHUNK_SIZE);
67 Self {
68 chunks: vec![chunk],
69 prefix_sums: vec![0],
70 current: 0,
71 offset: 0,
72 }
73 }
74
75 pub fn empty() -> Self {
80 Self {
81 chunks: Vec::new(),
82 prefix_sums: Vec::new(),
83 current: 0,
84 offset: 0,
85 }
86 }
87
88 pub fn alloc(&mut self, len: usize) -> &mut [u8] {
92 if len == 0 {
93 return &mut [];
94 }
95
96 self.ensure_capacity(len);
97
98 let chunk = &mut self.chunks[self.current];
99 let start = self.offset;
100 let new_len = start + len;
101
102 if new_len > chunk.len() {
107 chunk.resize(new_len, 0);
108 }
109
110 self.offset = new_len;
111 &mut chunk[start..new_len]
112 }
113
114 #[inline(always)]
118 pub fn alloc_copy(&mut self, data: &[u8]) -> usize {
119 let len = data.len();
120 if len == 0 {
121 return self.global_offset();
122 }
123
124 let chunk = &mut self.chunks[self.current];
127 let remaining = chunk.capacity() - self.offset;
128 if remaining >= len {
129 let start = self.offset;
130 if start == chunk.len() {
134 chunk.extend_from_slice(data);
135 } else {
136 let new_len = start + len;
137 if new_len > chunk.len() {
138 chunk.resize(new_len, 0);
139 }
140 chunk[start..start + len].copy_from_slice(data);
141 }
142 let global = self.prefix_sums[self.current] + start;
143 self.offset = start + len;
144 return global;
145 }
146
147 self.alloc_copy_slow(data)
149 }
150
151 #[cold]
153 #[inline(never)]
154 fn alloc_copy_slow(&mut self, data: &[u8]) -> usize {
155 self.ensure_capacity(data.len());
156
157 let chunk = &mut self.chunks[self.current];
158 let start = self.offset;
159 chunk.extend_from_slice(data);
160
161 let global = self.prefix_sums[self.current] + start;
162 self.offset = start + data.len();
163 global
164 }
165
166 pub fn get(&self, global_offset: usize, len: usize) -> &[u8] {
172 if len == 0 {
173 return &[];
174 }
175
176 let (chunk_idx, local_offset) = self.resolve_offset(global_offset);
177 &self.chunks[chunk_idx][local_offset..local_offset + len]
178 }
179
180 pub fn get_str(&self, global_offset: usize, len: usize) -> Option<&str> {
184 if len == 0 {
185 return Some("");
186 }
187 simdutf8::basic::from_utf8(self.get(global_offset, len)).ok()
188 }
189
190 pub fn reset(&mut self) {
194 self.chunks.retain(|c| c.capacity() <= SHRINK_THRESHOLD);
196
197 if self.chunks.is_empty() {
198 self.chunks.push(Vec::with_capacity(INITIAL_CHUNK_SIZE));
199 }
200
201 for chunk in &mut self.chunks {
203 chunk.clear();
204 }
205
206 self.rebuild_prefix_sums();
208
209 self.current = 0;
210 self.offset = 0;
211 }
212
213 pub fn allocated(&self) -> usize {
215 let mut total = 0;
216 for (i, chunk) in self.chunks.iter().enumerate() {
217 if i < self.current {
218 total += chunk.len();
219 } else if i == self.current {
220 total += self.offset;
221 }
222 }
223 total
224 }
225
226 pub fn capacity(&self) -> usize {
228 self.chunks.iter().map(|c| c.capacity()).sum()
229 }
230
231 fn ensure_capacity(&mut self, len: usize) {
235 let chunk = &self.chunks[self.current];
236 let remaining = chunk.capacity().saturating_sub(self.offset);
237
238 if remaining >= len {
239 return;
240 }
241
242 let prev_cap = chunk.capacity();
244 let new_cap = prev_cap
245 .saturating_mul(2)
246 .max(len)
247 .max(INITIAL_CHUNK_SIZE)
248 .min(MAX_CHUNK_SIZE.max(len)); let next_idx = self.current + 1;
252 if next_idx < self.chunks.len() && self.chunks[next_idx].capacity() >= len {
253 self.current = next_idx;
254 self.offset = 0;
255 return;
256 }
257
258 let new_chunk = Vec::with_capacity(new_cap);
260 let prefix = self.prefix_sums[self.chunks.len() - 1]
261 + self.chunks.last().map_or(0, |c| c.capacity());
262 if next_idx < self.chunks.len() {
263 self.chunks[next_idx] = new_chunk;
264 self.rebuild_prefix_sums();
266 } else {
267 self.chunks.push(new_chunk);
268 self.prefix_sums.push(prefix);
269 }
270 self.current = next_idx;
271 self.offset = 0;
272 }
273
274 fn rebuild_prefix_sums(&mut self) {
276 self.prefix_sums.clear();
277 let mut sum = 0;
278 for chunk in &self.chunks {
279 self.prefix_sums.push(sum);
280 sum += chunk.capacity();
281 }
282 }
283
284 pub fn global_offset(&self) -> usize {
286 self.global_offset_at(self.current, self.offset)
287 }
288
289 fn global_offset_at(&self, chunk_idx: usize, local_offset: usize) -> usize {
292 self.prefix_sums[chunk_idx] + local_offset
293 }
294
295 fn resolve_offset(&self, global_offset: usize) -> (usize, usize) {
298 if self.chunks.len() == 1 {
300 debug_assert!(
301 global_offset < self.chunks[0].capacity(),
302 "arena offset {global_offset} out of bounds in single chunk (cap={})",
303 self.chunks[0].capacity()
304 );
305 return (0, global_offset);
306 }
307
308 let idx = match self.prefix_sums.binary_search(&global_offset) {
310 Ok(i) => i,
311 Err(0) => 0, Err(i) => i - 1,
313 };
314 let local = global_offset - self.prefix_sums[idx];
315 debug_assert!(
316 local < self.chunks[idx].capacity(),
317 "arena offset {global_offset} out of bounds in chunk {idx} (cap={})",
318 self.chunks[idx].capacity()
319 );
320 (idx, local)
321 }
322}
323
324impl Default for Arena {
325 fn default() -> Self {
326 Self::new()
327 }
328}
329
330thread_local! {
333 static ARENA_POOL: RefCell<Vec<Arena>> = const { RefCell::new(Vec::new()) };
334}
335
336pub fn acquire_arena() -> Arena {
351 ARENA_POOL
352 .with(|pool| pool.borrow_mut().pop())
353 .unwrap_or_default()
354}
355
356pub fn release_arena(mut arena: Arena) {
361 arena.reset();
362 ARENA_POOL.with(|pool| {
363 let mut pool = pool.borrow_mut();
364 if pool.len() < MAX_POOL_SIZE {
365 pool.push(arena);
366 }
367 });
369}
370
371pub struct ArenaRows<T> {
393 rows: Vec<T>,
394 arena: Arena,
395}
396
397impl<T> ArenaRows<T> {
398 pub fn new(rows: Vec<T>, arena: Arena) -> Self {
404 Self { rows, arena }
405 }
406
407 #[inline]
409 pub fn len(&self) -> usize {
410 self.rows.len()
411 }
412
413 #[inline]
415 pub fn is_empty(&self) -> bool {
416 self.rows.is_empty()
417 }
418
419 #[inline]
421 pub fn get(&self, idx: usize) -> Option<&T> {
422 self.rows.get(idx)
423 }
424
425 #[inline]
427 pub fn iter(&self) -> std::slice::Iter<'_, T> {
428 self.rows.iter()
429 }
430
431 pub fn into_parts(self) -> (Vec<T>, Arena) {
435 (self.rows, self.arena)
436 }
437
438 #[inline]
440 pub fn arena_allocated(&self) -> usize {
441 self.arena.allocated()
442 }
443}
444
445impl<T> std::ops::Deref for ArenaRows<T> {
446 type Target = [T];
447
448 #[inline]
449 fn deref(&self) -> &[T] {
450 &self.rows
451 }
452}
453
454impl<'a, T> IntoIterator for &'a ArenaRows<T> {
455 type Item = &'a T;
456 type IntoIter = std::slice::Iter<'a, T>;
457
458 #[inline]
459 fn into_iter(self) -> Self::IntoIter {
460 self.rows.iter()
461 }
462}
463
464impl<T: std::fmt::Debug> std::fmt::Debug for ArenaRows<T> {
465 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
466 f.debug_struct("ArenaRows")
467 .field("len", &self.rows.len())
468 .field("arena_allocated", &self.arena.allocated())
469 .field("rows", &self.rows)
470 .finish()
471 }
472}
473
474pub struct ValidatedRows<T> {
496 rows: Vec<T>,
497 text_buf: String,
498 blob_arena: Arena,
499}
500
501impl<T> ValidatedRows<T> {
502 pub fn new(rows: Vec<T>, text_buf: String, blob_arena: Arena) -> Self {
505 Self {
506 rows,
507 text_buf,
508 blob_arena,
509 }
510 }
511
512 #[inline]
514 pub fn text(&self) -> &str {
515 &self.text_buf
516 }
517
518 #[inline]
522 pub fn text_slice(&self, start: u32, end: u32) -> &str {
523 &self.text_buf[start as usize..end as usize]
524 }
525
526 #[inline]
528 pub fn blob_slice(&self, offset: u32, len: u32) -> &[u8] {
529 self.blob_arena.get(offset as usize, len as usize)
530 }
531
532 #[inline]
534 pub fn len(&self) -> usize {
535 self.rows.len()
536 }
537
538 #[inline]
540 pub fn is_empty(&self) -> bool {
541 self.rows.is_empty()
542 }
543
544 #[inline]
546 pub fn get_inner(&self, idx: usize) -> Option<&T> {
547 self.rows.get(idx)
548 }
549
550 #[inline]
552 pub fn iter_inner(&self) -> std::slice::Iter<'_, T> {
553 self.rows.iter()
554 }
555
556 #[inline]
558 pub fn text_len(&self) -> usize {
559 self.text_buf.len()
560 }
561
562 #[inline]
564 pub fn blob_allocated(&self) -> usize {
565 self.blob_arena.allocated()
566 }
567
568 #[inline]
570 pub fn arena_allocated(&self) -> usize {
571 self.text_buf.len() + self.blob_arena.allocated()
572 }
573}
574
575impl<T> std::ops::Deref for ValidatedRows<T> {
576 type Target = [T];
577
578 #[inline]
579 fn deref(&self) -> &[T] {
580 &self.rows
581 }
582}
583
584impl<'a, T> IntoIterator for &'a ValidatedRows<T> {
585 type Item = &'a T;
586 type IntoIter = std::slice::Iter<'a, T>;
587
588 #[inline]
589 fn into_iter(self) -> Self::IntoIter {
590 self.rows.iter()
591 }
592}
593
594impl<T: std::fmt::Debug> std::fmt::Debug for ValidatedRows<T> {
595 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
596 f.debug_struct("ValidatedRows")
597 .field("len", &self.rows.len())
598 .field("text_len", &self.text_buf.len())
599 .field("blob_allocated", &self.blob_arena.allocated())
600 .field("rows", &self.rows)
601 .finish()
602 }
603}
604
605#[cfg(test)]
606mod tests {
607 use super::*;
608
609 #[test]
610 fn basic_alloc_and_get() {
611 let mut arena = Arena::new();
612 let offset = arena.alloc_copy(b"hello");
613 assert_eq!(arena.get(offset, 5), b"hello");
614 }
615
616 #[test]
617 fn multiple_allocs() {
618 let mut arena = Arena::new();
619 let o1 = arena.alloc_copy(b"foo");
620 let o2 = arena.alloc_copy(b"bar");
621 let o3 = arena.alloc_copy(b"baz");
622
623 assert_eq!(arena.get(o1, 3), b"foo");
624 assert_eq!(arena.get(o2, 3), b"bar");
625 assert_eq!(arena.get(o3, 3), b"baz");
626 }
627
628 #[test]
629 fn alloc_str_retrieval() {
630 let mut arena = Arena::new();
631 let offset = arena.alloc_copy(b"hello world");
632 assert_eq!(arena.get_str(offset, 11), Some("hello world"));
633 }
634
635 #[test]
636 fn zero_length_alloc() {
637 let mut arena = Arena::new();
638 let offset = arena.alloc_copy(b"");
639 let data = arena.get(offset, 0);
640 assert!(data.is_empty());
641 }
642
643 #[test]
644 fn alloc_returns_zeroed_slice() {
645 let mut arena = Arena::new();
646 let slice = arena.alloc(16);
647 assert!(slice.iter().all(|&b| b == 0));
648 }
649
650 #[test]
651 fn reset_allows_reuse() {
652 let mut arena = Arena::new();
653 let _o1 = arena.alloc_copy(b"before reset");
654 assert_eq!(arena.allocated(), 12);
655
656 arena.reset();
657 assert_eq!(arena.allocated(), 0);
658
659 let o2 = arena.alloc_copy(b"after reset");
660 assert_eq!(arena.get(o2, 11), b"after reset");
661 }
662
663 #[test]
664 fn chunk_growth() {
665 let mut arena = Arena::new();
666
667 let big = vec![0xAA; INITIAL_CHUNK_SIZE + 1];
669 let offset = arena.alloc_copy(&big);
670 assert_eq!(arena.get(offset, big.len())[0], 0xAA);
671 assert!(
672 arena.chunks.len() >= 2,
673 "should have grown to a second chunk"
674 );
675 }
676
677 #[test]
678 fn large_single_alloc() {
679 let mut arena = Arena::new();
680 let data = vec![0x42; 2 * MAX_CHUNK_SIZE];
681 let offset = arena.alloc_copy(&data);
682 let result = arena.get(offset, data.len());
683 assert!(result.iter().all(|&b| b == 0x42));
684 }
685
686 #[test]
687 fn one_hundred_rows_in_one_chunk() {
688 let mut arena = Arena::new();
689 let row_data = b"typical row data, about 50 bytes of text content.";
690
691 let mut offsets = Vec::new();
692 for _ in 0..100 {
693 offsets.push(arena.alloc_copy(row_data));
694 }
695
696 assert_eq!(arena.chunks.len(), 1);
698
699 for &offset in &offsets {
700 assert_eq!(arena.get(offset, row_data.len()), row_data);
701 }
702 }
703
704 #[test]
705 fn reset_discards_oversized_chunks() {
706 let mut arena = Arena::new();
707
708 let big = vec![0xFF; SHRINK_THRESHOLD + 1];
710 arena.alloc_copy(&big);
711
712 let _chunks_before = arena.chunks.len();
713 arena.reset();
714
715 for chunk in &arena.chunks {
717 assert!(
718 chunk.capacity() <= SHRINK_THRESHOLD,
719 "oversized chunk not discarded: capacity={}",
720 chunk.capacity()
721 );
722 }
723 }
724
725 #[test]
726 fn thread_local_pool_acquire_release() {
727 let mut arena = acquire_arena();
728 arena.alloc_copy(b"test data");
729 release_arena(arena);
730
731 let arena2 = acquire_arena();
733 assert_eq!(arena2.allocated(), 0); release_arena(arena2);
735 }
736
737 #[test]
738 fn thread_local_pool_max_size() {
739 for _ in 0..MAX_POOL_SIZE + 2 {
741 let arena = Arena::new();
742 release_arena(arena);
743 }
744
745 ARENA_POOL.with(|pool| {
746 assert!(pool.borrow().len() <= MAX_POOL_SIZE);
747 });
748 }
749
750 #[test]
751 fn capacity_reports_total() {
752 let arena = Arena::new();
753 assert!(arena.capacity() >= INITIAL_CHUNK_SIZE);
754 }
755
756 #[test]
757 fn allocated_tracks_usage() {
758 let mut arena = Arena::new();
759 assert_eq!(arena.allocated(), 0);
760 arena.alloc_copy(b"12345");
761 assert_eq!(arena.allocated(), 5);
762 arena.alloc_copy(b"67890");
763 assert_eq!(arena.allocated(), 10);
764 }
765
766 #[test]
767 fn alloc_at_exact_8kb_boundary() {
768 let mut arena = Arena::new();
769
770 let filler = vec![0xAA; INITIAL_CHUNK_SIZE];
772 let o1 = arena.alloc_copy(&filler);
773 assert_eq!(arena.get(o1, INITIAL_CHUNK_SIZE)[0], 0xAA);
774 assert_eq!(arena.chunks.len(), 1);
775
776 let o2 = arena.alloc_copy(b"x");
778 assert_eq!(arena.get(o2, 1), b"x");
779 assert!(arena.chunks.len() >= 2, "should have grown past 8KB chunk");
780
781 assert_eq!(arena.get(o1, INITIAL_CHUNK_SIZE)[0], 0xAA);
783 assert_eq!(
784 arena.get(o1, INITIAL_CHUNK_SIZE)[INITIAL_CHUNK_SIZE - 1],
785 0xAA
786 );
787 }
788
789 #[test]
790 fn prefix_sums_correct_after_multi_chunk() {
791 let mut arena = Arena::new();
792 let mut offsets = Vec::new();
793
794 for i in 0..4 {
796 let data = vec![i as u8; INITIAL_CHUNK_SIZE + 1];
797 offsets.push((arena.alloc_copy(&data), data.len()));
798 }
799
800 for (idx, &(offset, len)) in offsets.iter().enumerate() {
802 let data = arena.get(offset, len);
803 assert!(data.iter().all(|&b| b == idx as u8));
804 }
805 }
806
807 #[test]
808 fn prefix_sums_correct_after_reset() {
809 let mut arena = Arena::new();
810
811 let big = vec![0xBB; INITIAL_CHUNK_SIZE + 1];
813 arena.alloc_copy(&big);
814 assert!(arena.chunks.len() >= 2);
815
816 arena.reset();
817
818 let o = arena.alloc_copy(b"after reset");
820 assert_eq!(arena.get(o, 11), b"after reset");
821 }
822
823 #[test]
825 fn resolve_offset_zero() {
826 let arena = Arena::new();
827 let (chunk_idx, local) = arena.resolve_offset(0);
828 assert_eq!(chunk_idx, 0);
829 assert_eq!(local, 0);
830 }
831
832 #[test]
834 fn resolve_offset_single_chunk_fast_path() {
835 let mut arena = Arena::new();
836 let o1 = arena.alloc_copy(b"hello");
838 let o2 = arena.alloc_copy(b"world");
839 assert_eq!(arena.chunks.len(), 1, "should be single chunk");
840
841 assert_eq!(arena.get(o1, 5), b"hello");
843 assert_eq!(arena.get(o2, 5), b"world");
844 }
845
846 #[test]
850 fn get_str_invalid_utf8_returns_none() {
851 let mut arena = Arena::new();
852 let offset = arena.alloc_copy(&[0xFF, 0xFE, 0xFD]);
853 assert_eq!(arena.get_str(offset, 3), None);
854 }
855
856 #[test]
858 fn get_str_valid_utf8() {
859 let mut arena = Arena::new();
860 let offset = arena.alloc_copy("hello".as_bytes());
861 assert_eq!(arena.get_str(offset, 5), Some("hello"));
862 }
863
864 #[test]
866 fn get_str_empty_returns_some_empty() {
867 let arena = Arena::new();
868 assert_eq!(arena.get_str(0, 0), Some(""));
869 }
870
871 #[test]
873 #[should_panic]
874 fn get_out_of_bounds_panics() {
875 let arena = Arena::new();
876 arena.get(INITIAL_CHUNK_SIZE + 100, 1);
878 }
879
880 #[test]
882 fn ensure_capacity_reuses_next_chunk() {
883 let mut arena = Arena::new();
884
885 let big = vec![0xAA; INITIAL_CHUNK_SIZE + 1];
887 arena.alloc_copy(&big);
888 assert!(arena.chunks.len() >= 2);
889
890 arena.reset();
892 assert_eq!(arena.current, 0);
893 assert_eq!(arena.offset, 0);
894
895 let filler = vec![0xBB; INITIAL_CHUNK_SIZE];
897 arena.alloc_copy(&filler);
898 let o = arena.alloc_copy(b"reuse check");
900 assert_eq!(arena.get(o, 11), b"reuse check");
901 }
902
903 #[test]
905 fn arena_cross_thread_no_crash() {
906 let mut arena = Arena::new();
909 arena.alloc_copy(b"test data");
910
911 let handle = std::thread::spawn(move || {
912 assert_eq!(arena.get(0, 9), b"test data");
914 arena.reset();
915 arena
916 });
917
918 let arena = handle.join().unwrap();
919 release_arena(arena);
921 }
922
923 #[test]
926 fn arena_rows_basic() {
927 let arena = Arena::new();
928 let ar: ArenaRows<i64> = ArenaRows::new(vec![42], arena);
929 assert_eq!(ar.len(), 1);
930 assert!(!ar.is_empty());
931 assert_eq!(ar[0], 42);
932 assert_eq!(ar.get(0), Some(&42));
933 }
934
935 #[test]
936 fn arena_rows_empty() {
937 let arena = Arena::new();
938 let ar: ArenaRows<i64> = ArenaRows::new(vec![], arena);
939 assert!(ar.is_empty());
940 assert_eq!(ar.len(), 0);
941 assert!(ar.get(0).is_none());
942 }
943
944 #[test]
945 fn arena_rows_iter() {
946 let arena = Arena::new();
947 let ar: ArenaRows<i64> = ArenaRows::new(vec![10, 20, 30], arena);
948 let vals: Vec<&i64> = ar.iter().collect();
949 assert_eq!(vals, vec![&10, &20, &30]);
950 }
951
952 #[test]
953 fn arena_rows_deref() {
954 let arena = Arena::new();
955 let ar: ArenaRows<i64> = ArenaRows::new(vec![1, 2, 3], arena);
956 let slice: &[i64] = &ar;
957 assert_eq!(slice, &[1, 2, 3]);
958 }
959
960 #[test]
961 fn arena_rows_for_loop() {
962 let arena = Arena::new();
963 let ar: ArenaRows<i64> = ArenaRows::new(vec![10, 20], arena);
964 let mut sum = 0;
965 for &val in &ar {
966 sum += val;
967 }
968 assert_eq!(sum, 30);
969 }
970
971 #[test]
972 fn arena_rows_debug() {
973 let arena = Arena::new();
974 let ar: ArenaRows<i64> = ArenaRows::new(vec![42], arena);
975 let dbg = format!("{ar:?}");
976 assert!(dbg.contains("ArenaRows"));
977 assert!(dbg.contains("42"));
978 }
979
980 #[test]
981 fn arena_rows_arena_allocated() {
982 let mut arena = Arena::new();
983 arena.alloc_copy(b"some data");
984 let allocated = arena.allocated();
985 let ar: ArenaRows<i64> = ArenaRows::new(vec![], arena);
986 assert_eq!(ar.arena_allocated(), allocated);
987 }
988
989 #[test]
990 fn arena_rows_into_parts() {
991 let arena = Arena::new();
992 let ar: ArenaRows<i64> = ArenaRows::new(vec![1, 2, 3], arena);
993 let (v, _arena) = ar.into_parts();
994 assert_eq!(v, vec![1, 2, 3]);
995 }
996
997 #[test]
998 fn arena_rows_into_parts_empty() {
999 let arena = Arena::new();
1000 let ar: ArenaRows<i64> = ArenaRows::new(vec![], arena);
1001 let (v, _arena) = ar.into_parts();
1002 assert!(v.is_empty());
1003 }
1004
1005 #[test]
1006 fn arena_rows_get_out_of_bounds() {
1007 let arena = Arena::new();
1008 let ar: ArenaRows<i64> = ArenaRows::new(vec![42], arena);
1009 assert_eq!(ar.get(0), Some(&42));
1010 assert_eq!(ar.get(1), None);
1011 assert_eq!(ar.get(999), None);
1012 }
1013
1014 #[test]
1017 fn validated_rows_basic() {
1018 let text_buf = String::from("alicebob");
1019 let blob_arena = Arena::new();
1020
1021 #[derive(Debug)]
1022 #[allow(dead_code)]
1023 struct Inner {
1024 id: i64,
1025 name_start: u32,
1026 name_end: u32,
1027 }
1028
1029 let rows = vec![
1030 Inner {
1031 id: 1,
1032 name_start: 0,
1033 name_end: 5,
1034 },
1035 Inner {
1036 id: 2,
1037 name_start: 5,
1038 name_end: 8,
1039 },
1040 ];
1041 let vr = ValidatedRows::new(rows, text_buf, blob_arena);
1042
1043 assert_eq!(vr.len(), 2);
1044 assert!(!vr.is_empty());
1045 assert_eq!(vr.text_slice(vr[0].name_start, vr[0].name_end), "alice");
1046 assert_eq!(vr.text_slice(vr[1].name_start, vr[1].name_end), "bob");
1047 }
1048
1049 #[test]
1050 fn validated_rows_empty() {
1051 let vr: ValidatedRows<i64> = ValidatedRows::new(vec![], String::new(), Arena::new());
1052 assert!(vr.is_empty());
1053 assert_eq!(vr.len(), 0);
1054 assert_eq!(vr.text_len(), 0);
1055 }
1056
1057 #[test]
1058 fn validated_rows_blob() {
1059 let mut blob_arena = Arena::new();
1060 let off = blob_arena.alloc_copy(&[0xDE, 0xAD]);
1061
1062 #[derive(Debug)]
1063 struct Inner {
1064 blob_off: u32,
1065 blob_len: u32,
1066 }
1067
1068 let rows = vec![Inner {
1069 blob_off: off as u32,
1070 blob_len: 2,
1071 }];
1072 let vr = ValidatedRows::new(rows, String::new(), blob_arena);
1073
1074 assert_eq!(vr.blob_slice(vr[0].blob_off, vr[0].blob_len), &[0xDE, 0xAD]);
1075 }
1076
1077 #[test]
1078 fn validated_rows_arena_allocated() {
1079 let mut blob_arena = Arena::new();
1080 blob_arena.alloc_copy(&[1, 2, 3]);
1081 let text_buf = String::from("hello");
1082
1083 let vr: ValidatedRows<i64> = ValidatedRows::new(vec![], text_buf, blob_arena);
1084 assert_eq!(vr.arena_allocated(), 5 + 3); }
1086
1087 #[test]
1088 fn validated_rows_debug() {
1089 let vr: ValidatedRows<i64> = ValidatedRows::new(vec![42], String::new(), Arena::new());
1090 let dbg = format!("{vr:?}");
1091 assert!(dbg.contains("ValidatedRows"));
1092 assert!(dbg.contains("42"));
1093 }
1094
1095 #[test]
1096 fn validated_rows_deref() {
1097 let vr: ValidatedRows<i64> = ValidatedRows::new(vec![1, 2, 3], String::new(), Arena::new());
1098 let slice: &[i64] = &vr;
1099 assert_eq!(slice, &[1, 2, 3]);
1100 }
1101
1102 #[test]
1103 fn validated_rows_iter() {
1104 let vr: ValidatedRows<i64> = ValidatedRows::new(vec![10, 20], String::new(), Arena::new());
1105 let mut sum = 0;
1106 for &val in &vr {
1107 sum += val;
1108 }
1109 assert_eq!(sum, 30);
1110 }
1111
1112 #[test]
1115 fn alloc_zero_returns_empty_slice() {
1116 let mut arena = Arena::new();
1117 let slice = arena.alloc(0);
1118 assert!(slice.is_empty());
1119 }
1120
1121 #[test]
1124 fn get_str_zero_len_returns_empty() {
1125 let arena = Arena::new();
1126 assert_eq!(arena.get_str(0, 0), Some(""));
1127 }
1128
1129 #[test]
1134 fn validated_rows_empty_text_buf() {
1135 let vr: ValidatedRows<i64> = ValidatedRows::new(vec![1, 2, 3], String::new(), Arena::new());
1136 assert_eq!(vr.text(), "");
1137 assert_eq!(vr.text_len(), 0);
1138 assert_eq!(vr.len(), 3);
1139 }
1140
1141 #[test]
1142 fn validated_rows_blob_only_no_text() {
1143 let mut blob_arena = Arena::new();
1144 let o1 = blob_arena.alloc_copy(&[0x01, 0x02, 0x03]);
1145 let o2 = blob_arena.alloc_copy(&[0xAA, 0xBB]);
1146
1147 #[derive(Debug)]
1148 struct Inner {
1149 off: u32,
1150 len: u32,
1151 }
1152
1153 let rows = vec![
1154 Inner {
1155 off: o1 as u32,
1156 len: 3,
1157 },
1158 Inner {
1159 off: o2 as u32,
1160 len: 2,
1161 },
1162 ];
1163 let vr = ValidatedRows::new(rows, String::new(), blob_arena);
1164 assert_eq!(vr.text_len(), 0);
1165 assert_eq!(vr.blob_slice(vr[0].off, vr[0].len), &[0x01, 0x02, 0x03]);
1166 assert_eq!(vr.blob_slice(vr[1].off, vr[1].len), &[0xAA, 0xBB]);
1167 }
1168
1169 #[test]
1170 #[should_panic]
1171 fn validated_rows_text_slice_out_of_bounds() {
1172 let vr: ValidatedRows<i64> = ValidatedRows::new(vec![], String::from("hi"), Arena::new());
1173 vr.text_slice(0, 100);
1175 }
1176
1177 #[test]
1178 #[should_panic]
1179 fn validated_rows_blob_slice_out_of_bounds() {
1180 let blob_arena = Arena::new();
1181 let vr: ValidatedRows<i64> = ValidatedRows::new(vec![], String::new(), blob_arena);
1182 vr.blob_slice(0, 100);
1184 }
1185
1186 #[test]
1187 fn validated_rows_large_10k_rows() {
1188 let mut text_buf = String::new();
1189 let blob_arena = Arena::new();
1190
1191 #[derive(Debug)]
1192 struct Inner {
1193 start: u32,
1194 end: u32,
1195 }
1196
1197 let mut rows = Vec::with_capacity(10_000);
1198 for i in 0..10_000u32 {
1199 let start = text_buf.len() as u32;
1200 text_buf.push_str(&format!("row_{i}"));
1201 let end = text_buf.len() as u32;
1202 rows.push(Inner { start, end });
1203 }
1204
1205 let vr = ValidatedRows::new(rows, text_buf, blob_arena);
1206 assert_eq!(vr.len(), 10_000);
1207 assert_eq!(vr.text_slice(vr[0].start, vr[0].end), "row_0");
1208 assert_eq!(vr.text_slice(vr[9999].start, vr[9999].end), "row_9999");
1209 }
1210
1211 #[test]
1212 fn validated_rows_text_slice_empty_range() {
1213 let vr: ValidatedRows<i64> =
1214 ValidatedRows::new(vec![], String::from("hello"), Arena::new());
1215 assert_eq!(vr.text_slice(0, 0), "");
1216 assert_eq!(vr.text_slice(3, 3), "");
1217 }
1218
1219 #[test]
1220 fn validated_rows_get_inner() {
1221 let vr: ValidatedRows<i64> =
1222 ValidatedRows::new(vec![10, 20, 30], String::new(), Arena::new());
1223 assert_eq!(vr.get_inner(0), Some(&10));
1224 assert_eq!(vr.get_inner(1), Some(&20));
1225 assert_eq!(vr.get_inner(2), Some(&30));
1226 assert_eq!(vr.get_inner(3), None);
1227 }
1228
1229 #[test]
1230 fn validated_rows_iter_inner() {
1231 let vr: ValidatedRows<i64> = ValidatedRows::new(vec![5, 10], String::new(), Arena::new());
1232 let vals: Vec<&i64> = vr.iter_inner().collect();
1233 assert_eq!(vals, vec![&5, &10]);
1234 }
1235
1236 #[test]
1237 fn validated_rows_blob_allocated_zero() {
1238 let vr: ValidatedRows<i64> = ValidatedRows::new(vec![], String::new(), Arena::new());
1239 assert_eq!(vr.blob_allocated(), 0);
1240 }
1241
1242 #[test]
1247 fn arena_get_zero_len() {
1248 let arena = Arena::new();
1249 let data = arena.get(0, 0);
1250 assert!(data.is_empty());
1251 }
1252
1253 #[test]
1254 fn arena_alloc_copy_zero_len() {
1255 let mut arena = Arena::new();
1256 let offset = arena.alloc_copy(b"");
1257 assert_eq!(arena.get(offset, 0), &[]);
1258 }
1259
1260 #[test]
1261 fn arena_global_offset_initial() {
1262 let arena = Arena::new();
1263 assert_eq!(arena.global_offset(), 0);
1264 }
1265
1266 #[test]
1267 fn arena_global_offset_advances() {
1268 let mut arena = Arena::new();
1269 arena.alloc_copy(b"12345");
1270 assert_eq!(arena.global_offset(), 5);
1271 arena.alloc_copy(b"67890");
1272 assert_eq!(arena.global_offset(), 10);
1273 }
1274
1275 #[test]
1276 fn arena_multiple_resets() {
1277 let mut arena = Arena::new();
1278 for _ in 0..10 {
1279 arena.alloc_copy(b"data");
1280 assert_eq!(arena.allocated(), 4);
1281 arena.reset();
1282 assert_eq!(arena.allocated(), 0);
1283 }
1284 }
1285
1286 #[test]
1287 fn arena_get_str_unicode() {
1288 let texts = [
1289 "\u{1F600}\u{1F4A9}", "\u{4e16}\u{754c}", "caf\u{00e9}", "\u{1F468}\u{200D}\u{1F469}", ];
1294 for text in &texts {
1295 let mut arena = Arena::new();
1296 let offset = arena.alloc_copy(text.as_bytes());
1297 assert_eq!(
1298 arena.get_str(offset, text.len()),
1299 Some(*text),
1300 "failed for text: {text}"
1301 );
1302 }
1303 }
1304
1305 #[test]
1306 fn arena_get_str_partial_utf8_returns_none() {
1307 let mut arena = Arena::new();
1309 let offset = arena.alloc_copy(&[0xC3]);
1310 assert_eq!(arena.get_str(offset, 1), None);
1311 }
1312
1313 #[test]
1314 fn arena_default_is_new() {
1315 let a1 = Arena::new();
1316 let a2 = Arena::default();
1317 assert_eq!(a1.allocated(), a2.allocated());
1318 assert_eq!(a1.capacity(), a2.capacity());
1319 }
1320
1321 #[test]
1326 fn arena_rows_large() {
1327 let arena = Arena::new();
1328 let rows: Vec<i64> = (0..1000).collect();
1329 let ar = ArenaRows::new(rows, arena);
1330 assert_eq!(ar.len(), 1000);
1331 assert_eq!(ar[0], 0);
1332 assert_eq!(ar[999], 999);
1333 }
1334
1335 #[test]
1336 fn arena_rows_with_arena_data() {
1337 let mut arena = Arena::new();
1338 let offset = arena.alloc_copy(b"stored data");
1339
1340 #[derive(Debug)]
1341 #[allow(dead_code)]
1342 struct Inner {
1343 off: usize,
1344 len: usize,
1345 }
1346
1347 let ar = ArenaRows::new(
1348 vec![Inner {
1349 off: offset,
1350 len: 11,
1351 }],
1352 arena,
1353 );
1354 assert_eq!(ar.len(), 1);
1355 }
1356
1357 #[test]
1362 fn thread_local_pool_acquire_fresh() {
1363 ARENA_POOL.with(|pool| pool.borrow_mut().clear());
1365 let arena = acquire_arena();
1366 assert_eq!(arena.allocated(), 0);
1367 release_arena(arena);
1368 }
1369
1370 #[test]
1371 fn thread_local_pool_recycle_resets() {
1372 let mut arena = Arena::new();
1373 arena.alloc_copy(b"something");
1374 assert!(arena.allocated() > 0);
1375 release_arena(arena);
1376
1377 let arena2 = acquire_arena();
1378 assert_eq!(arena2.allocated(), 0, "recycled arena should be reset");
1379 release_arena(arena2);
1380 }
1381
1382 #[test]
1385 fn arena_reset_clears_data_positions() {
1386 let mut arena = Arena::new();
1387 let o1 = arena.alloc_copy(b"first query data");
1388 assert_eq!(arena.get(o1, 16), b"first query data");
1389
1390 arena.reset();
1391 assert_eq!(arena.allocated(), 0);
1392 assert_eq!(arena.current, 0);
1393 assert_eq!(arena.offset, 0);
1394
1395 let o2 = arena.alloc_copy(b"second query dat");
1398 assert_eq!(o2, 0, "first alloc after reset should be at offset 0");
1399 assert_eq!(arena.get(o2, 16), b"second query dat");
1400 }
1401
1402 #[test]
1403 fn arena_reset_discards_oversized_chunks() {
1404 let mut arena = Arena::new();
1405 let big = vec![0xAA; 128 * 1024];
1407 arena.alloc_copy(&big);
1408 let cap_before = arena.capacity();
1409 assert!(cap_before >= 128 * 1024);
1410
1411 arena.reset();
1412 let cap_after = arena.capacity();
1413 assert!(
1415 cap_after < cap_before,
1416 "oversized chunks should be discarded on reset: before={cap_before}, after={cap_after}"
1417 );
1418 }
1419
1420 #[test]
1423 fn alloc_copy_zero_length_returns_valid_offset() {
1424 let mut arena = Arena::new();
1425 let o1 = arena.alloc_copy(b"");
1426 let o2 = arena.alloc_copy(b"hello");
1427 assert_eq!(o1, o2, "zero-length alloc should not advance offset");
1430 assert_eq!(arena.get(o2, 5), b"hello");
1431 }
1432
1433 #[test]
1436 fn get_zero_length_returns_empty() {
1437 let arena = Arena::new();
1438 assert_eq!(arena.get(0, 0), &[]);
1439 assert_eq!(arena.get(9999, 0), &[]);
1440 }
1441
1442 #[test]
1449 #[should_panic]
1450 fn arena_empty_alloc_copy_panics_without_reset() {
1451 let mut arena = Arena::empty();
1452 assert_eq!(arena.chunks.len(), 0);
1453 let _ = arena.alloc_copy(b"boom");
1455 }
1456
1457 #[test]
1458 #[should_panic]
1459 fn arena_empty_alloc_panics_without_reset() {
1460 let mut arena = Arena::empty();
1461 let _ = arena.alloc(8);
1463 }
1464
1465 #[test]
1466 fn arena_empty_reset_does_not_panic() {
1467 let mut arena = Arena::empty();
1468 arena.reset();
1470 assert!(
1472 !arena.chunks.is_empty(),
1473 "reset should create initial chunk if empty"
1474 );
1475 assert_eq!(arena.allocated(), 0);
1476 assert_eq!(arena.current, 0);
1477 assert_eq!(arena.offset, 0);
1478 }
1479
1480 #[test]
1481 fn arena_empty_reset_then_alloc() {
1482 let mut arena = Arena::empty();
1483 arena.reset();
1484 let offset = arena.alloc_copy(b"after reset on empty");
1485 assert_eq!(arena.get(offset, 20), b"after reset on empty");
1486 }
1487
1488 #[test]
1489 fn arena_empty_capacity_is_zero() {
1490 let arena = Arena::empty();
1491 assert_eq!(arena.capacity(), 0);
1492 assert_eq!(arena.allocated(), 0);
1493 }
1494
1495 #[test]
1496 fn arena_empty_reset_then_multiple_allocs() {
1497 let mut arena = Arena::empty();
1498 arena.reset(); let o1 = arena.alloc_copy(b"first");
1500 let o2 = arena.alloc_copy(b"second");
1501 assert_eq!(arena.get(o1, 5), b"first");
1502 assert_eq!(arena.get(o2, 6), b"second");
1503 }
1504
1505 #[test]
1508 fn alloc_copy_exactly_fills_chunk() {
1509 let mut arena = Arena::new();
1510 let data = vec![0xCC; INITIAL_CHUNK_SIZE];
1512 let offset = arena.alloc_copy(&data);
1513 assert_eq!(arena.chunks.len(), 1, "should still be one chunk");
1514 assert_eq!(arena.get(offset, INITIAL_CHUNK_SIZE)[0], 0xCC);
1515 assert_eq!(arena.allocated(), INITIAL_CHUNK_SIZE);
1516
1517 let o2 = arena.alloc_copy(b"");
1519 assert_eq!(arena.get(o2, 0), &[]);
1520 }
1521
1522 #[test]
1525 fn arena_empty_get_zero_len() {
1526 let arena = Arena::empty();
1529 assert_eq!(arena.get(0, 0), &[]);
1530 }
1531}