turbo_trace_server/
chunked_vec.rs1use std::{
14 mem::MaybeUninit,
15 ops::{Index, IndexMut},
16};
17
18const CHUNK_SIZE: usize = 1 << 16;
21
22#[inline]
24fn split_index(idx: usize) -> (usize, usize) {
25 (idx / CHUNK_SIZE, idx % CHUNK_SIZE)
26}
27
28type Chunk<T> = Box<[MaybeUninit<T>; CHUNK_SIZE]>;
29
30fn new_chunk<T>() -> Chunk<T> {
33 unsafe {
38 let raw: Box<MaybeUninit<[MaybeUninit<T>; CHUNK_SIZE]>> = Box::new_uninit();
39 raw.assume_init()
40 }
41}
42
43pub struct ChunkedVec<T> {
44 chunks: Vec<Chunk<T>>,
45 len: usize,
46}
47
48impl<T> ChunkedVec<T> {
49 pub fn new() -> Self {
50 Self {
51 chunks: Vec::new(),
52 len: 0,
53 }
54 }
55
56 pub fn len(&self) -> usize {
57 self.len
58 }
59
60 pub fn push(&mut self, value: T) -> usize {
62 let idx = self.len;
63 let (chunk_idx, off) = split_index(idx);
64 if off == 0 {
65 debug_assert_eq!(chunk_idx, self.chunks.len());
67 self.chunks.push(new_chunk());
68 }
69 self.chunks[chunk_idx][off].write(value);
70 self.len += 1;
71 idx
72 }
73
74 pub fn get(&self, idx: usize) -> Option<&T> {
75 if idx >= self.len {
76 return None;
77 }
78 let (chunk_idx, off) = split_index(idx);
79 Some(unsafe { self.chunks[chunk_idx][off].assume_init_ref() })
82 }
83}
84
85impl<T> Default for ChunkedVec<T> {
86 fn default() -> Self {
87 Self::new()
88 }
89}
90
91impl<T> Drop for ChunkedVec<T> {
92 fn drop(&mut self) {
93 let (last_chunk, last_chunk_len) = split_index(self.len);
97
98 for (chunk_index, chunk) in self.chunks.iter_mut().enumerate() {
99 let chunk_end = if chunk_index == last_chunk {
100 last_chunk_len
101 } else {
102 CHUNK_SIZE
103 };
104 for slot in &mut chunk[0..chunk_end] {
105 unsafe { slot.assume_init_drop() };
108 }
109 }
110 }
111}
112
113impl<T> Index<usize> for ChunkedVec<T> {
114 type Output = T;
115
116 #[inline]
117 fn index(&self, idx: usize) -> &T {
118 assert!(idx < self.len, "index out of bounds: {idx} >= {}", self.len);
119 let (chunk_idx, off) = split_index(idx);
120 unsafe { self.chunks[chunk_idx][off].assume_init_ref() }
122 }
123}
124
125impl<T> IndexMut<usize> for ChunkedVec<T> {
126 #[inline]
127 fn index_mut(&mut self, idx: usize) -> &mut T {
128 assert!(idx < self.len, "index out of bounds: {idx} >= {}", self.len);
129 let (chunk_idx, off) = split_index(idx);
130 unsafe { self.chunks[chunk_idx][off].assume_init_mut() }
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn empty() {
141 let v: ChunkedVec<u32> = ChunkedVec::new();
142 assert_eq!(v.len(), 0);
143 assert!(v.get(0).is_none());
144 }
145
146 #[test]
147 fn push_within_first_chunk() {
148 let mut v = ChunkedVec::new();
149 for i in 0..1000u32 {
150 assert_eq!(v.push(i), i as usize);
151 }
152 assert_eq!(v.len(), 1000);
153 assert_eq!(v[0], 0);
154 assert_eq!(v[999], 999);
155 assert_eq!(v.get(1000), None);
156 }
157
158 #[test]
159 fn push_across_chunk_boundary() {
160 let mut v = ChunkedVec::new();
161 let total = 3 * CHUNK_SIZE + 17;
163 for i in 0..total {
164 v.push(i);
165 }
166 assert_eq!(v.len(), total);
167 assert_eq!(v[0], 0);
168 assert_eq!(v[CHUNK_SIZE - 1], CHUNK_SIZE - 1);
169 assert_eq!(v[CHUNK_SIZE], CHUNK_SIZE);
170 assert_eq!(v[2 * CHUNK_SIZE], 2 * CHUNK_SIZE);
171 assert_eq!(v[total - 1], total - 1);
172 }
173
174 #[test]
175 fn index_mut_writes_through() {
176 let mut v = ChunkedVec::new();
177 for i in 0..(CHUNK_SIZE + 5) {
178 v.push(i);
179 }
180 v[CHUNK_SIZE + 3] = 9999;
181 assert_eq!(v[CHUNK_SIZE + 3], 9999);
182 }
183
184 #[test]
185 fn drops_elements_on_drop() {
186 use std::rc::Rc;
187 let counter = Rc::new(());
188 {
189 let mut v: ChunkedVec<Rc<()>> = ChunkedVec::new();
190 let total = 2 * CHUNK_SIZE + 5;
192 for _ in 0..total {
193 v.push(counter.clone());
194 }
195 assert_eq!(Rc::strong_count(&counter), total + 1);
196 } assert_eq!(Rc::strong_count(&counter), 1);
198 }
199}