1use core::fmt::{Debug, Formatter, Result as FmtResult};
67use core::num::NonZeroU32;
68
69const NUM_TOP_BINS: usize = 32;
71
72const BINS_PER_LEAF: usize = 8;
74
75const NUM_LEAF_BINS: usize = NUM_TOP_BINS * BINS_PER_LEAF;
77
78const TOP_BINS_INDEX_SHIFT: u32 = 3;
80
81const LEAF_BINS_INDEX_MASK: u32 = 7;
83
84const DEFAULT_MAX_NODES: u32 = 128 * 1024;
90
91pub fn min_allocator_size(size: u32, alignment: NonZeroU32) -> u32 {
101 let aligned = align_up(size, alignment.get()).expect("the aligned size does not fit in a u32");
102 small_float::float_to_uint(small_float::uint_to_float_round_up(aligned))
103}
104
105fn align_up(size: u32, alignment: u32) -> Option<u32> {
109 let rounded = size.checked_add(alignment - 1)? / alignment * alignment;
110 Some(rounded)
111}
112
113pub struct Allocator {
128 size: u32,
130 alignment: u32,
132 max_nodes: u32,
138 free_storage: u32,
143
144 used_bins_top: u32,
146 used_bins: [u8; NUM_TOP_BINS],
149 bin_indices: [Option<NodeIndex>; NUM_LEAF_BINS],
151
152 nodes: Vec<Node>,
154 free_nodes: Vec<NodeIndex>,
156 num_free_nodes: u32,
158 tail: Option<NodeIndex>,
162}
163
164#[derive(Clone, Copy, Debug, PartialEq, Eq)]
166pub struct Allocation {
167 pub offset: u32,
171 metadata: NodeIndex,
173}
174
175#[derive(Clone, Copy, Debug, PartialEq, Eq)]
177pub struct StorageReport {
178 pub total_free_space: u32,
180 pub largest_free_region: u32,
182}
183
184#[derive(Clone, Copy, Default)]
187struct Node {
188 data_offset: u32,
190 data_size: u32,
192 bin_list_prev: Option<NodeIndex>,
194 bin_list_next: Option<NodeIndex>,
196 neighbor_prev: Option<NodeIndex>,
198 neighbor_next: Option<NodeIndex>,
200 used: bool,
202}
203
204#[derive(Clone, Copy, PartialEq, Eq)]
209struct NodeIndex(NonZeroU32);
210
211impl NodeIndex {
212 fn new(index: u32) -> Self {
219 match NonZeroU32::new(index + 1) {
220 Some(index) => Self(index),
221 None => unreachable!(
222 "node indices are bounded by `Allocator::with_max_nodes_and_alignment`"
223 ),
224 }
225 }
226
227 fn get(self) -> usize {
229 (self.0.get() - 1) as usize
230 }
231}
232
233impl Debug for NodeIndex {
234 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
235 self.get().fmt(f)
236 }
237}
238
239impl Allocator {
240 pub fn new(size: u32) -> Self {
243 Self::with_max_nodes_and_alignment(size, DEFAULT_MAX_NODES, NonZeroU32::MIN)
244 }
245
246 pub fn with_max_nodes_and_alignment(size: u32, max_nodes: u32, alignment: NonZeroU32) -> Self {
253 assert!(max_nodes > 0, "an allocator needs at least one node");
254 assert!(
255 max_nodes < u32::MAX,
256 "an allocator cannot track u32::MAX nodes"
257 );
258
259 let mut this = Self {
260 size,
261 alignment: alignment.get(),
262 max_nodes,
263 free_storage: 0,
264 used_bins_top: 0,
265 used_bins: [0; NUM_TOP_BINS],
266 bin_indices: [None; NUM_LEAF_BINS],
267 nodes: Vec::new(),
268 free_nodes: Vec::new(),
269 num_free_nodes: 0,
270 tail: None,
271 };
272 this.reset();
273 this
274 }
275
276 pub fn reset(&mut self) {
278 self.free_storage = 0;
279 self.used_bins_top = 0;
280 self.used_bins = [0; NUM_TOP_BINS];
281 self.bin_indices = [None; NUM_LEAF_BINS];
282 self.nodes = vec![Node::default(); self.max_nodes as usize];
283
284 self.free_nodes = (0..self.max_nodes)
287 .map(|i| NodeIndex::new(self.max_nodes - i - 1))
288 .collect();
289 self.num_free_nodes = self.max_nodes;
290
291 let only_node = self.insert_node_into_bin(self.size, 0);
294 self.tail = Some(only_node);
295 }
296
297 pub fn allocate(&mut self, size: u32) -> Option<Allocation> {
304 if self.num_free_nodes == 0 {
306 return None;
307 }
308
309 let size = align_up(size, self.alignment)?;
314
315 let min_bin_index = small_float::uint_to_float_round_up(size);
318
319 let min_top_bin_index = min_bin_index >> TOP_BINS_INDEX_SHIFT;
320 let min_leaf_bin_index = min_bin_index & LEAF_BINS_INDEX_MASK;
321
322 let mut top_bin_index = min_top_bin_index;
323 let mut leaf_bin_index = None;
324
325 if (self.used_bins_top & (1 << top_bin_index)) != 0 {
328 leaf_bin_index = find_lowest_bit_set_after(
329 u32::from(self.used_bins[top_bin_index as usize]),
330 min_leaf_bin_index,
331 );
332 }
333
334 let leaf_bin_index = match leaf_bin_index {
338 Some(leaf_bin_index) => leaf_bin_index,
339 None => {
340 top_bin_index =
341 find_lowest_bit_set_after(self.used_bins_top, min_top_bin_index + 1)?;
342
343 self.used_bins[top_bin_index as usize].trailing_zeros()
346 }
347 };
348
349 let bin_index = (top_bin_index << TOP_BINS_INDEX_SHIFT) | leaf_bin_index;
350
351 let node_index = self.bin_indices[bin_index as usize].unwrap();
354 let node = &mut self.nodes[node_index.get()];
355 let node_total_size = node.data_size;
356 node.data_size = size;
357 node.used = true;
358 self.bin_indices[bin_index as usize] = node.bin_list_next;
359 if let Some(bin_list_next) = node.bin_list_next {
360 self.nodes[bin_list_next.get()].bin_list_prev = None;
361 }
362 self.free_storage -= node_total_size;
363
364 if self.bin_indices[bin_index as usize].is_none() {
366 self.used_bins[top_bin_index as usize] &= !(1 << leaf_bin_index);
368
369 if self.used_bins[top_bin_index as usize] == 0 {
371 self.used_bins_top &= !(1 << top_bin_index);
373 }
374 }
375
376 let remainder_size = node_total_size - size;
378 if remainder_size > 0 {
379 let Node {
380 data_offset,
381 neighbor_next,
382 ..
383 } = self.nodes[node_index.get()];
384
385 let new_node_index = self.insert_node_into_bin(remainder_size, data_offset + size);
386
387 let node = &mut self.nodes[node_index.get()];
391 if let Some(neighbor_next) = node.neighbor_next {
392 self.nodes[neighbor_next.get()].neighbor_prev = Some(new_node_index);
393 } else {
394 self.tail = Some(new_node_index);
396 }
397 self.nodes[new_node_index.get()].neighbor_prev = Some(node_index);
398 self.nodes[new_node_index.get()].neighbor_next = neighbor_next;
399 self.nodes[node_index.get()].neighbor_next = Some(new_node_index);
400 }
401
402 let node = &self.nodes[node_index.get()];
403 Some(Allocation {
404 offset: node.data_offset,
405 metadata: node_index,
406 })
407 }
408
409 pub fn free(&mut self, allocation: Allocation) {
415 let node_index = allocation.metadata;
416
417 let Node {
420 data_offset: mut offset,
421 data_size: mut size,
422 used,
423 ..
424 } = self.nodes[node_index.get()];
425
426 assert!(used, "the allocation was already freed");
428
429 if let Some(neighbor_prev) = self.nodes[node_index.get()].neighbor_prev {
430 let prev_node = self.nodes[neighbor_prev.get()];
431 if !prev_node.used {
432 offset = prev_node.data_offset;
435 size += prev_node.data_size;
436
437 debug_assert_eq!(prev_node.neighbor_next, Some(node_index));
438 self.nodes[node_index.get()].neighbor_prev = prev_node.neighbor_prev;
439
440 self.remove_node_from_bin(neighbor_prev);
443 }
444 }
445
446 if let Some(neighbor_next) = self.nodes[node_index.get()].neighbor_next {
447 let next_node = self.nodes[neighbor_next.get()];
448 if !next_node.used {
449 size += next_node.data_size;
452
453 debug_assert_eq!(next_node.neighbor_prev, Some(node_index));
454 self.nodes[node_index.get()].neighbor_next = next_node.neighbor_next;
455
456 self.remove_node_from_bin(neighbor_next);
459 }
460 }
461
462 let Node {
463 neighbor_next,
464 neighbor_prev,
465 ..
466 } = self.nodes[node_index.get()];
467
468 self.free_nodes[self.num_free_nodes as usize] = node_index;
472 self.num_free_nodes += 1;
473
474 let combined_node_index = self.insert_node_into_bin(size, offset);
476
477 if let Some(neighbor_next) = neighbor_next {
479 self.nodes[combined_node_index.get()].neighbor_next = Some(neighbor_next);
480 self.nodes[neighbor_next.get()].neighbor_prev = Some(combined_node_index);
481 } else {
482 self.tail = Some(combined_node_index);
484 }
485 if let Some(neighbor_prev) = neighbor_prev {
486 self.nodes[combined_node_index.get()].neighbor_prev = Some(neighbor_prev);
487 self.nodes[neighbor_prev.get()].neighbor_next = Some(combined_node_index);
488 }
489 }
490
491 pub fn extend(&mut self, additional: u32) -> bool {
503 let Some(additional) = align_up(additional, self.alignment) else {
504 return false;
505 };
506 let Some(size) = self.size.checked_add(additional) else {
507 return false;
508 };
509
510 match self.tail {
513 Some(tail) if !self.nodes[tail.get()].used => {
518 let tail_size = self.nodes[tail.get()].data_size;
519 let Some(grown) = tail_size.checked_add(additional) else {
520 return false;
521 };
522 self.rebin_node(tail, grown);
523 }
524 _ => {
527 if self.num_free_nodes == 0 {
528 return false;
529 }
530 let new_node_index = self.insert_node_into_bin(additional, self.size);
531 if let Some(tail) = self.tail {
532 self.nodes[tail.get()].neighbor_next = Some(new_node_index);
533 }
534 self.nodes[new_node_index.get()].neighbor_prev = self.tail;
535 self.tail = Some(new_node_index);
536 }
537 }
538
539 self.size = size;
540 true
541 }
542
543 fn rebin_node(&mut self, node_index: NodeIndex, size: u32) {
549 self.remove_node_from_bin(node_index);
550
551 let node = self.nodes[node_index.get()];
556 self.num_free_nodes -= 1;
557
558 let bin_index = small_float::uint_to_float_round_down(size);
559 let top_bin_index = bin_index >> TOP_BINS_INDEX_SHIFT;
560 let leaf_bin_index = bin_index & LEAF_BINS_INDEX_MASK;
561 if self.bin_indices[bin_index as usize].is_none() {
562 self.used_bins[top_bin_index as usize] |= 1 << leaf_bin_index;
563 self.used_bins_top |= 1 << top_bin_index;
564 }
565
566 let top_node_index = self.bin_indices[bin_index as usize];
567 self.nodes[node_index.get()] = Node {
568 data_size: size,
569 bin_list_prev: None,
572 bin_list_next: top_node_index,
573 ..node
574 };
575 if let Some(top_node_index) = top_node_index {
576 self.nodes[top_node_index.get()].bin_list_prev = Some(node_index);
577 }
578 self.bin_indices[bin_index as usize] = Some(node_index);
579 self.free_storage += size;
580 }
581
582 fn insert_node_into_bin(&mut self, size: u32, data_offset: u32) -> NodeIndex {
586 let bin_index = small_float::uint_to_float_round_down(size);
589
590 let top_bin_index = bin_index >> TOP_BINS_INDEX_SHIFT;
591 let leaf_bin_index = bin_index & LEAF_BINS_INDEX_MASK;
592
593 if self.bin_indices[bin_index as usize].is_none() {
595 self.used_bins[top_bin_index as usize] |= 1 << leaf_bin_index;
597 self.used_bins_top |= 1 << top_bin_index;
598 }
599
600 let top_node_index = self.bin_indices[bin_index as usize];
603 self.num_free_nodes -= 1;
604 let node_index = self.free_nodes[self.num_free_nodes as usize];
605 self.nodes[node_index.get()] = Node {
606 data_offset,
607 data_size: size,
608 bin_list_next: top_node_index,
609 ..Node::default()
610 };
611 if let Some(top_node_index) = top_node_index {
612 self.nodes[top_node_index.get()].bin_list_prev = Some(node_index);
613 }
614 self.bin_indices[bin_index as usize] = Some(node_index);
615
616 self.free_storage += size;
617 node_index
618 }
619
620 fn remove_node_from_bin(&mut self, node_index: NodeIndex) {
626 let node = self.nodes[node_index.get()];
628
629 match node.bin_list_prev {
630 Some(bin_list_prev) => {
631 self.nodes[bin_list_prev.get()].bin_list_next = node.bin_list_next;
634 if let Some(bin_list_next) = node.bin_list_next {
635 self.nodes[bin_list_next.get()].bin_list_prev = node.bin_list_prev;
636 }
637 }
638 None => {
639 let bin_index = small_float::uint_to_float_round_down(node.data_size);
644
645 let top_bin_index = bin_index >> TOP_BINS_INDEX_SHIFT;
646 let leaf_bin_index = bin_index & LEAF_BINS_INDEX_MASK;
647
648 self.bin_indices[bin_index as usize] = node.bin_list_next;
649 if let Some(bin_list_next) = node.bin_list_next {
650 self.nodes[bin_list_next.get()].bin_list_prev = None;
651 }
652
653 if self.bin_indices[bin_index as usize].is_none() {
655 self.used_bins[top_bin_index as usize] &= !(1 << leaf_bin_index);
657
658 if self.used_bins[top_bin_index as usize] == 0 {
660 self.used_bins_top &= !(1 << top_bin_index);
662 }
663 }
664 }
665 }
666
667 self.free_nodes[self.num_free_nodes as usize] = node_index;
669 self.num_free_nodes += 1;
670
671 self.free_storage -= node.data_size;
672 }
673
674 pub fn allocation_size(&self, allocation: Allocation) -> u32 {
680 self.nodes[allocation.metadata.get()].data_size
681 }
682
683 pub fn size(&self) -> u32 {
687 self.size
688 }
689
690 pub fn alignment(&self) -> u32 {
692 self.alignment
693 }
694
695 pub fn storage_report(&self) -> StorageReport {
698 let mut largest_free_region = 0;
699 let mut free_storage = 0;
700
701 if self.num_free_nodes > 0 {
703 free_storage = self.free_storage;
704 if self.used_bins_top > 0 {
705 let top_bin_index = 31 - self.used_bins_top.leading_zeros();
706 let leaf_bin_index =
707 31 - u32::from(self.used_bins[top_bin_index as usize]).leading_zeros();
708 largest_free_region = small_float::float_to_uint(
709 (top_bin_index << TOP_BINS_INDEX_SHIFT) | leaf_bin_index,
710 );
711 debug_assert!(free_storage >= largest_free_region);
712 }
713 }
714
715 StorageReport {
716 total_free_space: free_storage,
717 largest_free_region,
718 }
719 }
720}
721
722impl Debug for Allocator {
723 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
724 let report = self.storage_report();
725 f.debug_struct("Allocator")
726 .field("size", &self.size)
727 .field("alignment", &self.alignment)
728 .field("max_nodes", &self.max_nodes)
729 .field("total_free_space", &report.total_free_space)
730 .field("largest_free_region", &report.largest_free_region)
731 .field("free_nodes", &self.num_free_nodes)
732 .finish()
733 }
734}
735
736fn find_lowest_bit_set_after(bit_mask: u32, start_bit_index: u32) -> Option<u32> {
739 debug_assert!(start_bit_index < 32);
740 let mask_before_start_index = (1 << start_bit_index) - 1;
741 let bits_after = bit_mask & !mask_before_start_index;
742 if bits_after == 0 {
743 None
744 } else {
745 Some(bits_after.trailing_zeros())
746 }
747}
748
749mod small_float {
751 pub const MANTISSA_BITS: u32 = 3;
753 pub const MANTISSA_VALUE: u32 = 1 << MANTISSA_BITS;
755 pub const MANTISSA_MASK: u32 = MANTISSA_VALUE - 1;
757
758 pub fn uint_to_float_round_up(size: u32) -> u32 {
764 let mut exp = 0;
765 let mut mantissa;
766
767 if size < MANTISSA_VALUE {
768 mantissa = size
770 } else {
771 let highest_set_bit = 31 - size.leading_zeros();
774 let mantissa_start_bit = highest_set_bit - MANTISSA_BITS;
775 exp = mantissa_start_bit + 1;
776 mantissa = (size >> mantissa_start_bit) & MANTISSA_MASK;
777
778 let low_bits_mask = (1 << mantissa_start_bit) - 1;
780 if (size & low_bits_mask) != 0 {
781 mantissa += 1;
782 }
783 }
784
785 (exp << MANTISSA_BITS) + mantissa
788 }
789
790 pub fn uint_to_float_round_down(size: u32) -> u32 {
792 let mut exp = 0;
793 let mantissa;
794
795 if size < MANTISSA_VALUE {
796 mantissa = size
798 } else {
799 let highest_set_bit = 31 - size.leading_zeros();
802 let mantissa_start_bit = highest_set_bit - MANTISSA_BITS;
803 exp = mantissa_start_bit + 1;
804 mantissa = (size >> mantissa_start_bit) & MANTISSA_MASK;
805 }
806
807 (exp << MANTISSA_BITS) | mantissa
808 }
809
810 pub fn float_to_uint(float_value: u32) -> u32 {
812 let exponent = float_value >> MANTISSA_BITS;
813 let mantissa = float_value & MANTISSA_MASK;
814 if exponent == 0 {
815 mantissa
816 } else {
817 (mantissa | MANTISSA_VALUE) << (exponent - 1)
818 }
819 }
820}
821
822#[cfg(test)]
823mod tests {
824 use core::num::NonZeroU32;
825
826 use super::*;
827
828 const COPY_ALIGNMENT: NonZeroU32 = NonZeroU32::new(wgpu::COPY_BUFFER_ALIGNMENT as u32).unwrap();
830
831 #[test]
832 fn small_float_uint_to_float() {
833 let precise_number_count = 17;
837 for i in 0..precise_number_count {
838 let round_up = small_float::uint_to_float_round_up(i);
839 let round_down = small_float::uint_to_float_round_down(i);
840 assert_eq!(i, round_up);
841 assert_eq!(i, round_down);
842 }
843
844 struct NumberFloatUpDown {
846 number: u32,
847 up: u32,
848 down: u32,
849 }
850
851 let test_data = [
852 NumberFloatUpDown {
853 number: 17,
854 up: 17,
855 down: 16,
856 },
857 NumberFloatUpDown {
858 number: 118,
859 up: 39,
860 down: 38,
861 },
862 NumberFloatUpDown {
863 number: 1024,
864 up: 64,
865 down: 64,
866 },
867 NumberFloatUpDown {
868 number: 65536,
869 up: 112,
870 down: 112,
871 },
872 NumberFloatUpDown {
873 number: 529445,
874 up: 137,
875 down: 136,
876 },
877 NumberFloatUpDown {
878 number: 1048575,
879 up: 144,
880 down: 143,
881 },
882 ];
883
884 for v in test_data {
885 let round_up = small_float::uint_to_float_round_up(v.number);
886 let round_down = small_float::uint_to_float_round_down(v.number);
887 assert_eq!(round_up, v.up);
888 assert_eq!(round_down, v.down);
889 }
890 }
891
892 #[test]
893 fn small_float_float_to_uint() {
894 let precise_number_count = 17;
898 for i in 0..precise_number_count {
899 let v = small_float::float_to_uint(i);
900 assert_eq!(i, v);
901 }
902
903 for i in 0..240 {
906 let v = small_float::float_to_uint(i);
907 let round_up = small_float::uint_to_float_round_up(v);
908 let round_down = small_float::uint_to_float_round_down(v);
909 assert_eq!(i, round_up);
910 assert_eq!(i, round_down);
911 }
912 }
913
914 #[test]
915 fn basic_offset_allocator() {
916 let mut allocator = Allocator::new(1024 * 1024 * 256);
917 let a = allocator.allocate(1337).unwrap();
918 let offset: u32 = a.offset;
919 assert_eq!(offset, 0);
920 allocator.free(a);
921 }
922
923 #[test]
924 fn allocate_offset_allocator_simple() {
925 let mut allocator = Allocator::new(1024 * 1024 * 256);
926
927 let a = allocator.allocate(0).unwrap();
930 assert_eq!(a.offset, 0);
931
932 let b = allocator.allocate(1).unwrap();
933 assert_eq!(b.offset, 0);
934
935 let c = allocator.allocate(123).unwrap();
936 assert_eq!(c.offset, 1);
937
938 let d = allocator.allocate(1234).unwrap();
939 assert_eq!(d.offset, 124);
940
941 allocator.free(a);
942 allocator.free(b);
943 allocator.free(c);
944 allocator.free(d);
945
946 let validate_all = allocator.allocate(1024 * 1024 * 256).unwrap();
949 assert_eq!(validate_all.offset, 0);
950 allocator.free(validate_all);
951 }
952
953 #[test]
954 fn allocate_offset_allocator_merge_trivial() {
955 let mut allocator = Allocator::new(1024 * 1024 * 256);
956
957 let a = allocator.allocate(1337).unwrap();
960 assert_eq!(a.offset, 0);
961 allocator.free(a);
962
963 let b = allocator.allocate(1337).unwrap();
964 assert_eq!(b.offset, 0);
965 allocator.free(b);
966
967 let validate_all = allocator.allocate(1024 * 1024 * 256).unwrap();
970 assert_eq!(validate_all.offset, 0);
971 allocator.free(validate_all);
972 }
973
974 #[test]
975 fn allocate_offset_allocator_reuse_trivial() {
976 let mut allocator = Allocator::new(1024 * 1024 * 256);
977
978 let a = allocator.allocate(1024).unwrap();
981 assert_eq!(a.offset, 0);
982
983 let b = allocator.allocate(3456).unwrap();
984 assert_eq!(b.offset, 1024);
985
986 allocator.free(a);
987
988 let c = allocator.allocate(1024).unwrap();
989 assert_eq!(c.offset, 0);
990
991 allocator.free(c);
992 allocator.free(b);
993
994 let validate_all = allocator.allocate(1024 * 1024 * 256).unwrap();
997 assert_eq!(validate_all.offset, 0);
998 allocator.free(validate_all);
999 }
1000
1001 #[test]
1002 fn allocate_offset_allocator_reuse_complex() {
1003 let mut allocator = Allocator::new(1024 * 1024 * 256);
1004
1005 let a = allocator.allocate(1024).unwrap();
1009 assert_eq!(a.offset, 0);
1010
1011 let b = allocator.allocate(3456).unwrap();
1012 assert_eq!(b.offset, 1024);
1013
1014 allocator.free(a);
1015
1016 let c = allocator.allocate(2345).unwrap();
1017 assert_eq!(c.offset, 1024 + 3456);
1018
1019 let d = allocator.allocate(456).unwrap();
1020 assert_eq!(d.offset, 0);
1021
1022 let e = allocator.allocate(512).unwrap();
1023 assert_eq!(e.offset, 456);
1024
1025 let report = allocator.storage_report();
1026 assert_eq!(
1027 report.total_free_space,
1028 1024 * 1024 * 256 - 3456 - 2345 - 456 - 512
1029 );
1030 assert_ne!(report.largest_free_region, report.total_free_space);
1031
1032 allocator.free(c);
1033 allocator.free(d);
1034 allocator.free(b);
1035 allocator.free(e);
1036
1037 let validate_all = allocator.allocate(1024 * 1024 * 256).unwrap();
1040 assert_eq!(validate_all.offset, 0);
1041 allocator.free(validate_all);
1042 }
1043
1044 #[test]
1045 fn allocate_offset_allocator_zero_fragmentation() {
1046 let mut allocator = Allocator::new(1024 * 1024 * 256);
1047
1048 let mut allocations: [_; 256] = core::array::from_fn(|i| {
1052 let allocation = allocator.allocate(1024 * 1024).unwrap();
1053 assert_eq!(allocation.offset, i as u32 * 1024 * 1024);
1054 allocation
1055 });
1056
1057 let report = allocator.storage_report();
1058 assert_eq!(report.total_free_space, 0);
1059 assert_eq!(report.largest_free_region, 0);
1060
1061 allocator.free(allocations[243]);
1063 allocator.free(allocations[5]);
1064 allocator.free(allocations[123]);
1065 allocator.free(allocations[95]);
1066
1067 allocator.free(allocations[151]);
1069 allocator.free(allocations[152]);
1070 allocator.free(allocations[153]);
1071 allocator.free(allocations[154]);
1072
1073 allocations[243] = allocator.allocate(1024 * 1024).unwrap();
1074 allocations[5] = allocator.allocate(1024 * 1024).unwrap();
1075 allocations[123] = allocator.allocate(1024 * 1024).unwrap();
1076 allocations[95] = allocator.allocate(1024 * 1024).unwrap();
1077 allocations[151] = allocator.allocate(1024 * 1024 * 4).unwrap(); for (i, allocation) in allocations.iter().enumerate() {
1080 if !(152..155).contains(&i) {
1081 allocator.free(*allocation);
1082 }
1083 }
1084
1085 let report2 = allocator.storage_report();
1086 assert_eq!(report2.total_free_space, 1024 * 1024 * 256);
1087 assert_eq!(report2.largest_free_region, 1024 * 1024 * 256);
1088
1089 let validate_all = allocator.allocate(1024 * 1024 * 256).unwrap();
1092 assert_eq!(validate_all.offset, 0);
1093 allocator.free(validate_all);
1094 }
1095
1096 #[test]
1097 fn ext_min_allocator_size() {
1098 static TEST_OBJECT_SIZES: [u32; 42] = [
1100 0, 1, 2, 3, 4, 5, 8, 17, 23, 36, 51, 68, 87, 151, 165, 167, 201, 223, 306, 346, 394,
1101 411, 806, 969, 1404, 1798, 2236, 4281, 4745, 13989, 21095, 26594, 27146, 29679, 144685,
1102 153878, 495127, 727999, 1377073, 9440387, 41994490, 68520116,
1103 ];
1104
1105 for needed_object_size in TEST_OBJECT_SIZES {
1106 let allocator_size = min_allocator_size(needed_object_size, NonZeroU32::MIN);
1107 let mut allocator = Allocator::new(allocator_size);
1108 assert!(allocator.allocate(needed_object_size).is_some());
1109 }
1110 }
1111
1112 #[test]
1113 fn aligned_allocations_are_contiguous_and_aligned() {
1114 let mut allocator =
1115 Allocator::with_max_nodes_and_alignment(1 << 20, DEFAULT_MAX_NODES, COPY_ALIGNMENT);
1116
1117 let a = allocator.allocate(3).unwrap();
1120 assert_eq!(a.offset, 0);
1121 assert_eq!(allocator.allocation_size(a), 4);
1122
1123 let b = allocator.allocate(5).unwrap();
1124 assert_eq!(b.offset, 4);
1125 assert_eq!(allocator.allocation_size(b), 8);
1126
1127 let c = allocator.allocate(4).unwrap();
1128 assert_eq!(c.offset, 12);
1129 assert_eq!(allocator.allocation_size(c), 4);
1130
1131 for allocation in [a, b, c] {
1132 assert_eq!(allocation.offset % COPY_ALIGNMENT.get(), 0);
1133 }
1134
1135 allocator.free(b);
1138 allocator.free(a);
1139 allocator.free(c);
1140
1141 let report = allocator.storage_report();
1142 assert_eq!(report.total_free_space, 1 << 20);
1143 assert_eq!(report.largest_free_region, 1 << 20);
1144 }
1145
1146 #[test]
1147 fn min_allocator_size_holds_aligned_objects() {
1148 static TEST_OBJECT_SIZES: [u32; 42] = [
1150 0, 1, 2, 3, 4, 5, 8, 17, 23, 36, 51, 68, 87, 151, 165, 167, 201, 223, 306, 346, 394,
1151 411, 806, 969, 1404, 1798, 2236, 4281, 4745, 13989, 21095, 26594, 27146, 29679, 144685,
1152 153878, 495127, 727999, 1377073, 9440387, 41994490, 68520116,
1153 ];
1154
1155 for needed_object_size in TEST_OBJECT_SIZES {
1156 let allocator_size = min_allocator_size(needed_object_size, COPY_ALIGNMENT);
1157 let mut allocator = Allocator::with_max_nodes_and_alignment(
1158 allocator_size,
1159 DEFAULT_MAX_NODES,
1160 COPY_ALIGNMENT,
1161 );
1162 let allocation = allocator.allocate(needed_object_size).unwrap_or_else(|| {
1163 panic!("{needed_object_size} units must fit in {allocator_size}")
1164 });
1165 assert_eq!(allocation.offset % COPY_ALIGNMENT.get(), 0);
1166 assert!(allocator.allocation_size(allocation) >= needed_object_size);
1167 }
1168 }
1169
1170 #[test]
1171 fn the_last_node_of_the_free_list_is_usable() {
1172 let mut allocator = Allocator::with_max_nodes_and_alignment(2, 2, NonZeroU32::MIN);
1176 let allocation = allocator.allocate(1).unwrap();
1177 assert_eq!(allocation.offset, 0);
1178 assert!(allocator.allocate(1).is_none());
1179 }
1180
1181 #[test]
1182 fn every_offset_stays_aligned_under_churn() {
1183 let mut seed = 0x9e37_79b9u32;
1185 let mut next = move || {
1186 seed ^= seed << 13;
1187 seed ^= seed >> 17;
1188 seed ^= seed << 5;
1189 seed
1190 };
1191
1192 let alignment = COPY_ALIGNMENT;
1193 let mut allocator = Allocator::with_max_nodes_and_alignment(1 << 16, 4096, alignment);
1194
1195 let mut live: Vec<(Allocation, u32)> = Vec::new();
1198
1199 for _ in 0..10_000 {
1200 if !live.is_empty() && next() % 3 == 0 {
1202 let index = (next() as usize) % live.len();
1203 let (allocation, _) = live.swap_remove(index);
1204 allocator.free(allocation);
1205 continue;
1206 }
1207
1208 let size = next() % 2000;
1209 let Some(allocation) = allocator.allocate(size) else {
1210 continue;
1211 };
1212 let charged = allocator.allocation_size(allocation);
1213
1214 assert_eq!(allocation.offset % alignment.get(), 0);
1216 assert!(charged >= size);
1217 assert_eq!(charged % alignment.get(), 0);
1218
1219 let end = allocation.offset + charged;
1221 for &(other, other_charged) in &live {
1222 let other_end = other.offset + other_charged;
1223 assert!(
1224 end <= other.offset || other_end <= allocation.offset,
1225 "range {}..{} overlaps {}..{}",
1226 allocation.offset,
1227 end,
1228 other.offset,
1229 other_end
1230 );
1231 }
1232
1233 live.push((allocation, charged));
1234 }
1235
1236 for (allocation, _) in live {
1238 allocator.free(allocation);
1239 }
1240 let report = allocator.storage_report();
1241 assert_eq!(report.total_free_space, 1 << 16);
1242 assert_eq!(report.largest_free_region, 1 << 16);
1243 }
1244
1245 #[test]
1246 #[should_panic(expected = "already freed")]
1247 fn double_free_panics() {
1248 let mut allocator = Allocator::new(1024);
1249 let allocation = allocator.allocate(64).unwrap();
1250 allocator.free(allocation);
1251 allocator.free(allocation);
1252 }
1253
1254 #[test]
1255 fn extend_keeps_existing_offsets_and_appends_at_the_end() {
1256 let mut allocator =
1257 Allocator::with_max_nodes_and_alignment(1024, DEFAULT_MAX_NODES, COPY_ALIGNMENT);
1258 assert_eq!(allocator.size(), 1024);
1259
1260 let a = allocator.allocate(512).unwrap();
1263 let b = allocator.allocate(512).unwrap();
1264 assert_eq!((a.offset, b.offset), (0, 512));
1265
1266 assert!(allocator.extend(1024));
1267 assert_eq!(allocator.size(), 2048);
1268
1269 assert_eq!(allocator.allocation_size(a), 512);
1271 assert_eq!(allocator.allocation_size(b), 512);
1272
1273 let c = allocator.allocate(1024).unwrap();
1274 assert_eq!(
1275 c.offset, 1024,
1276 "the new range comes from the appended space"
1277 );
1278
1279 allocator.free(a);
1282 allocator.free(b);
1283 allocator.free(c);
1284 let report = allocator.storage_report();
1285 assert_eq!(report.total_free_space, 2048);
1286 assert_eq!(report.largest_free_region, 2048);
1287
1288 let whole = allocator.allocate(2048).unwrap();
1289 assert_eq!(whole.offset, 0);
1290 }
1291
1292 #[test]
1293 fn extend_merges_into_a_free_tail() {
1294 let mut allocator =
1295 Allocator::with_max_nodes_and_alignment(1024, DEFAULT_MAX_NODES, COPY_ALIGNMENT);
1296
1297 let a = allocator.allocate(256).unwrap();
1301 assert!(allocator.extend(1024));
1302
1303 let report = allocator.storage_report();
1304 assert_eq!(report.total_free_space, 2048 - 256);
1305 assert_eq!(report.largest_free_region, 2048 - 256);
1306
1307 let tail = allocator.allocate(2048 - 256).unwrap();
1308 assert_eq!(tail.offset, 256);
1309 allocator.free(a);
1310 allocator.free(tail);
1311
1312 let whole = allocator.allocate(2048).unwrap();
1313 assert_eq!(whole.offset, 0);
1314 }
1315
1316 #[test]
1317 fn extend_appends_after_an_allocated_tail() {
1318 let mut allocator = Allocator::with_max_nodes_and_alignment(2, 3, NonZeroU32::MIN);
1319
1320 let a = allocator.allocate(2).unwrap();
1323 assert_eq!(a.offset, 0);
1324 assert!(allocator.extend(64));
1325 assert_eq!(allocator.size(), 66);
1326
1327 assert!(allocator.extend(64));
1330 assert_eq!(allocator.size(), 130);
1331
1332 let b = allocator.allocate(128).unwrap();
1335 assert_eq!(b.offset, 2);
1336
1337 allocator.free(a);
1338 allocator.free(b);
1339 assert_eq!(allocator.storage_report().total_free_space, 130);
1340
1341 let whole = allocator.allocate(128).unwrap();
1346 assert_eq!(whole.offset, 0);
1347 }
1348}