@@ -17,3 +17,178 @@ fn uninit_mask() {
1717 assert ! ( !mask. get( Size :: from_bytes( i) ) , "{i} should not be set" ) ;
1818 }
1919}
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