@@ -4,6 +4,9 @@ use rustc_index::Idx;
44use crate :: rmeta:: decoder:: MetaBlob ;
55use crate :: rmeta:: * ;
66
7+ const SPARSE_BLOCK_LEN : usize = 64 ;
8+ const SPARSE_INDEX_BYTES : usize = 12 ;
9+
710pub ( super ) trait IsDefault : Default {
811 fn is_default ( & self ) -> bool ;
912}
@@ -465,11 +468,6 @@ impl<Ie: Idx, Id: Idx, const N: usize, T: FixedSizeEncoding<ByteArray = [u8; N]>
465468 ) ;
466469 }
467470 if !value. is_default ( ) {
468- // FIXME(eddyb) investigate more compact encodings for sparse tables.
469- // On the PR @michaelwoerister mentioned:
470- // > Space requirements could perhaps be optimized by using the HAMT `popcnt`
471- // > trick (i.e. divide things into buckets of 32 or 64 items and then
472- // > store bit-masks of which item in each bucket is actually serialized).
473471 let block = self . blocks . ensure_contains_elem ( i, || [ 0 ; N ] ) ;
474472 value. write_to_bytes ( block) ;
475473 if self . width != N {
@@ -483,17 +481,45 @@ impl<Ie: Idx, Id: Idx, const N: usize, T: FixedSizeEncoding<ByteArray = [u8; N]>
483481 let pos = buf. position ( ) ;
484482
485483 let width = self . width ;
484+ let len = self . blocks . len ( ) ;
485+ let index_bytes = len. div_ceil ( SPARSE_BLOCK_LEN ) * SPARSE_INDEX_BYTES ;
486+ let non_defaults = if index_bytes < len * width {
487+ self . blocks . iter ( ) . filter ( |block| * * block != [ 0 ; N ] ) . count ( )
488+ } else {
489+ len
490+ } ;
491+ let sparse =
492+ u32:: try_from ( non_defaults) . is_ok ( ) && index_bytes + non_defaults * width < len * width;
493+
494+ if sparse {
495+ let mut base = 0u32 ;
496+ for chunk in self . blocks . raw . chunks ( SPARSE_BLOCK_LEN ) {
497+ let mut bitmap = 0u64 ;
498+ for ( i, block) in chunk. iter ( ) . enumerate ( ) {
499+ if * block != [ 0 ; N ] {
500+ bitmap |= 1 << i;
501+ }
502+ }
503+ buf. write_array ( bitmap. to_le_bytes ( ) ) ;
504+ buf. write_array ( base. to_le_bytes ( ) ) ;
505+ base += bitmap. count_ones ( ) ;
506+ }
507+ }
508+
486509 for block in & self . blocks {
487- buf. write_with ( |dest| {
488- * dest = * block;
489- width
490- } ) ;
510+ if !sparse || * block != [ 0 ; N ] {
511+ buf. write_with ( |dest| {
512+ * dest = * block;
513+ width
514+ } ) ;
515+ }
491516 }
492517
518+ debug_assert ! ( width <= usize :: MAX >> 1 ) ;
493519 LazyTable :: from_position_and_encoded_size (
494520 NonZero :: new ( pos) . unwrap ( ) ,
495- width,
496- self . blocks . len ( ) ,
521+ ( width << 1 ) | usize :: from ( sparse ) ,
522+ len,
497523 )
498524 }
499525}
@@ -518,8 +544,23 @@ where
518544 return Default :: default ( ) ;
519545 }
520546
521- let width = self . width ;
522- let start = self . position . get ( ) + ( width * i. index ( ) ) ;
547+ let width = self . width_and_sparse >> 1 ;
548+ let mut position = self . position . get ( ) ;
549+ let mut index = i. index ( ) ;
550+ if self . width_and_sparse & 1 != 0 {
551+ let block = index / SPARSE_BLOCK_LEN ;
552+ let bit = 1u64 << ( index % SPARSE_BLOCK_LEN ) ;
553+ let start = position + block * SPARSE_INDEX_BYTES ;
554+ let bitmap = u64:: from_le_bytes ( metadata. blob ( ) [ start..start + 8 ] . try_into ( ) . unwrap ( ) ) ;
555+ if bitmap & bit == 0 {
556+ return Default :: default ( ) ;
557+ }
558+ let base =
559+ u32:: from_le_bytes ( metadata. blob ( ) [ start + 8 ..start + 12 ] . try_into ( ) . unwrap ( ) ) ;
560+ index = base as usize + ( bitmap & ( bit - 1 ) ) . count_ones ( ) as usize ;
561+ position += self . len . div_ceil ( SPARSE_BLOCK_LEN ) * SPARSE_INDEX_BYTES ;
562+ }
563+ let start = position + width * index;
523564 let end = start + width;
524565 let bytes = & metadata. blob ( ) [ start..end] ;
525566
0 commit comments