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