@@ -69,6 +69,12 @@ pub struct BitUnpacker {
6969 mask : u64 ,
7070}
7171
72+ pub type BlockNumber = usize ;
73+
74+ // 16k
75+ const BLOCK_SIZE_MIN_POW : u8 = 14 ;
76+ const BLOCK_SIZE_MIN : usize = 2 << BLOCK_SIZE_MIN_POW ;
77+
7278impl BitUnpacker {
7379 /// Creates a bit unpacker, that assumes the same bitwidth for all values.
7480 ///
@@ -82,6 +88,7 @@ impl BitUnpacker {
8288 } else {
8389 ( 1u64 << num_bits) - 1u64
8490 } ;
91+
8592 BitUnpacker {
8693 num_bits : u32:: from ( num_bits) ,
8794 mask,
@@ -92,10 +99,63 @@ impl BitUnpacker {
9299 self . num_bits as u8
93100 }
94101
102+ /// Calculates a block number for the given `idx`.
103+ #[ inline]
104+ pub fn block_num ( & self , idx : u32 ) -> BlockNumber {
105+ // Find the address in bits of the index.
106+ let addr_in_bits = ( idx * self . num_bits ) as usize ;
107+
108+ // Then round down to the nearest byte.
109+ let addr_in_bytes = addr_in_bits >> 3 ;
110+
111+ // And compute the containing BlockNumber.
112+ addr_in_bytes >> ( BLOCK_SIZE_MIN_POW + 1 )
113+ }
114+
115+ /// Given a block number and dataset length, calculates a data Range for the block.
116+ pub fn block ( & self , block : BlockNumber , data_len : usize ) -> Range < usize > {
117+ let block_addr = block << ( BLOCK_SIZE_MIN_POW + 1 ) ;
118+ // We extend the end of the block by a constant factor, so that it overlaps the next
119+ // block. That ensures that we never need to read on a block boundary.
120+ block_addr..( std:: cmp:: min ( block_addr + BLOCK_SIZE_MIN + 8 , data_len) )
121+ }
122+
123+ /// Calculates the number of blocks for the given data_len.
124+ ///
125+ /// Usually only called at startup to pre-allocate structures.
126+ pub fn block_count ( & self , data_len : usize ) -> usize {
127+ let block_count = data_len / ( BLOCK_SIZE_MIN as usize ) ;
128+ if data_len % ( BLOCK_SIZE_MIN as usize ) == 0 {
129+ block_count
130+ } else {
131+ block_count + 1
132+ }
133+ }
134+
135+ /// Returns a range within the data which covers the given id_range.
136+ ///
137+ /// NOTE: This method is used for batch reads which bypass blocks to avoid dealing with block
138+ /// boundaries.
139+ #[ inline]
140+ 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 ;
145+ // TODO: We fetch more than we need and then truncate.
146+ start..( std:: cmp:: min ( end + 8 , data_len) )
147+ }
148+
95149 #[ inline]
96150 pub fn get ( & self , idx : u32 , data : & [ u8 ] ) -> u64 {
151+ self . get_from_subset ( idx, 0 , data)
152+ }
153+
154+ /// Get the value at the given idx, which must exist within the given subset of the data.
155+ #[ inline]
156+ pub fn get_from_subset ( & self , idx : u32 , data_offset : usize , data : & [ u8 ] ) -> u64 {
97157 let addr_in_bits = idx * self . num_bits ;
98- let addr = ( addr_in_bits >> 3 ) as usize ;
158+ let addr = ( addr_in_bits >> 3 ) as usize - data_offset ;
99159 if addr + 8 > data. len ( ) {
100160 if self . num_bits == 0 {
101161 return 0 ;
@@ -129,7 +189,7 @@ impl BitUnpacker {
129189 // #Panics
130190 //
131191 // This methods panics if `num_bits` is > 32.
132- fn get_batch_u32s ( & self , start_idx : u32 , data : & [ u8 ] , output : & mut [ u32 ] ) {
192+ fn get_batch_u32s ( & self , start_idx : u32 , data_offset : usize , data : & [ u8 ] , output : & mut [ u32 ] ) {
133193 assert ! (
134194 self . bit_width( ) <= 32 ,
135195 "Bitwidth must be <= 32 to use this method."
@@ -140,14 +200,14 @@ impl BitUnpacker {
140200 let end_bit_read = end_idx * self . num_bits ;
141201 let end_byte_read = ( end_bit_read + 7 ) / 8 ;
142202 assert ! (
143- end_byte_read as usize <= data. len( ) ,
203+ end_byte_read as usize <= data_offset + data. len( ) ,
144204 "Requested index is out of bounds."
145205 ) ;
146206
147207 // Simple slow implementation of get_batch_u32s, to deal with our ramps.
148208 let get_batch_ramp = |start_idx : u32 , output : & mut [ u32 ] | {
149209 for ( out, idx) in output. iter_mut ( ) . zip ( start_idx..) {
150- * out = self . get ( idx, data) as u32 ;
210+ * out = self . get_from_subset ( idx, data_offset , data) as u32 ;
151211 }
152212 } ;
153213
@@ -177,7 +237,7 @@ impl BitUnpacker {
177237 get_batch_ramp ( start_idx, & mut output[ ..entrance_ramp_len as usize ] ) ;
178238
179239 // Highway
180- let mut offset = ( highway_start * self . num_bits ) as usize / 8 ;
240+ let mut offset = ( ( highway_start * self . num_bits ) as usize / 8 ) - data_offset ;
181241 let mut output_cursor = ( highway_start - start_idx) as usize ;
182242 for _ in 0 ..num_blocks {
183243 offset += BitPacker1x . decompress (
@@ -199,31 +259,43 @@ impl BitUnpacker {
199259 id_range : Range < u32 > ,
200260 data : & [ u8 ] ,
201261 positions : & mut Vec < u32 > ,
262+ ) {
263+ self . get_ids_for_value_range_from_subset ( range, id_range, 0 , data, positions)
264+ }
265+
266+ pub fn get_ids_for_value_range_from_subset (
267+ & self ,
268+ range : RangeInclusive < u64 > ,
269+ id_range : Range < u32 > ,
270+ data_offset : usize ,
271+ data : & [ u8 ] ,
272+ positions : & mut Vec < u32 > ,
202273 ) {
203274 if self . bit_width ( ) > 32 {
204- self . get_ids_for_value_range_slow ( range, id_range, data, positions)
275+ self . get_ids_for_value_range_slow ( range, id_range, data_offset , data, positions)
205276 } else {
206277 if * range. start ( ) > u32:: MAX as u64 {
207278 positions. clear ( ) ;
208279 return ;
209280 }
210281 let range_u32 = ( * range. start ( ) as u32 ) ..=( * range. end ( ) ) . min ( u32:: MAX as u64 ) as u32 ;
211- self . get_ids_for_value_range_fast ( range_u32, id_range, data, positions)
282+ self . get_ids_for_value_range_fast ( range_u32, id_range, data_offset , data, positions)
212283 }
213284 }
214285
215286 fn get_ids_for_value_range_slow (
216287 & self ,
217288 range : RangeInclusive < u64 > ,
218289 id_range : Range < u32 > ,
290+ data_offset : usize ,
219291 data : & [ u8 ] ,
220292 positions : & mut Vec < u32 > ,
221293 ) {
222294 positions. clear ( ) ;
223295 for i in id_range {
224296 // If we cared we could make this branchless, but the slow implementation should rarely
225297 // kick in.
226- let val = self . get ( i , data) ;
298+ let val = self . get_from_subset ( i , data_offset , data) ;
227299 if range. contains ( & val) {
228300 positions. push ( i) ;
229301 }
@@ -234,11 +306,12 @@ impl BitUnpacker {
234306 & self ,
235307 value_range : RangeInclusive < u32 > ,
236308 id_range : Range < u32 > ,
309+ data_offset : usize ,
237310 data : & [ u8 ] ,
238311 positions : & mut Vec < u32 > ,
239312 ) {
240313 positions. resize ( id_range. len ( ) , 0u32 ) ;
241- self . get_batch_u32s ( id_range. start , data, positions) ;
314+ self . get_batch_u32s ( id_range. start , data_offset , data, positions) ;
242315 crate :: filter_vec:: filter_vec_in_place ( value_range, id_range. start , positions)
243316 }
244317}
@@ -329,14 +402,14 @@ mod test {
329402 fn test_get_batch_panics_over_32_bits ( ) {
330403 let bitunpacker = BitUnpacker :: new ( 33 ) ;
331404 let mut output: [ u32 ; 1 ] = [ 0u32 ] ;
332- bitunpacker. get_batch_u32s ( 0 , & [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , & mut output[ ..] ) ;
405+ bitunpacker. get_batch_u32s ( 0 , 0 , & [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , & mut output[ ..] ) ;
333406 }
334407
335408 #[ test]
336409 fn test_get_batch_limit ( ) {
337410 let bitunpacker = BitUnpacker :: new ( 1 ) ;
338411 let mut output: [ u32 ; 3 ] = [ 0u32 , 0u32 , 0u32 ] ;
339- bitunpacker. get_batch_u32s ( 8 * 4 - 3 , & [ 0u8 , 0u8 , 0u8 , 0u8 ] , & mut output[ ..] ) ;
412+ bitunpacker. get_batch_u32s ( 8 * 4 - 3 , 0 , & [ 0u8 , 0u8 , 0u8 , 0u8 ] , & mut output[ ..] ) ;
340413 }
341414
342415 #[ test]
@@ -345,7 +418,7 @@ mod test {
345418 let bitunpacker = BitUnpacker :: new ( 1 ) ;
346419 let mut output: [ u32 ; 3 ] = [ 0u32 , 0u32 , 0u32 ] ;
347420 // We are missing exactly one bit.
348- bitunpacker. get_batch_u32s ( 8 * 4 - 2 , & [ 0u8 , 0u8 , 0u8 , 0u8 ] , & mut output[ ..] ) ;
421+ bitunpacker. get_batch_u32s ( 8 * 4 - 2 , 0 , & [ 0u8 , 0u8 , 0u8 , 0u8 ] , & mut output[ ..] ) ;
349422 }
350423
351424 proptest:: proptest! {
@@ -368,7 +441,7 @@ mod test {
368441 for len in [ 0 , 1 , 2 , 32 , 33 , 34 , 64 ] {
369442 for start_idx in 0u32 ..32u32 {
370443 output. resize( len, 0 ) ;
371- bitunpacker. get_batch_u32s( start_idx, & buffer, & mut output) ;
444+ bitunpacker. get_batch_u32s( start_idx, 0 , & buffer, & mut output) ;
372445 for ( i, output_byte) in output. iter( ) . enumerate( ) {
373446 let expected = ( start_idx + i as u32 ) & mask;
374447 assert_eq!( * output_byte, expected) ;
0 commit comments