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 if self.chunks.is_empty() {
216 return 0;
217 }
218 if self.current == 0 {
220 return self.offset;
221 }
222 let mut total = self.offset;
224 for chunk in &self.chunks[..self.current] {
225 total += chunk.len();
226 }
227 total
228 }
229
230 pub fn capacity(&self) -> usize {
232 self.chunks.iter().map(|c| c.capacity()).sum()
233 }
234
235 fn ensure_capacity(&mut self, len: usize) {
239 let chunk = &self.chunks[self.current];
240 let remaining = chunk.capacity().saturating_sub(self.offset);
241
242 if remaining >= len {
243 return;
244 }
245
246 let prev_cap = chunk.capacity();
248 let new_cap = prev_cap
249 .saturating_mul(2)
250 .max(len)
251 .max(INITIAL_CHUNK_SIZE)
252 .min(MAX_CHUNK_SIZE.max(len)); let next_idx = self.current + 1;
256 if next_idx < self.chunks.len() && self.chunks[next_idx].capacity() >= len {
257 self.current = next_idx;
258 self.offset = 0;
259 return;
260 }
261
262 let new_chunk = Vec::with_capacity(new_cap);
264 let prefix = self.prefix_sums[self.chunks.len() - 1]
265 + self.chunks.last().map_or(0, |c| c.capacity());
266 if next_idx < self.chunks.len() {
267 self.chunks[next_idx] = new_chunk;
268 self.rebuild_prefix_sums();
270 } else {
271 self.chunks.push(new_chunk);
272 self.prefix_sums.push(prefix);
273 }
274 self.current = next_idx;
275 self.offset = 0;
276 }
277
278 fn rebuild_prefix_sums(&mut self) {
280 self.prefix_sums.clear();
281 let mut sum = 0;
282 for chunk in &self.chunks {
283 self.prefix_sums.push(sum);
284 sum += chunk.capacity();
285 }
286 }
287
288 pub fn global_offset(&self) -> usize {
290 self.global_offset_at(self.current, self.offset)
291 }
292
293 fn global_offset_at(&self, chunk_idx: usize, local_offset: usize) -> usize {
296 self.prefix_sums[chunk_idx] + local_offset
297 }
298
299 fn resolve_offset(&self, global_offset: usize) -> (usize, usize) {
302 if self.chunks.len() == 1 {
304 debug_assert!(
305 global_offset < self.chunks[0].capacity(),
306 "arena offset {global_offset} out of bounds in single chunk (cap={})",
307 self.chunks[0].capacity()
308 );
309 return (0, global_offset);
310 }
311
312 let idx = match self.prefix_sums.binary_search(&global_offset) {
314 Ok(i) => i,
315 Err(0) => 0, Err(i) => i - 1,
317 };
318 let local = global_offset - self.prefix_sums[idx];
319 debug_assert!(
320 local < self.chunks[idx].capacity(),
321 "arena offset {global_offset} out of bounds in chunk {idx} (cap={})",
322 self.chunks[idx].capacity()
323 );
324 (idx, local)
325 }
326}
327
328impl Default for Arena {
329 fn default() -> Self {
330 Self::new()
331 }
332}
333
334thread_local! {
337 static ARENA_POOL: RefCell<Vec<Arena>> = const { RefCell::new(Vec::new()) };
338}
339
340pub fn acquire_arena() -> Arena {
355 ARENA_POOL
356 .with(|pool| pool.borrow_mut().pop())
357 .unwrap_or_default()
358}
359
360pub fn release_arena(mut arena: Arena) {
365 arena.reset();
366 ARENA_POOL.with(|pool| {
367 let mut pool = pool.borrow_mut();
368 if pool.len() < MAX_POOL_SIZE {
369 pool.push(arena);
370 }
371 });
373}
374
375pub struct ArenaRows<T> {
397 rows: Vec<T>,
398 arena: Arena,
399}
400
401impl<T> ArenaRows<T> {
402 pub fn new(rows: Vec<T>, arena: Arena) -> Self {
408 Self { rows, arena }
409 }
410
411 #[inline]
413 pub fn len(&self) -> usize {
414 self.rows.len()
415 }
416
417 #[inline]
419 pub fn is_empty(&self) -> bool {
420 self.rows.is_empty()
421 }
422
423 #[inline]
425 pub fn get(&self, idx: usize) -> Option<&T> {
426 self.rows.get(idx)
427 }
428
429 #[inline]
431 pub fn iter(&self) -> std::slice::Iter<'_, T> {
432 self.rows.iter()
433 }
434
435 pub fn into_parts(self) -> (Vec<T>, Arena) {
439 (self.rows, self.arena)
440 }
441
442 #[inline]
444 pub fn arena_allocated(&self) -> usize {
445 self.arena.allocated()
446 }
447}
448
449impl<T> std::ops::Deref for ArenaRows<T> {
450 type Target = [T];
451
452 #[inline]
453 fn deref(&self) -> &[T] {
454 &self.rows
455 }
456}
457
458impl<'a, T> IntoIterator for &'a ArenaRows<T> {
459 type Item = &'a T;
460 type IntoIter = std::slice::Iter<'a, T>;
461
462 #[inline]
463 fn into_iter(self) -> Self::IntoIter {
464 self.rows.iter()
465 }
466}
467
468impl<T: std::fmt::Debug> std::fmt::Debug for ArenaRows<T> {
469 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
470 f.debug_struct("ArenaRows")
471 .field("len", &self.rows.len())
472 .field("arena_allocated", &self.arena.allocated())
473 .field("rows", &self.rows)
474 .finish()
475 }
476}
477
478pub struct ValidatedRows<T> {
500 rows: Vec<T>,
501 text_buf: String,
502 blob_arena: Arena,
503}
504
505impl<T> ValidatedRows<T> {
506 pub fn new(rows: Vec<T>, text_buf: String, blob_arena: Arena) -> Self {
509 Self {
510 rows,
511 text_buf,
512 blob_arena,
513 }
514 }
515
516 #[inline]
518 pub fn text(&self) -> &str {
519 &self.text_buf
520 }
521
522 #[inline]
526 pub fn text_slice(&self, start: u32, end: u32) -> &str {
527 &self.text_buf[start as usize..end as usize]
528 }
529
530 #[inline]
532 pub fn blob_slice(&self, offset: u32, len: u32) -> &[u8] {
533 self.blob_arena.get(offset as usize, len as usize)
534 }
535
536 #[inline]
538 pub fn len(&self) -> usize {
539 self.rows.len()
540 }
541
542 #[inline]
544 pub fn is_empty(&self) -> bool {
545 self.rows.is_empty()
546 }
547
548 #[inline]
550 pub fn get_inner(&self, idx: usize) -> Option<&T> {
551 self.rows.get(idx)
552 }
553
554 #[inline]
556 pub fn iter_inner(&self) -> std::slice::Iter<'_, T> {
557 self.rows.iter()
558 }
559
560 #[inline]
562 pub fn text_len(&self) -> usize {
563 self.text_buf.len()
564 }
565
566 #[inline]
568 pub fn blob_allocated(&self) -> usize {
569 self.blob_arena.allocated()
570 }
571
572 #[inline]
574 pub fn arena_allocated(&self) -> usize {
575 self.text_buf.len() + self.blob_arena.allocated()
576 }
577}
578
579impl<T> std::ops::Deref for ValidatedRows<T> {
580 type Target = [T];
581
582 #[inline]
583 fn deref(&self) -> &[T] {
584 &self.rows
585 }
586}
587
588impl<'a, T> IntoIterator for &'a ValidatedRows<T> {
589 type Item = &'a T;
590 type IntoIter = std::slice::Iter<'a, T>;
591
592 #[inline]
593 fn into_iter(self) -> Self::IntoIter {
594 self.rows.iter()
595 }
596}
597
598impl<T: std::fmt::Debug> std::fmt::Debug for ValidatedRows<T> {
599 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
600 f.debug_struct("ValidatedRows")
601 .field("len", &self.rows.len())
602 .field("text_len", &self.text_buf.len())
603 .field("blob_allocated", &self.blob_arena.allocated())
604 .field("rows", &self.rows)
605 .finish()
606 }
607}
608
609#[cfg(test)]
610mod tests {
611 use super::*;
612
613 #[test]
614 fn basic_alloc_and_get() {
615 let mut arena = Arena::new();
616 let offset = arena.alloc_copy(b"hello");
617 assert_eq!(arena.get(offset, 5), b"hello");
618 }
619
620 #[test]
621 fn multiple_allocs() {
622 let mut arena = Arena::new();
623 let o1 = arena.alloc_copy(b"foo");
624 let o2 = arena.alloc_copy(b"bar");
625 let o3 = arena.alloc_copy(b"baz");
626
627 assert_eq!(arena.get(o1, 3), b"foo");
628 assert_eq!(arena.get(o2, 3), b"bar");
629 assert_eq!(arena.get(o3, 3), b"baz");
630 }
631
632 #[test]
633 fn alloc_str_retrieval() {
634 let mut arena = Arena::new();
635 let offset = arena.alloc_copy(b"hello world");
636 assert_eq!(arena.get_str(offset, 11), Some("hello world"));
637 }
638
639 #[test]
640 fn zero_length_alloc() {
641 let mut arena = Arena::new();
642 let offset = arena.alloc_copy(b"");
643 let data = arena.get(offset, 0);
644 assert!(data.is_empty());
645 }
646
647 #[test]
648 fn alloc_returns_zeroed_slice() {
649 let mut arena = Arena::new();
650 let slice = arena.alloc(16);
651 assert!(slice.iter().all(|&b| b == 0));
652 }
653
654 #[test]
655 fn reset_allows_reuse() {
656 let mut arena = Arena::new();
657 let _o1 = arena.alloc_copy(b"before reset");
658 assert_eq!(arena.allocated(), 12);
659
660 arena.reset();
661 assert_eq!(arena.allocated(), 0);
662
663 let o2 = arena.alloc_copy(b"after reset");
664 assert_eq!(arena.get(o2, 11), b"after reset");
665 }
666
667 #[test]
668 fn chunk_growth() {
669 let mut arena = Arena::new();
670
671 let big = vec![0xAA; INITIAL_CHUNK_SIZE + 1];
673 let offset = arena.alloc_copy(&big);
674 assert_eq!(arena.get(offset, big.len())[0], 0xAA);
675 assert!(
676 arena.chunks.len() >= 2,
677 "should have grown to a second chunk"
678 );
679 }
680
681 #[test]
682 fn large_single_alloc() {
683 let mut arena = Arena::new();
684 let data = vec![0x42; 2 * MAX_CHUNK_SIZE];
685 let offset = arena.alloc_copy(&data);
686 let result = arena.get(offset, data.len());
687 assert!(result.iter().all(|&b| b == 0x42));
688 }
689
690 #[test]
691 fn one_hundred_rows_in_one_chunk() {
692 let mut arena = Arena::new();
693 let row_data = b"typical row data, about 50 bytes of text content.";
694
695 let mut offsets = Vec::new();
696 for _ in 0..100 {
697 offsets.push(arena.alloc_copy(row_data));
698 }
699
700 assert_eq!(arena.chunks.len(), 1);
702
703 for &offset in &offsets {
704 assert_eq!(arena.get(offset, row_data.len()), row_data);
705 }
706 }
707
708 #[test]
709 fn reset_discards_oversized_chunks() {
710 let mut arena = Arena::new();
711
712 let big = vec![0xFF; SHRINK_THRESHOLD + 1];
714 arena.alloc_copy(&big);
715
716 let _chunks_before = arena.chunks.len();
717 arena.reset();
718
719 for chunk in &arena.chunks {
721 assert!(
722 chunk.capacity() <= SHRINK_THRESHOLD,
723 "oversized chunk not discarded: capacity={}",
724 chunk.capacity()
725 );
726 }
727 }
728
729 #[test]
730 fn thread_local_pool_acquire_release() {
731 let mut arena = acquire_arena();
732 arena.alloc_copy(b"test data");
733 release_arena(arena);
734
735 let arena2 = acquire_arena();
737 assert_eq!(arena2.allocated(), 0); release_arena(arena2);
739 }
740
741 #[test]
742 fn thread_local_pool_max_size() {
743 for _ in 0..MAX_POOL_SIZE + 2 {
745 let arena = Arena::new();
746 release_arena(arena);
747 }
748
749 ARENA_POOL.with(|pool| {
750 assert!(pool.borrow().len() <= MAX_POOL_SIZE);
751 });
752 }
753
754 #[test]
755 fn capacity_reports_total() {
756 let arena = Arena::new();
757 assert!(arena.capacity() >= INITIAL_CHUNK_SIZE);
758 }
759
760 #[test]
761 fn allocated_tracks_usage() {
762 let mut arena = Arena::new();
763 assert_eq!(arena.allocated(), 0);
764 arena.alloc_copy(b"12345");
765 assert_eq!(arena.allocated(), 5);
766 arena.alloc_copy(b"67890");
767 assert_eq!(arena.allocated(), 10);
768 }
769
770 #[test]
771 fn alloc_at_exact_8kb_boundary() {
772 let mut arena = Arena::new();
773
774 let filler = vec![0xAA; INITIAL_CHUNK_SIZE];
776 let o1 = arena.alloc_copy(&filler);
777 assert_eq!(arena.get(o1, INITIAL_CHUNK_SIZE)[0], 0xAA);
778 assert_eq!(arena.chunks.len(), 1);
779
780 let o2 = arena.alloc_copy(b"x");
782 assert_eq!(arena.get(o2, 1), b"x");
783 assert!(arena.chunks.len() >= 2, "should have grown past 8KB chunk");
784
785 assert_eq!(arena.get(o1, INITIAL_CHUNK_SIZE)[0], 0xAA);
787 assert_eq!(
788 arena.get(o1, INITIAL_CHUNK_SIZE)[INITIAL_CHUNK_SIZE - 1],
789 0xAA
790 );
791 }
792
793 #[test]
794 fn prefix_sums_correct_after_multi_chunk() {
795 let mut arena = Arena::new();
796 let mut offsets = Vec::new();
797
798 for i in 0..4 {
800 let data = vec![i as u8; INITIAL_CHUNK_SIZE + 1];
801 offsets.push((arena.alloc_copy(&data), data.len()));
802 }
803
804 for (idx, &(offset, len)) in offsets.iter().enumerate() {
806 let data = arena.get(offset, len);
807 assert!(data.iter().all(|&b| b == idx as u8));
808 }
809 }
810
811 #[test]
812 fn prefix_sums_correct_after_reset() {
813 let mut arena = Arena::new();
814
815 let big = vec![0xBB; INITIAL_CHUNK_SIZE + 1];
817 arena.alloc_copy(&big);
818 assert!(arena.chunks.len() >= 2);
819
820 arena.reset();
821
822 let o = arena.alloc_copy(b"after reset");
824 assert_eq!(arena.get(o, 11), b"after reset");
825 }
826
827 #[test]
829 fn resolve_offset_zero() {
830 let arena = Arena::new();
831 let (chunk_idx, local) = arena.resolve_offset(0);
832 assert_eq!(chunk_idx, 0);
833 assert_eq!(local, 0);
834 }
835
836 #[test]
838 fn resolve_offset_single_chunk_fast_path() {
839 let mut arena = Arena::new();
840 let o1 = arena.alloc_copy(b"hello");
842 let o2 = arena.alloc_copy(b"world");
843 assert_eq!(arena.chunks.len(), 1, "should be single chunk");
844
845 assert_eq!(arena.get(o1, 5), b"hello");
847 assert_eq!(arena.get(o2, 5), b"world");
848 }
849
850 #[test]
854 fn get_str_invalid_utf8_returns_none() {
855 let mut arena = Arena::new();
856 let offset = arena.alloc_copy(&[0xFF, 0xFE, 0xFD]);
857 assert_eq!(arena.get_str(offset, 3), None);
858 }
859
860 #[test]
862 fn get_str_valid_utf8() {
863 let mut arena = Arena::new();
864 let offset = arena.alloc_copy("hello".as_bytes());
865 assert_eq!(arena.get_str(offset, 5), Some("hello"));
866 }
867
868 #[test]
870 fn get_str_empty_returns_some_empty() {
871 let arena = Arena::new();
872 assert_eq!(arena.get_str(0, 0), Some(""));
873 }
874
875 #[test]
877 #[should_panic]
878 fn get_out_of_bounds_panics() {
879 let arena = Arena::new();
880 arena.get(INITIAL_CHUNK_SIZE + 100, 1);
882 }
883
884 #[test]
886 fn ensure_capacity_reuses_next_chunk() {
887 let mut arena = Arena::new();
888
889 let big = vec![0xAA; INITIAL_CHUNK_SIZE + 1];
891 arena.alloc_copy(&big);
892 assert!(arena.chunks.len() >= 2);
893
894 arena.reset();
896 assert_eq!(arena.current, 0);
897 assert_eq!(arena.offset, 0);
898
899 let filler = vec![0xBB; INITIAL_CHUNK_SIZE];
901 arena.alloc_copy(&filler);
902 let o = arena.alloc_copy(b"reuse check");
904 assert_eq!(arena.get(o, 11), b"reuse check");
905 }
906
907 #[test]
909 fn arena_cross_thread_no_crash() {
910 let mut arena = Arena::new();
913 arena.alloc_copy(b"test data");
914
915 let handle = std::thread::spawn(move || {
916 assert_eq!(arena.get(0, 9), b"test data");
918 arena.reset();
919 arena
920 });
921
922 let arena = handle.join().unwrap();
923 release_arena(arena);
925 }
926
927 #[test]
930 fn arena_rows_basic() {
931 let arena = Arena::new();
932 let ar: ArenaRows<i64> = ArenaRows::new(vec![42], arena);
933 assert_eq!(ar.len(), 1);
934 assert!(!ar.is_empty());
935 assert_eq!(ar[0], 42);
936 assert_eq!(ar.get(0), Some(&42));
937 }
938
939 #[test]
940 fn arena_rows_empty() {
941 let arena = Arena::new();
942 let ar: ArenaRows<i64> = ArenaRows::new(vec![], arena);
943 assert!(ar.is_empty());
944 assert_eq!(ar.len(), 0);
945 assert!(ar.get(0).is_none());
946 }
947
948 #[test]
949 fn arena_rows_iter() {
950 let arena = Arena::new();
951 let ar: ArenaRows<i64> = ArenaRows::new(vec![10, 20, 30], arena);
952 let vals: Vec<&i64> = ar.iter().collect();
953 assert_eq!(vals, vec![&10, &20, &30]);
954 }
955
956 #[test]
957 fn arena_rows_deref() {
958 let arena = Arena::new();
959 let ar: ArenaRows<i64> = ArenaRows::new(vec![1, 2, 3], arena);
960 let slice: &[i64] = &ar;
961 assert_eq!(slice, &[1, 2, 3]);
962 }
963
964 #[test]
965 fn arena_rows_for_loop() {
966 let arena = Arena::new();
967 let ar: ArenaRows<i64> = ArenaRows::new(vec![10, 20], arena);
968 let mut sum = 0;
969 for &val in &ar {
970 sum += val;
971 }
972 assert_eq!(sum, 30);
973 }
974
975 #[test]
976 fn arena_rows_debug() {
977 let arena = Arena::new();
978 let ar: ArenaRows<i64> = ArenaRows::new(vec![42], arena);
979 let dbg = format!("{ar:?}");
980 assert!(dbg.contains("ArenaRows"));
981 assert!(dbg.contains("42"));
982 }
983
984 #[test]
985 fn arena_rows_arena_allocated() {
986 let mut arena = Arena::new();
987 arena.alloc_copy(b"some data");
988 let allocated = arena.allocated();
989 let ar: ArenaRows<i64> = ArenaRows::new(vec![], arena);
990 assert_eq!(ar.arena_allocated(), allocated);
991 }
992
993 #[test]
994 fn arena_rows_into_parts() {
995 let arena = Arena::new();
996 let ar: ArenaRows<i64> = ArenaRows::new(vec![1, 2, 3], arena);
997 let (v, _arena) = ar.into_parts();
998 assert_eq!(v, vec![1, 2, 3]);
999 }
1000
1001 #[test]
1002 fn arena_rows_into_parts_empty() {
1003 let arena = Arena::new();
1004 let ar: ArenaRows<i64> = ArenaRows::new(vec![], arena);
1005 let (v, _arena) = ar.into_parts();
1006 assert!(v.is_empty());
1007 }
1008
1009 #[test]
1010 fn arena_rows_get_out_of_bounds() {
1011 let arena = Arena::new();
1012 let ar: ArenaRows<i64> = ArenaRows::new(vec![42], arena);
1013 assert_eq!(ar.get(0), Some(&42));
1014 assert_eq!(ar.get(1), None);
1015 assert_eq!(ar.get(999), None);
1016 }
1017
1018 #[test]
1021 fn validated_rows_basic() {
1022 let text_buf = String::from("alicebob");
1023 let blob_arena = Arena::new();
1024
1025 #[derive(Debug)]
1026 #[allow(dead_code)]
1027 struct Inner {
1028 id: i64,
1029 name_start: u32,
1030 name_end: u32,
1031 }
1032
1033 let rows = vec![
1034 Inner {
1035 id: 1,
1036 name_start: 0,
1037 name_end: 5,
1038 },
1039 Inner {
1040 id: 2,
1041 name_start: 5,
1042 name_end: 8,
1043 },
1044 ];
1045 let vr = ValidatedRows::new(rows, text_buf, blob_arena);
1046
1047 assert_eq!(vr.len(), 2);
1048 assert!(!vr.is_empty());
1049 assert_eq!(vr.text_slice(vr[0].name_start, vr[0].name_end), "alice");
1050 assert_eq!(vr.text_slice(vr[1].name_start, vr[1].name_end), "bob");
1051 }
1052
1053 #[test]
1054 fn validated_rows_empty() {
1055 let vr: ValidatedRows<i64> = ValidatedRows::new(vec![], String::new(), Arena::new());
1056 assert!(vr.is_empty());
1057 assert_eq!(vr.len(), 0);
1058 assert_eq!(vr.text_len(), 0);
1059 }
1060
1061 #[test]
1062 fn validated_rows_blob() {
1063 let mut blob_arena = Arena::new();
1064 let off = blob_arena.alloc_copy(&[0xDE, 0xAD]);
1065
1066 #[derive(Debug)]
1067 struct Inner {
1068 blob_off: u32,
1069 blob_len: u32,
1070 }
1071
1072 let rows = vec![Inner {
1073 blob_off: off as u32,
1074 blob_len: 2,
1075 }];
1076 let vr = ValidatedRows::new(rows, String::new(), blob_arena);
1077
1078 assert_eq!(vr.blob_slice(vr[0].blob_off, vr[0].blob_len), &[0xDE, 0xAD]);
1079 }
1080
1081 #[test]
1082 fn validated_rows_arena_allocated() {
1083 let mut blob_arena = Arena::new();
1084 blob_arena.alloc_copy(&[1, 2, 3]);
1085 let text_buf = String::from("hello");
1086
1087 let vr: ValidatedRows<i64> = ValidatedRows::new(vec![], text_buf, blob_arena);
1088 assert_eq!(vr.arena_allocated(), 5 + 3); }
1090
1091 #[test]
1092 fn validated_rows_debug() {
1093 let vr: ValidatedRows<i64> = ValidatedRows::new(vec![42], String::new(), Arena::new());
1094 let dbg = format!("{vr:?}");
1095 assert!(dbg.contains("ValidatedRows"));
1096 assert!(dbg.contains("42"));
1097 }
1098
1099 #[test]
1100 fn validated_rows_deref() {
1101 let vr: ValidatedRows<i64> = ValidatedRows::new(vec![1, 2, 3], String::new(), Arena::new());
1102 let slice: &[i64] = &vr;
1103 assert_eq!(slice, &[1, 2, 3]);
1104 }
1105
1106 #[test]
1107 fn validated_rows_iter() {
1108 let vr: ValidatedRows<i64> = ValidatedRows::new(vec![10, 20], String::new(), Arena::new());
1109 let mut sum = 0;
1110 for &val in &vr {
1111 sum += val;
1112 }
1113 assert_eq!(sum, 30);
1114 }
1115
1116 #[test]
1119 fn alloc_zero_returns_empty_slice() {
1120 let mut arena = Arena::new();
1121 let slice = arena.alloc(0);
1122 assert!(slice.is_empty());
1123 }
1124
1125 #[test]
1128 fn get_str_zero_len_returns_empty() {
1129 let arena = Arena::new();
1130 assert_eq!(arena.get_str(0, 0), Some(""));
1131 }
1132
1133 #[test]
1138 fn validated_rows_empty_text_buf() {
1139 let vr: ValidatedRows<i64> = ValidatedRows::new(vec![1, 2, 3], String::new(), Arena::new());
1140 assert_eq!(vr.text(), "");
1141 assert_eq!(vr.text_len(), 0);
1142 assert_eq!(vr.len(), 3);
1143 }
1144
1145 #[test]
1146 fn validated_rows_blob_only_no_text() {
1147 let mut blob_arena = Arena::new();
1148 let o1 = blob_arena.alloc_copy(&[0x01, 0x02, 0x03]);
1149 let o2 = blob_arena.alloc_copy(&[0xAA, 0xBB]);
1150
1151 #[derive(Debug)]
1152 struct Inner {
1153 off: u32,
1154 len: u32,
1155 }
1156
1157 let rows = vec![
1158 Inner {
1159 off: o1 as u32,
1160 len: 3,
1161 },
1162 Inner {
1163 off: o2 as u32,
1164 len: 2,
1165 },
1166 ];
1167 let vr = ValidatedRows::new(rows, String::new(), blob_arena);
1168 assert_eq!(vr.text_len(), 0);
1169 assert_eq!(vr.blob_slice(vr[0].off, vr[0].len), &[0x01, 0x02, 0x03]);
1170 assert_eq!(vr.blob_slice(vr[1].off, vr[1].len), &[0xAA, 0xBB]);
1171 }
1172
1173 #[test]
1174 #[should_panic]
1175 fn validated_rows_text_slice_out_of_bounds() {
1176 let vr: ValidatedRows<i64> = ValidatedRows::new(vec![], String::from("hi"), Arena::new());
1177 vr.text_slice(0, 100);
1179 }
1180
1181 #[test]
1182 #[should_panic]
1183 fn validated_rows_blob_slice_out_of_bounds() {
1184 let blob_arena = Arena::new();
1185 let vr: ValidatedRows<i64> = ValidatedRows::new(vec![], String::new(), blob_arena);
1186 vr.blob_slice(0, 100);
1188 }
1189
1190 #[test]
1191 fn validated_rows_large_10k_rows() {
1192 let mut text_buf = String::new();
1193 let blob_arena = Arena::new();
1194
1195 #[derive(Debug)]
1196 struct Inner {
1197 start: u32,
1198 end: u32,
1199 }
1200
1201 let mut rows = Vec::with_capacity(10_000);
1202 for i in 0..10_000u32 {
1203 let start = text_buf.len() as u32;
1204 text_buf.push_str(&format!("row_{i}"));
1205 let end = text_buf.len() as u32;
1206 rows.push(Inner { start, end });
1207 }
1208
1209 let vr = ValidatedRows::new(rows, text_buf, blob_arena);
1210 assert_eq!(vr.len(), 10_000);
1211 assert_eq!(vr.text_slice(vr[0].start, vr[0].end), "row_0");
1212 assert_eq!(vr.text_slice(vr[9999].start, vr[9999].end), "row_9999");
1213 }
1214
1215 #[test]
1216 fn validated_rows_text_slice_empty_range() {
1217 let vr: ValidatedRows<i64> =
1218 ValidatedRows::new(vec![], String::from("hello"), Arena::new());
1219 assert_eq!(vr.text_slice(0, 0), "");
1220 assert_eq!(vr.text_slice(3, 3), "");
1221 }
1222
1223 #[test]
1224 fn validated_rows_get_inner() {
1225 let vr: ValidatedRows<i64> =
1226 ValidatedRows::new(vec![10, 20, 30], String::new(), Arena::new());
1227 assert_eq!(vr.get_inner(0), Some(&10));
1228 assert_eq!(vr.get_inner(1), Some(&20));
1229 assert_eq!(vr.get_inner(2), Some(&30));
1230 assert_eq!(vr.get_inner(3), None);
1231 }
1232
1233 #[test]
1234 fn validated_rows_iter_inner() {
1235 let vr: ValidatedRows<i64> = ValidatedRows::new(vec![5, 10], String::new(), Arena::new());
1236 let vals: Vec<&i64> = vr.iter_inner().collect();
1237 assert_eq!(vals, vec![&5, &10]);
1238 }
1239
1240 #[test]
1241 fn validated_rows_blob_allocated_zero() {
1242 let vr: ValidatedRows<i64> = ValidatedRows::new(vec![], String::new(), Arena::new());
1243 assert_eq!(vr.blob_allocated(), 0);
1244 }
1245
1246 #[test]
1251 fn arena_get_zero_len() {
1252 let arena = Arena::new();
1253 let data = arena.get(0, 0);
1254 assert!(data.is_empty());
1255 }
1256
1257 #[test]
1258 fn arena_alloc_copy_zero_len() {
1259 let mut arena = Arena::new();
1260 let offset = arena.alloc_copy(b"");
1261 assert_eq!(arena.get(offset, 0), &[]);
1262 }
1263
1264 #[test]
1265 fn arena_global_offset_initial() {
1266 let arena = Arena::new();
1267 assert_eq!(arena.global_offset(), 0);
1268 }
1269
1270 #[test]
1271 fn arena_global_offset_advances() {
1272 let mut arena = Arena::new();
1273 arena.alloc_copy(b"12345");
1274 assert_eq!(arena.global_offset(), 5);
1275 arena.alloc_copy(b"67890");
1276 assert_eq!(arena.global_offset(), 10);
1277 }
1278
1279 #[test]
1280 fn arena_multiple_resets() {
1281 let mut arena = Arena::new();
1282 for _ in 0..10 {
1283 arena.alloc_copy(b"data");
1284 assert_eq!(arena.allocated(), 4);
1285 arena.reset();
1286 assert_eq!(arena.allocated(), 0);
1287 }
1288 }
1289
1290 #[test]
1291 fn arena_get_str_unicode() {
1292 let texts = [
1293 "\u{1F600}\u{1F4A9}", "\u{4e16}\u{754c}", "caf\u{00e9}", "\u{1F468}\u{200D}\u{1F469}", ];
1298 for text in &texts {
1299 let mut arena = Arena::new();
1300 let offset = arena.alloc_copy(text.as_bytes());
1301 assert_eq!(
1302 arena.get_str(offset, text.len()),
1303 Some(*text),
1304 "failed for text: {text}"
1305 );
1306 }
1307 }
1308
1309 #[test]
1310 fn arena_get_str_partial_utf8_returns_none() {
1311 let mut arena = Arena::new();
1313 let offset = arena.alloc_copy(&[0xC3]);
1314 assert_eq!(arena.get_str(offset, 1), None);
1315 }
1316
1317 #[test]
1318 fn arena_default_is_new() {
1319 let a1 = Arena::new();
1320 let a2 = Arena::default();
1321 assert_eq!(a1.allocated(), a2.allocated());
1322 assert_eq!(a1.capacity(), a2.capacity());
1323 }
1324
1325 #[test]
1330 fn arena_rows_large() {
1331 let arena = Arena::new();
1332 let rows: Vec<i64> = (0..1000).collect();
1333 let ar = ArenaRows::new(rows, arena);
1334 assert_eq!(ar.len(), 1000);
1335 assert_eq!(ar[0], 0);
1336 assert_eq!(ar[999], 999);
1337 }
1338
1339 #[test]
1340 fn arena_rows_with_arena_data() {
1341 let mut arena = Arena::new();
1342 let offset = arena.alloc_copy(b"stored data");
1343
1344 #[derive(Debug)]
1345 #[allow(dead_code)]
1346 struct Inner {
1347 off: usize,
1348 len: usize,
1349 }
1350
1351 let ar = ArenaRows::new(
1352 vec![Inner {
1353 off: offset,
1354 len: 11,
1355 }],
1356 arena,
1357 );
1358 assert_eq!(ar.len(), 1);
1359 }
1360
1361 #[test]
1366 fn thread_local_pool_acquire_fresh() {
1367 ARENA_POOL.with(|pool| pool.borrow_mut().clear());
1369 let arena = acquire_arena();
1370 assert_eq!(arena.allocated(), 0);
1371 release_arena(arena);
1372 }
1373
1374 #[test]
1375 fn thread_local_pool_recycle_resets() {
1376 let mut arena = Arena::new();
1377 arena.alloc_copy(b"something");
1378 assert!(arena.allocated() > 0);
1379 release_arena(arena);
1380
1381 let arena2 = acquire_arena();
1382 assert_eq!(arena2.allocated(), 0, "recycled arena should be reset");
1383 release_arena(arena2);
1384 }
1385
1386 #[test]
1389 fn arena_reset_clears_data_positions() {
1390 let mut arena = Arena::new();
1391 let o1 = arena.alloc_copy(b"first query data");
1392 assert_eq!(arena.get(o1, 16), b"first query data");
1393
1394 arena.reset();
1395 assert_eq!(arena.allocated(), 0);
1396 assert_eq!(arena.current, 0);
1397 assert_eq!(arena.offset, 0);
1398
1399 let o2 = arena.alloc_copy(b"second query dat");
1402 assert_eq!(o2, 0, "first alloc after reset should be at offset 0");
1403 assert_eq!(arena.get(o2, 16), b"second query dat");
1404 }
1405
1406 #[test]
1407 fn arena_reset_discards_oversized_chunks() {
1408 let mut arena = Arena::new();
1409 let big = vec![0xAA; 128 * 1024];
1411 arena.alloc_copy(&big);
1412 let cap_before = arena.capacity();
1413 assert!(cap_before >= 128 * 1024);
1414
1415 arena.reset();
1416 let cap_after = arena.capacity();
1417 assert!(
1419 cap_after < cap_before,
1420 "oversized chunks should be discarded on reset: before={cap_before}, after={cap_after}"
1421 );
1422 }
1423
1424 #[test]
1427 fn alloc_copy_zero_length_returns_valid_offset() {
1428 let mut arena = Arena::new();
1429 let o1 = arena.alloc_copy(b"");
1430 let o2 = arena.alloc_copy(b"hello");
1431 assert_eq!(o1, o2, "zero-length alloc should not advance offset");
1434 assert_eq!(arena.get(o2, 5), b"hello");
1435 }
1436
1437 #[test]
1440 fn get_zero_length_returns_empty() {
1441 let arena = Arena::new();
1442 assert_eq!(arena.get(0, 0), &[]);
1443 assert_eq!(arena.get(9999, 0), &[]);
1444 }
1445
1446 #[test]
1453 #[should_panic]
1454 fn arena_empty_alloc_copy_panics_without_reset() {
1455 let mut arena = Arena::empty();
1456 assert_eq!(arena.chunks.len(), 0);
1457 let _ = arena.alloc_copy(b"boom");
1459 }
1460
1461 #[test]
1462 #[should_panic]
1463 fn arena_empty_alloc_panics_without_reset() {
1464 let mut arena = Arena::empty();
1465 let _ = arena.alloc(8);
1467 }
1468
1469 #[test]
1470 fn arena_empty_reset_does_not_panic() {
1471 let mut arena = Arena::empty();
1472 arena.reset();
1474 assert!(
1476 !arena.chunks.is_empty(),
1477 "reset should create initial chunk if empty"
1478 );
1479 assert_eq!(arena.allocated(), 0);
1480 assert_eq!(arena.current, 0);
1481 assert_eq!(arena.offset, 0);
1482 }
1483
1484 #[test]
1485 fn arena_empty_reset_then_alloc() {
1486 let mut arena = Arena::empty();
1487 arena.reset();
1488 let offset = arena.alloc_copy(b"after reset on empty");
1489 assert_eq!(arena.get(offset, 20), b"after reset on empty");
1490 }
1491
1492 #[test]
1493 fn arena_empty_capacity_is_zero() {
1494 let arena = Arena::empty();
1495 assert_eq!(arena.capacity(), 0);
1496 assert_eq!(arena.allocated(), 0);
1497 }
1498
1499 #[test]
1500 fn arena_empty_reset_then_multiple_allocs() {
1501 let mut arena = Arena::empty();
1502 arena.reset(); let o1 = arena.alloc_copy(b"first");
1504 let o2 = arena.alloc_copy(b"second");
1505 assert_eq!(arena.get(o1, 5), b"first");
1506 assert_eq!(arena.get(o2, 6), b"second");
1507 }
1508
1509 #[test]
1512 fn alloc_copy_exactly_fills_chunk() {
1513 let mut arena = Arena::new();
1514 let data = vec![0xCC; INITIAL_CHUNK_SIZE];
1516 let offset = arena.alloc_copy(&data);
1517 assert_eq!(arena.chunks.len(), 1, "should still be one chunk");
1518 assert_eq!(arena.get(offset, INITIAL_CHUNK_SIZE)[0], 0xCC);
1519 assert_eq!(arena.allocated(), INITIAL_CHUNK_SIZE);
1520
1521 let o2 = arena.alloc_copy(b"");
1523 assert_eq!(arena.get(o2, 0), &[]);
1524 }
1525
1526 #[test]
1529 fn arena_empty_get_zero_len() {
1530 let arena = Arena::empty();
1533 assert_eq!(arena.get(0, 0), &[]);
1534 }
1535
1536 #[test]
1539 fn get_str_unicode_multibyte() {
1540 let mut arena = Arena::new();
1541 let text = "\u{1F600}\u{00E9}";
1542 let offset = arena.alloc_copy(text.as_bytes());
1543 assert_eq!(arena.get_str(offset, text.len()), Some(text));
1544 }
1545
1546 #[test]
1549 fn release_arena_empty_does_not_panic() {
1550 let arena = Arena::empty();
1551 release_arena(arena); }
1553
1554 #[test]
1557 fn validated_rows_blob_slice_roundtrip() {
1558 let mut arena = Arena::new();
1559 let offset = arena.alloc_copy(&[0xDE, 0xAD]);
1560 let rows: ValidatedRows<u32> =
1561 ValidatedRows::new(vec![offset as u32], String::new(), arena);
1562 let blob = rows.blob_slice(offset as u32, 2);
1563 assert_eq!(blob, &[0xDE, 0xAD]);
1564 }
1565
1566 #[test]
1569 fn validated_rows_deref_and_for_loop() {
1570 let arena = Arena::new();
1571 let rows: ValidatedRows<i32> = ValidatedRows::new(vec![1, 2, 3], String::new(), arena);
1572 let slice: &[i32] = &rows;
1573 assert_eq!(slice, &[1, 2, 3]);
1574
1575 let mut sum = 0;
1576 for &v in &rows {
1577 sum += v;
1578 }
1579 assert_eq!(sum, 6);
1580 }
1581}