]>
Commit | Line | Data |
---|---|---|
353b0b11 FG |
1 | use super::*; |
2 | use crate::mir::interpret::alloc_range; | |
3 | ||
4 | #[test] | |
5 | fn uninit_mask() { | |
6 | let mut mask = InitMask::new(Size::from_bytes(500), false); | |
7 | assert!(!mask.get(Size::from_bytes(499))); | |
8 | mask.set_range(alloc_range(Size::from_bytes(499), Size::from_bytes(1)), true); | |
9 | assert!(mask.get(Size::from_bytes(499))); | |
10 | mask.set_range((100..256).into(), true); | |
11 | for i in 0..100 { | |
12 | assert!(!mask.get(Size::from_bytes(i)), "{i} should not be set"); | |
13 | } | |
14 | for i in 100..256 { | |
15 | assert!(mask.get(Size::from_bytes(i)), "{i} should be set"); | |
16 | } | |
17 | for i in 256..499 { | |
18 | assert!(!mask.get(Size::from_bytes(i)), "{i} should not be set"); | |
19 | } | |
20 | } | |
21 | ||
22 | /// Returns the number of materialized blocks for this mask. | |
23 | fn materialized_block_count(mask: &InitMask) -> usize { | |
24 | match mask.blocks { | |
25 | InitMaskBlocks::Lazy { .. } => 0, | |
26 | InitMaskBlocks::Materialized(ref blocks) => blocks.blocks.len(), | |
27 | } | |
28 | } | |
29 | ||
30 | #[test] | |
31 | fn materialize_mask_within_range() { | |
32 | // To have spare bits, we use a mask size smaller than its block size of 64. | |
33 | let mut mask = InitMask::new(Size::from_bytes(16), false); | |
34 | assert_eq!(materialized_block_count(&mask), 0); | |
35 | ||
36 | // Forces materialization, but doesn't require growth. This is case #1 documented in the | |
37 | // `set_range` method. | |
38 | mask.set_range((8..16).into(), true); | |
39 | assert_eq!(materialized_block_count(&mask), 1); | |
40 | ||
41 | for i in 0..8 { | |
42 | assert!(!mask.get(Size::from_bytes(i)), "{i} should not be set"); | |
43 | } | |
44 | for i in 8..16 { | |
45 | assert!(mask.get(Size::from_bytes(i)), "{i} should be set"); | |
46 | } | |
47 | } | |
48 | ||
49 | #[test] | |
50 | fn grow_within_unused_bits_with_full_overwrite() { | |
51 | // To have spare bits, we use a mask size smaller than its block size of 64. | |
52 | let mut mask = InitMask::new(Size::from_bytes(16), true); | |
53 | for i in 0..16 { | |
54 | assert!(mask.get(Size::from_bytes(i)), "{i} should be set"); | |
55 | } | |
56 | ||
57 | // Grow without requiring an additional block. Full overwrite. | |
58 | // This can be fully handled without materialization. | |
59 | let range = (0..32).into(); | |
60 | mask.set_range(range, true); | |
61 | ||
62 | for i in 0..32 { | |
63 | assert!(mask.get(Size::from_bytes(i)), "{i} should be set"); | |
64 | } | |
65 | ||
66 | assert_eq!(materialized_block_count(&mask), 0); | |
67 | } | |
68 | ||
69 | // This test checks that an initmask's spare capacity is correctly used when growing within block | |
70 | // capacity. This can be fully handled without materialization. | |
71 | #[test] | |
72 | fn grow_same_state_within_unused_bits() { | |
73 | // To have spare bits, we use a mask size smaller than its block size of 64. | |
74 | let mut mask = InitMask::new(Size::from_bytes(16), true); | |
75 | for i in 0..16 { | |
76 | assert!(mask.get(Size::from_bytes(i)), "{i} should be set"); | |
77 | } | |
78 | ||
79 | // Grow without requiring an additional block. The gap between the current length and the | |
80 | // range's beginning should be set to the same value as the range. | |
81 | let range = (24..32).into(); | |
82 | mask.set_range(range, true); | |
83 | ||
84 | // We want to make sure the unused bits in the first block are correct | |
85 | for i in 16..24 { | |
86 | assert!(mask.get(Size::from_bytes(i)), "{i} should be set"); | |
87 | } | |
88 | ||
89 | for i in 24..32 { | |
90 | assert!(mask.get(Size::from_bytes(i)), "{i} should be set"); | |
91 | } | |
92 | ||
93 | assert_eq!(1, mask.range_as_init_chunks((0..32).into()).count()); | |
94 | assert_eq!(materialized_block_count(&mask), 0); | |
95 | } | |
96 | ||
97 | // This is the same test as `grow_same_state_within_unused_bits` but with both init and uninit | |
98 | // states: this forces materialization; otherwise the mask could stay lazy even when needing to | |
99 | // grow. | |
100 | #[test] | |
101 | fn grow_mixed_state_within_unused_bits() { | |
102 | // To have spare bits, we use a mask size smaller than its block size of 64. | |
103 | let mut mask = InitMask::new(Size::from_bytes(16), true); | |
104 | for i in 0..16 { | |
105 | assert!(mask.get(Size::from_bytes(i)), "{i} should be set"); | |
106 | } | |
107 | ||
108 | // Grow without requiring an additional block. The gap between the current length and the | |
109 | // range's beginning should be set to the same value as the range. Note: since this is fully | |
110 | // out-of-bounds of the current mask, this is case #3 described in the `set_range` method. | |
111 | let range = (24..32).into(); | |
112 | mask.set_range(range, false); | |
113 | ||
114 | // We want to make sure the unused bits in the first block are correct | |
115 | for i in 16..24 { | |
116 | assert!(!mask.get(Size::from_bytes(i)), "{i} should not be set"); | |
117 | } | |
118 | ||
119 | for i in 24..32 { | |
120 | assert!(!mask.get(Size::from_bytes(i)), "{i} should not be set"); | |
121 | } | |
122 | ||
123 | assert_eq!(1, mask.range_as_init_chunks((0..16).into()).count()); | |
124 | assert_eq!(2, mask.range_as_init_chunks((0..32).into()).count()); | |
125 | assert_eq!(materialized_block_count(&mask), 1); | |
126 | } | |
127 | ||
128 | // This is similar to `grow_mixed_state_within_unused_bits` to force materialization, but the range | |
129 | // to set partially overlaps the mask, so this requires a different growth + write pattern in the | |
130 | // mask. | |
131 | #[test] | |
132 | fn grow_within_unused_bits_with_overlap() { | |
133 | // To have spare bits, we use a mask size smaller than its block size of 64. | |
134 | let mut mask = InitMask::new(Size::from_bytes(16), true); | |
135 | for i in 0..16 { | |
136 | assert!(mask.get(Size::from_bytes(i)), "{i} should be set"); | |
137 | } | |
138 | ||
139 | // Grow without requiring an additional block, but leave no gap after the current len. Note: | |
140 | // since this is partially out-of-bounds of the current mask, this is case #2 described in the | |
141 | // `set_range` method. | |
142 | let range = (8..24).into(); | |
143 | mask.set_range(range, false); | |
144 | ||
145 | // We want to make sure the unused bits in the first block are correct | |
146 | for i in 8..24 { | |
147 | assert!(!mask.get(Size::from_bytes(i)), "{i} should not be set"); | |
148 | } | |
149 | ||
150 | assert_eq!(1, mask.range_as_init_chunks((0..8).into()).count()); | |
151 | assert_eq!(2, mask.range_as_init_chunks((0..24).into()).count()); | |
152 | assert_eq!(materialized_block_count(&mask), 1); | |
153 | } | |
154 | ||
155 | // Force materialization before a full overwrite: the mask can now become lazy. | |
156 | #[test] | |
157 | fn grow_mixed_state_within_unused_bits_and_full_overwrite() { | |
158 | // To have spare bits, we use a mask size smaller than its block size of 64. | |
159 | let mut mask = InitMask::new(Size::from_bytes(16), true); | |
160 | let range = (0..16).into(); | |
161 | assert!(mask.is_range_initialized(range).is_ok()); | |
162 | ||
163 | // Force materialization. | |
164 | let range = (8..24).into(); | |
165 | mask.set_range(range, false); | |
166 | assert!(mask.is_range_initialized(range).is_err()); | |
167 | assert_eq!(materialized_block_count(&mask), 1); | |
168 | ||
169 | // Full overwrite, lazy blocks would be enough from now on. | |
170 | let range = (0..32).into(); | |
171 | mask.set_range(range, true); | |
172 | assert!(mask.is_range_initialized(range).is_ok()); | |
173 | ||
174 | assert_eq!(1, mask.range_as_init_chunks((0..32).into()).count()); | |
175 | assert_eq!(materialized_block_count(&mask), 0); | |
176 | } | |
177 | ||
178 | // Check that growth outside the current capacity can still be lazy: if the init state doesn't | |
179 | // change, we don't need materialized blocks. | |
180 | #[test] | |
181 | fn grow_same_state_outside_capacity() { | |
182 | // To have spare bits, we use a mask size smaller than its block size of 64. | |
183 | let mut mask = InitMask::new(Size::from_bytes(16), true); | |
184 | for i in 0..16 { | |
185 | assert!(mask.get(Size::from_bytes(i)), "{i} should be set"); | |
186 | } | |
187 | assert_eq!(materialized_block_count(&mask), 0); | |
188 | ||
189 | // Grow to 10 blocks with the same init state. | |
190 | let range = (24..640).into(); | |
191 | mask.set_range(range, true); | |
192 | ||
193 | assert_eq!(1, mask.range_as_init_chunks((0..640).into()).count()); | |
194 | assert_eq!(materialized_block_count(&mask), 0); | |
195 | } |