@@ -38,6 +38,12 @@ impl IsDefault for u32 {
38
38
}
39
39
}
40
40
41
+ impl IsDefault for u64 {
42
+ fn is_default ( & self ) -> bool {
43
+ * self == 0
44
+ }
45
+ }
46
+
41
47
impl < T > IsDefault for LazyArray < T > {
42
48
fn is_default ( & self ) -> bool {
43
49
self . num_elems == 0
@@ -89,6 +95,20 @@ impl FixedSizeEncoding for u32 {
89
95
}
90
96
}
91
97
98
+ impl FixedSizeEncoding for u64 {
99
+ type ByteArray = [ u8 ; 8 ] ;
100
+
101
+ #[ inline]
102
+ fn from_bytes ( b : & [ u8 ; 8 ] ) -> Self {
103
+ Self :: from_le_bytes ( * b)
104
+ }
105
+
106
+ #[ inline]
107
+ fn write_to_bytes ( self , b : & mut [ u8 ; 8 ] ) {
108
+ * b = self . to_le_bytes ( ) ;
109
+ }
110
+ }
111
+
92
112
macro_rules! fixed_size_enum {
93
113
( $ty: ty { $( ( $( $pat: tt) * ) ) * } ) => {
94
114
impl FixedSizeEncoding for Option <$ty> {
@@ -300,21 +320,21 @@ impl FixedSizeEncoding for UnusedGenericParams {
300
320
// generic `LazyValue<T>` impl, but in the general case we might not need / want
301
321
// to fit every `usize` in `u32`.
302
322
impl < T > FixedSizeEncoding for Option < LazyValue < T > > {
303
- type ByteArray = [ u8 ; 4 ] ;
323
+ type ByteArray = [ u8 ; 8 ] ;
304
324
305
325
#[ inline]
306
- fn from_bytes ( b : & [ u8 ; 4 ] ) -> Self {
307
- let position = NonZeroUsize :: new ( u32 :: from_bytes ( b) as usize ) ?;
326
+ fn from_bytes ( b : & [ u8 ; 8 ] ) -> Self {
327
+ let position = NonZeroUsize :: new ( u64 :: from_bytes ( b) as usize ) ?;
308
328
Some ( LazyValue :: from_position ( position) )
309
329
}
310
330
311
331
#[ inline]
312
- fn write_to_bytes ( self , b : & mut [ u8 ; 4 ] ) {
332
+ fn write_to_bytes ( self , b : & mut [ u8 ; 8 ] ) {
313
333
match self {
314
334
None => unreachable ! ( ) ,
315
335
Some ( lazy) => {
316
336
let position = lazy. position . get ( ) ;
317
- let position: u32 = position. try_into ( ) . unwrap ( ) ;
337
+ let position: u64 = position. try_into ( ) . unwrap ( ) ;
318
338
position. write_to_bytes ( b)
319
339
}
320
340
}
@@ -323,55 +343,75 @@ impl<T> FixedSizeEncoding for Option<LazyValue<T>> {
323
343
324
344
impl < T > LazyArray < T > {
325
345
#[ inline]
326
- fn write_to_bytes_impl ( self , b : & mut [ u8 ; 8 ] ) {
327
- let ( [ position_bytes, meta_bytes] , [ ] ) = b. as_chunks_mut :: < 4 > ( ) else { panic ! ( ) } ;
328
-
329
- let position = self . position . get ( ) ;
330
- let position: u32 = position. try_into ( ) . unwrap ( ) ;
331
- position. write_to_bytes ( position_bytes) ;
332
-
333
- let len = self . num_elems ;
334
- let len: u32 = len. try_into ( ) . unwrap ( ) ;
335
- len. write_to_bytes ( meta_bytes) ;
346
+ fn write_to_bytes_impl ( self , b : & mut [ u8 ; 16 ] ) {
347
+ let position = ( self . position . get ( ) as u64 ) . to_le_bytes ( ) ;
348
+ let len = ( self . num_elems as u64 ) . to_le_bytes ( ) ;
349
+
350
+ // Element width is selected at runtime on a per-table basis by omitting trailing
351
+ // zero bytes in table elements. This works very naturally when table elements are
352
+ // simple numbers but `LazyArray` is a pair of integers. If naively encoded, the second
353
+ // element would shield the trailing zeroes in the first. Interleaving the bytes
354
+ // of the position and length exposes trailing zeroes in both to the optimization.
355
+ // We encode length second because we generally expect it to be smaller.
356
+ for i in 0 ..8 {
357
+ b[ 2 * i] = position[ i] ;
358
+ b[ 2 * i + 1 ] = len[ i] ;
359
+ }
336
360
}
337
361
338
- fn from_bytes_impl ( position_bytes : & [ u8 ; 4 ] , meta_bytes : & [ u8 ; 4 ] ) -> Option < LazyArray < T > > {
339
- let position = NonZeroUsize :: new ( u32 :: from_bytes ( position_bytes ) as usize ) ?;
340
- let len = u32 :: from_bytes ( meta_bytes ) as usize ;
362
+ fn from_bytes_impl ( position : & [ u8 ; 8 ] , meta : & [ u8 ; 8 ] ) -> Option < LazyArray < T > > {
363
+ let position = NonZeroUsize :: new ( u64 :: from_bytes ( & position ) as usize ) ?;
364
+ let len = u64 :: from_bytes ( & meta ) as usize ;
341
365
Some ( LazyArray :: from_position_and_num_elems ( position, len) )
342
366
}
343
367
}
344
368
369
+ // Decoding helper for the encoding scheme used by `LazyArray`.
370
+ // Interleaving the bytes of the two integers exposes trailing bytes in the first integer
371
+ // to the varint scheme that we use for tables.
372
+ #[ inline]
373
+ fn decode_interleaved ( encoded : & [ u8 ; 16 ] ) -> ( [ u8 ; 8 ] , [ u8 ; 8 ] ) {
374
+ let mut first = [ 0u8 ; 8 ] ;
375
+ let mut second = [ 0u8 ; 8 ] ;
376
+ for i in 0 ..8 {
377
+ first[ i] = encoded[ 2 * i] ;
378
+ second[ i] = encoded[ 2 * i + 1 ] ;
379
+ }
380
+ ( first, second)
381
+ }
382
+
345
383
impl < T > FixedSizeEncoding for LazyArray < T > {
346
- type ByteArray = [ u8 ; 8 ] ;
384
+ type ByteArray = [ u8 ; 16 ] ;
347
385
348
386
#[ inline]
349
- fn from_bytes ( b : & [ u8 ; 8 ] ) -> Self {
350
- let ( [ position_bytes, meta_bytes] , [ ] ) = b. as_chunks :: < 4 > ( ) else { panic ! ( ) } ;
351
- if * meta_bytes == [ 0 ; 4 ] {
387
+ fn from_bytes ( b : & [ u8 ; 16 ] ) -> Self {
388
+ let ( position, meta) = decode_interleaved ( b) ;
389
+
390
+ if meta == [ 0 ; 8 ] {
352
391
return Default :: default ( ) ;
353
392
}
354
- LazyArray :: from_bytes_impl ( position_bytes , meta_bytes ) . unwrap ( )
393
+ LazyArray :: from_bytes_impl ( & position , & meta ) . unwrap ( )
355
394
}
356
395
357
396
#[ inline]
358
- fn write_to_bytes ( self , b : & mut [ u8 ; 8 ] ) {
397
+ fn write_to_bytes ( self , b : & mut [ u8 ; 16 ] ) {
359
398
assert ! ( !self . is_default( ) ) ;
360
399
self . write_to_bytes_impl ( b)
361
400
}
362
401
}
363
402
364
403
impl < T > FixedSizeEncoding for Option < LazyArray < T > > {
365
- type ByteArray = [ u8 ; 8 ] ;
404
+ type ByteArray = [ u8 ; 16 ] ;
366
405
367
406
#[ inline]
368
- fn from_bytes ( b : & [ u8 ; 8 ] ) -> Self {
369
- let ( [ position_bytes, meta_bytes] , [ ] ) = b. as_chunks :: < 4 > ( ) else { panic ! ( ) } ;
370
- LazyArray :: from_bytes_impl ( position_bytes, meta_bytes)
407
+ fn from_bytes ( b : & [ u8 ; 16 ] ) -> Self {
408
+ let ( position, meta) = decode_interleaved ( b) ;
409
+
410
+ LazyArray :: from_bytes_impl ( & position, & meta)
371
411
}
372
412
373
413
#[ inline]
374
- fn write_to_bytes ( self , b : & mut [ u8 ; 8 ] ) {
414
+ fn write_to_bytes ( self , b : & mut [ u8 ; 16 ] ) {
375
415
match self {
376
416
None => unreachable ! ( ) ,
377
417
Some ( lazy) => lazy. write_to_bytes_impl ( b) ,
@@ -381,13 +421,14 @@ impl<T> FixedSizeEncoding for Option<LazyArray<T>> {
381
421
382
422
/// Helper for constructing a table's serialization (also see `Table`).
383
423
pub ( super ) struct TableBuilder < I : Idx , T : FixedSizeEncoding > {
424
+ width : usize ,
384
425
blocks : IndexVec < I , T :: ByteArray > ,
385
426
_marker : PhantomData < T > ,
386
427
}
387
428
388
429
impl < I : Idx , T : FixedSizeEncoding > Default for TableBuilder < I , T > {
389
430
fn default ( ) -> Self {
390
- TableBuilder { blocks : Default :: default ( ) , _marker : PhantomData }
431
+ TableBuilder { width : 0 , blocks : Default :: default ( ) , _marker : PhantomData }
391
432
}
392
433
}
393
434
@@ -415,40 +456,63 @@ impl<I: Idx, const N: usize, T: FixedSizeEncoding<ByteArray = [u8; N]>> TableBui
415
456
// > store bit-masks of which item in each bucket is actually serialized).
416
457
let block = self . blocks . ensure_contains_elem ( i, || [ 0 ; N ] ) ;
417
458
value. write_to_bytes ( block) ;
459
+ if self . width != N {
460
+ let width = N - trailing_zeros ( block) ;
461
+ self . width = self . width . max ( width) ;
462
+ }
418
463
}
419
464
}
420
465
421
466
pub ( crate ) fn encode ( & self , buf : & mut FileEncoder ) -> LazyTable < I , T > {
422
467
let pos = buf. position ( ) ;
468
+
469
+ let width = self . width ;
423
470
for block in & self . blocks {
424
- buf. emit_raw_bytes ( block) ;
471
+ buf. emit_raw_bytes ( & block[ ..width ] ) ;
425
472
}
426
- let num_bytes = self . blocks . len ( ) * N ;
473
+
427
474
LazyTable :: from_position_and_encoded_size (
428
475
NonZeroUsize :: new ( pos as usize ) . unwrap ( ) ,
429
- num_bytes,
476
+ width,
477
+ self . blocks . len ( ) ,
430
478
)
431
479
}
432
480
}
433
481
482
+ fn trailing_zeros ( x : & [ u8 ] ) -> usize {
483
+ x. iter ( ) . rev ( ) . take_while ( |b| * * b == 0 ) . count ( )
484
+ }
485
+
434
486
impl < I : Idx , const N : usize , T : FixedSizeEncoding < ByteArray = [ u8 ; N ] > + ParameterizedOverTcx >
435
487
LazyTable < I , T >
436
488
where
437
489
for < ' tcx > T :: Value < ' tcx > : FixedSizeEncoding < ByteArray = [ u8 ; N ] > ,
438
490
{
439
491
/// Given the metadata, extract out the value at a particular index (if any).
440
- #[ inline( never) ]
441
492
pub ( super ) fn get < ' a , ' tcx , M : Metadata < ' a , ' tcx > > ( & self , metadata : M , i : I ) -> T :: Value < ' tcx > {
442
- trace ! ( "LazyTable::lookup: index={:?} len={:?}" , i, self . encoded_size) ;
493
+ trace ! ( "LazyTable::lookup: index={:?} len={:?}" , i, self . len) ;
494
+
495
+ // Access past the end of the table returns a Default
496
+ if i. index ( ) >= self . len {
497
+ return Default :: default ( ) ;
498
+ }
443
499
444
- let start = self . position . get ( ) ;
445
- let bytes = & metadata. blob ( ) [ start..start + self . encoded_size ] ;
446
- let ( bytes, [ ] ) = bytes. as_chunks :: < N > ( ) else { panic ! ( ) } ;
447
- bytes. get ( i. index ( ) ) . map_or_else ( Default :: default, FixedSizeEncoding :: from_bytes)
500
+ let width = self . width ;
501
+ let start = self . position . get ( ) + ( width * i. index ( ) ) ;
502
+ let end = start + width;
503
+ let bytes = & metadata. blob ( ) [ start..end] ;
504
+
505
+ if let Ok ( fixed) = bytes. try_into ( ) {
506
+ FixedSizeEncoding :: from_bytes ( fixed)
507
+ } else {
508
+ let mut fixed = [ 0u8 ; N ] ;
509
+ fixed[ ..width] . copy_from_slice ( bytes) ;
510
+ FixedSizeEncoding :: from_bytes ( & fixed)
511
+ }
448
512
}
449
513
450
514
/// Size of the table in entries, including possible gaps.
451
515
pub ( super ) fn size ( & self ) -> usize {
452
- self . encoded_size / N
516
+ self . len
453
517
}
454
518
}
0 commit comments