@@ -103,7 +103,7 @@ impl BitUnpacker {
103103 #[ inline]
104104 pub fn block_num ( & self , idx : u32 ) -> BlockNumber {
105105 // Find the address in bits of the index.
106- let addr_in_bits = ( idx * self . num_bits ) as usize ;
106+ let addr_in_bits = ( idx as usize ) * ( self . num_bits as usize ) ;
107107
108108 // Then round down to the nearest byte.
109109 let addr_in_bytes = addr_in_bits >> 3 ;
@@ -138,10 +138,11 @@ impl BitUnpacker {
138138 /// boundaries.
139139 #[ inline]
140140 pub fn block_oblivious_range ( & self , id_range : Range < u32 > , data_len : usize ) -> Range < usize > {
141- let start_in_bits = id_range. start * self . num_bits ;
142- let start = ( start_in_bits >> 3 ) as usize ;
143- let end_in_bits = id_range. end * self . num_bits ;
144- let end = ( end_in_bits >> 3 ) as usize ;
141+ let start_in_bits = ( id_range. start as usize ) * ( self . num_bits as usize ) ;
142+ let start = start_in_bits >> 3 ;
143+ assert ! ( start <= data_len) ;
144+ let end_in_bits = ( id_range. end as usize ) * ( self . num_bits as usize ) ;
145+ let end = end_in_bits >> 3 ;
145146 // TODO: We fetch more than we need and then truncate.
146147 start..( std:: cmp:: min ( end + 8 , data_len) )
147148 }
@@ -154,16 +155,16 @@ impl BitUnpacker {
154155 /// Get the value at the given idx, which must exist within the given subset of the data.
155156 #[ inline]
156157 pub fn get_from_subset ( & self , idx : u32 , data_offset : usize , data : & [ u8 ] ) -> u64 {
157- let addr_in_bits = idx * self . num_bits ;
158- let addr = ( addr_in_bits >> 3 ) as usize - data_offset;
158+ let addr_in_bits = ( idx as usize ) * ( self . num_bits as usize ) ;
159+ let addr = ( addr_in_bits >> 3 ) - data_offset;
159160 if addr + 8 > data. len ( ) {
160161 if self . num_bits == 0 {
161162 return 0 ;
162163 }
163- let bit_shift = addr_in_bits & 7 ;
164+ let bit_shift = ( addr_in_bits & 7 ) as u32 ;
164165 return self . get_slow_path ( addr, bit_shift, data) ;
165166 }
166- let bit_shift = addr_in_bits & 7 ;
167+ let bit_shift = ( addr_in_bits & 7 ) as u32 ;
167168 let bytes: [ u8 ; 8 ] = ( & data[ addr..addr + 8 ] ) . try_into ( ) . unwrap ( ) ;
168169 let val_unshifted_unmasked: u64 = u64:: from_le_bytes ( bytes) ;
169170 let val_shifted = val_unshifted_unmasked >> bit_shift;
@@ -197,7 +198,7 @@ impl BitUnpacker {
197198
198199 let end_idx = start_idx + output. len ( ) as u32 ;
199200
200- let end_bit_read = end_idx * self . num_bits ;
201+ let end_bit_read = ( end_idx as usize ) * ( self . num_bits as usize ) ;
201202 let end_byte_read = ( end_bit_read + 7 ) / 8 ;
202203 assert ! (
203204 end_byte_read as usize <= data_offset + data. len( ) ,
@@ -237,7 +238,7 @@ impl BitUnpacker {
237238 get_batch_ramp ( start_idx, & mut output[ ..entrance_ramp_len as usize ] ) ;
238239
239240 // Highway
240- let mut offset = ( ( highway_start * self . num_bits ) as usize / 8 ) - data_offset;
241+ let mut offset = ( ( ( highway_start as usize ) * ( self . num_bits as usize ) ) / 8 ) - data_offset;
241242 let mut output_cursor = ( highway_start - start_idx) as usize ;
242243 for _ in 0 ..num_blocks {
243244 offset += BitPacker1x . decompress (
@@ -450,4 +451,22 @@ mod test {
450451 }
451452 }
452453 }
454+
455+ #[ test]
456+ fn test_block_oblivious_range_overflow ( ) {
457+ let bitunpacker = BitUnpacker :: new ( 64 ) ;
458+ // Using IDs that would cause a 32-bit overflow:
459+ // start_doc * 64 bits = 4,294,967,232 (< 2^32)
460+ // end_doc * 64 bits = 4,294,967,360 (> 2^32)
461+ let start_doc = 67_108_863 ;
462+ let end_doc = 67_108_865 ;
463+ let data_len = 1_000_000_000 ;
464+ let range = bitunpacker. block_oblivious_range ( start_doc..end_doc, data_len) ;
465+ assert ! (
466+ range. start <= range. end,
467+ "Range start ({}) must be <= end ({})" ,
468+ range. start,
469+ range. end
470+ ) ;
471+ }
453472}
0 commit comments