@@ -16,76 +16,34 @@ use tracing::debug;
16
16
/// Unchecked invariant: `Self::default()` should encode as `[0; BYTE_LEN]`,
17
17
/// but this has no impact on safety.
18
18
pub ( super ) trait FixedSizeEncoding : Default {
19
- const BYTE_LEN : usize ;
20
-
21
- // FIXME(eddyb) convert to and from `[u8; Self::BYTE_LEN]` instead,
22
- // once that starts being allowed by the compiler (i.e. lazy normalization).
23
- fn from_bytes ( b : & [ u8 ] ) -> Self ;
24
- fn write_to_bytes ( self , b : & mut [ u8 ] ) ;
25
-
26
- // FIXME(eddyb) make these generic functions, or at least defaults here.
27
- // (same problem as above, needs `[u8; Self::BYTE_LEN]`)
28
- // For now, a macro (`fixed_size_encoding_byte_len_and_defaults`) is used.
29
-
30
- /// Read a `Self` value (encoded as `Self::BYTE_LEN` bytes),
31
- /// from `&b[i * Self::BYTE_LEN..]`, returning `None` if `i`
32
- /// is not in bounds, or `Some(Self::from_bytes(...))` otherwise.
33
- fn maybe_read_from_bytes_at ( b : & [ u8 ] , i : usize ) -> Option < Self > ;
34
- /// Write a `Self` value (encoded as `Self::BYTE_LEN` bytes),
35
- /// at `&mut b[i * Self::BYTE_LEN..]`, using `Self::write_to_bytes`.
36
- fn write_to_bytes_at ( self , b : & mut [ u8 ] , i : usize ) ;
37
- }
19
+ /// This should be `[u8; BYTE_LEN]`;
20
+ type ByteArray ;
38
21
39
- // HACK(eddyb) this shouldn't be needed (see comments on the methods above).
40
- macro_rules! fixed_size_encoding_byte_len_and_defaults {
41
- ( $byte_len: expr) => {
42
- const BYTE_LEN : usize = $byte_len;
43
- fn maybe_read_from_bytes_at( b: & [ u8 ] , i: usize ) -> Option <Self > {
44
- const BYTE_LEN : usize = $byte_len;
45
- // HACK(eddyb) ideally this would be done with fully safe code,
46
- // but slicing `[u8]` with `i * N..` is optimized worse, due to the
47
- // possibility of `i * N` overflowing, than indexing `[[u8; N]]`.
48
- let b = unsafe {
49
- std:: slice:: from_raw_parts( b. as_ptr( ) as * const [ u8 ; BYTE_LEN ] , b. len( ) / BYTE_LEN )
50
- } ;
51
- b. get( i) . map( |b| FixedSizeEncoding :: from_bytes( b) )
52
- }
53
- fn write_to_bytes_at( self , b: & mut [ u8 ] , i: usize ) {
54
- const BYTE_LEN : usize = $byte_len;
55
- // HACK(eddyb) ideally this would be done with fully safe code,
56
- // see similar comment in `read_from_bytes_at` for why it can't yet.
57
- let b = unsafe {
58
- std:: slice:: from_raw_parts_mut(
59
- b. as_mut_ptr( ) as * mut [ u8 ; BYTE_LEN ] ,
60
- b. len( ) / BYTE_LEN ,
61
- )
62
- } ;
63
- self . write_to_bytes( & mut b[ i] ) ;
64
- }
65
- } ;
22
+ fn from_bytes ( b : & Self :: ByteArray ) -> Self ;
23
+ fn write_to_bytes ( self , b : & mut Self :: ByteArray ) ;
66
24
}
67
25
68
26
impl FixedSizeEncoding for u32 {
69
- fixed_size_encoding_byte_len_and_defaults ! ( 4 ) ;
27
+ type ByteArray = [ u8 ; 4 ] ;
70
28
71
- fn from_bytes ( b : & [ u8 ] ) -> Self {
72
- let mut bytes = [ 0 ; Self :: BYTE_LEN ] ;
73
- bytes. copy_from_slice ( & b[ ..Self :: BYTE_LEN ] ) ;
74
- Self :: from_le_bytes ( bytes)
29
+ #[ inline]
30
+ fn from_bytes ( b : & [ u8 ; 4 ] ) -> Self {
31
+ Self :: from_le_bytes ( * b)
75
32
}
76
33
77
- fn write_to_bytes ( self , b : & mut [ u8 ] ) {
78
- b[ ..Self :: BYTE_LEN ] . copy_from_slice ( & self . to_le_bytes ( ) ) ;
34
+ #[ inline]
35
+ fn write_to_bytes ( self , b : & mut [ u8 ; 4 ] ) {
36
+ * b = self . to_le_bytes ( ) ;
79
37
}
80
38
}
81
39
82
40
macro_rules! fixed_size_enum {
83
41
( $ty: ty { $( ( $( $pat: tt) * ) ) * } ) => {
84
42
impl FixedSizeEncoding for Option <$ty> {
85
- fixed_size_encoding_byte_len_and_defaults! ( 1 ) ;
43
+ type ByteArray = [ u8 ; 1 ] ;
86
44
87
45
#[ inline]
88
- fn from_bytes( b: & [ u8 ] ) -> Self {
46
+ fn from_bytes( b: & [ u8 ; 1 ] ) -> Self {
89
47
use $ty:: * ;
90
48
if b[ 0 ] == 0 {
91
49
return None ;
@@ -97,7 +55,7 @@ macro_rules! fixed_size_enum {
97
55
}
98
56
99
57
#[ inline]
100
- fn write_to_bytes( self , b: & mut [ u8 ] ) {
58
+ fn write_to_bytes( self , b: & mut [ u8 ; 1 ] ) {
101
59
use $ty:: * ;
102
60
b[ 0 ] = match self {
103
61
None => 0 ,
@@ -184,45 +142,45 @@ fixed_size_enum! {
184
142
185
143
// We directly encode `DefPathHash` because a `Lazy` would encur a 25% cost.
186
144
impl FixedSizeEncoding for Option < DefPathHash > {
187
- fixed_size_encoding_byte_len_and_defaults ! ( 16 ) ;
145
+ type ByteArray = [ u8 ; 16 ] ;
188
146
189
147
#[ inline]
190
- fn from_bytes ( b : & [ u8 ] ) -> Self {
191
- Some ( DefPathHash ( Fingerprint :: from_le_bytes ( b . try_into ( ) . unwrap ( ) ) ) )
148
+ fn from_bytes ( b : & [ u8 ; 16 ] ) -> Self {
149
+ Some ( DefPathHash ( Fingerprint :: from_le_bytes ( * b ) ) )
192
150
}
193
151
194
152
#[ inline]
195
- fn write_to_bytes ( self , b : & mut [ u8 ] ) {
153
+ fn write_to_bytes ( self , b : & mut [ u8 ; 16 ] ) {
196
154
let Some ( DefPathHash ( fingerprint) ) = self else {
197
155
panic ! ( "Trying to encode absent DefPathHash." )
198
156
} ;
199
- b [ .. Self :: BYTE_LEN ] . copy_from_slice ( & fingerprint. to_le_bytes ( ) ) ;
157
+ * b = fingerprint. to_le_bytes ( ) ;
200
158
}
201
159
}
202
160
203
161
// We directly encode RawDefId because using a `Lazy` would incur a 50% overhead in the worst case.
204
162
impl FixedSizeEncoding for Option < RawDefId > {
205
- fixed_size_encoding_byte_len_and_defaults ! ( 2 * u32 :: BYTE_LEN ) ;
163
+ type ByteArray = [ u8 ; 8 ] ;
206
164
207
165
#[ inline]
208
- fn from_bytes ( b : & [ u8 ] ) -> Self {
209
- let krate = u32:: from_bytes ( & b[ 0 ..4 ] ) ;
210
- let index = u32:: from_bytes ( & b[ 4 ..8 ] ) ;
166
+ fn from_bytes ( b : & [ u8 ; 8 ] ) -> Self {
167
+ let krate = u32:: from_le_bytes ( b[ 0 ..4 ] . try_into ( ) . unwrap ( ) ) ;
168
+ let index = u32:: from_le_bytes ( b[ 4 ..8 ] . try_into ( ) . unwrap ( ) ) ;
211
169
if krate == 0 {
212
170
return None ;
213
171
}
214
172
Some ( RawDefId { krate : krate - 1 , index } )
215
173
}
216
174
217
175
#[ inline]
218
- fn write_to_bytes ( self , b : & mut [ u8 ] ) {
176
+ fn write_to_bytes ( self , b : & mut [ u8 ; 8 ] ) {
219
177
match self {
220
- None => 0u32 . write_to_bytes ( b ) ,
178
+ None => * b = [ 0 ; 8 ] ,
221
179
Some ( RawDefId { krate, index } ) => {
222
180
// CrateNum is less than `CrateNum::MAX_AS_U32`.
223
181
debug_assert ! ( krate < u32 :: MAX ) ;
224
- ( 1 + krate) . write_to_bytes ( & mut b [ 0 .. 4 ] ) ;
225
- index . write_to_bytes ( & mut b[ 4 ..8 ] ) ;
182
+ b [ 0 .. 4 ] . copy_from_slice ( & ( 1 + krate) . to_le_bytes ( ) ) ;
183
+ b[ 4 ..8 ] . copy_from_slice ( & index . to_le_bytes ( ) ) ;
226
184
}
227
185
}
228
186
}
@@ -232,44 +190,51 @@ impl FixedSizeEncoding for Option<RawDefId> {
232
190
// generic `Lazy<T>` impl, but in the general case we might not need / want to
233
191
// fit every `usize` in `u32`.
234
192
impl < T > FixedSizeEncoding for Option < Lazy < T > > {
235
- fixed_size_encoding_byte_len_and_defaults ! ( u32 :: BYTE_LEN ) ;
193
+ type ByteArray = [ u8 ; 4 ] ;
236
194
237
- fn from_bytes ( b : & [ u8 ] ) -> Self {
238
- Some ( Lazy :: from_position ( NonZeroUsize :: new ( u32:: from_bytes ( b) as usize ) ?) )
195
+ #[ inline]
196
+ fn from_bytes ( b : & [ u8 ; 4 ] ) -> Self {
197
+ let position = NonZeroUsize :: new ( u32:: from_bytes ( b) as usize ) ?;
198
+ Some ( Lazy :: from_position ( position) )
239
199
}
240
200
241
- fn write_to_bytes ( self , b : & mut [ u8 ] ) {
201
+ #[ inline]
202
+ fn write_to_bytes ( self , b : & mut [ u8 ; 4 ] ) {
242
203
let position = self . map_or ( 0 , |lazy| lazy. position . get ( ) ) ;
243
204
let position: u32 = position. try_into ( ) . unwrap ( ) ;
244
-
245
205
position. write_to_bytes ( b)
246
206
}
247
207
}
248
208
249
209
impl < T > FixedSizeEncoding for Option < Lazy < [ T ] > > {
250
- fixed_size_encoding_byte_len_and_defaults ! ( u32 :: BYTE_LEN * 2 ) ;
210
+ type ByteArray = [ u8 ; 8 ] ;
251
211
252
- fn from_bytes ( b : & [ u8 ] ) -> Self {
253
- Some ( Lazy :: from_position_and_meta (
254
- <Option < Lazy < T > > >:: from_bytes ( b) ?. position ,
255
- u32:: from_bytes ( & b[ u32:: BYTE_LEN ..] ) as usize ,
256
- ) )
212
+ #[ inline]
213
+ fn from_bytes ( b : & [ u8 ; 8 ] ) -> Self {
214
+ let ( [ ref position_bytes, ref meta_bytes] , [ ] ) = b. as_chunks :: < 4 > ( ) else { panic ! ( ) } ;
215
+ let position = NonZeroUsize :: new ( u32:: from_bytes ( position_bytes) as usize ) ?;
216
+ let len = u32:: from_bytes ( meta_bytes) as usize ;
217
+ Some ( Lazy :: from_position_and_meta ( position, len) )
257
218
}
258
219
259
- fn write_to_bytes ( self , b : & mut [ u8 ] ) {
260
- self . map ( |lazy| Lazy :: < T > :: from_position ( lazy. position ) ) . write_to_bytes ( b) ;
220
+ #[ inline]
221
+ fn write_to_bytes ( self , b : & mut [ u8 ; 8 ] ) {
222
+ let ( [ ref mut position_bytes, ref mut meta_bytes] , [ ] ) = b. as_chunks_mut :: < 4 > ( ) else { panic ! ( ) } ;
223
+
224
+ let position = self . map_or ( 0 , |lazy| lazy. position . get ( ) ) ;
225
+ let position: u32 = position. try_into ( ) . unwrap ( ) ;
226
+ position. write_to_bytes ( position_bytes) ;
261
227
262
228
let len = self . map_or ( 0 , |lazy| lazy. meta ) ;
263
229
let len: u32 = len. try_into ( ) . unwrap ( ) ;
264
-
265
- len. write_to_bytes ( & mut b[ u32:: BYTE_LEN ..] ) ;
230
+ len. write_to_bytes ( meta_bytes) ;
266
231
}
267
232
}
268
233
269
234
/// Random-access table (i.e. offering constant-time `get`/`set`), similar to
270
235
/// `Vec<Option<T>>`, but without requiring encoding or decoding all the values
271
236
/// eagerly and in-order.
272
- /// A total of `(max_idx + 1) * < Option<T> as FixedSizeEncoding>::BYTE_LEN` bytes
237
+ /// A total of `(max_idx + 1)` times ` Option<T> as FixedSizeEncoding>::ByteArray`
273
238
/// are used for a table, where `max_idx` is the largest index passed to
274
239
/// `TableBuilder::set`.
275
240
pub ( super ) struct Table < I : Idx , T >
@@ -287,53 +252,54 @@ pub(super) struct TableBuilder<I: Idx, T>
287
252
where
288
253
Option < T > : FixedSizeEncoding ,
289
254
{
290
- // FIXME(eddyb) use `IndexVec<I, [u8; <Option<T>>::BYTE_LEN]>` instead of
291
- // `Vec<u8>`, once that starts working (i.e. lazy normalization).
292
- // Then again, that has the downside of not allowing `TableBuilder::encode` to
293
- // obtain a `&[u8]` entirely in safe code, for writing the bytes out.
294
- bytes : Vec < u8 > ,
295
- _marker : PhantomData < ( fn ( & I ) , T ) > ,
255
+ blocks : IndexVec < I , <Option < T > as FixedSizeEncoding >:: ByteArray > ,
256
+ _marker : PhantomData < T > ,
296
257
}
297
258
298
259
impl < I : Idx , T > Default for TableBuilder < I , T >
299
260
where
300
261
Option < T > : FixedSizeEncoding ,
301
262
{
302
263
fn default ( ) -> Self {
303
- TableBuilder { bytes : vec ! [ ] , _marker : PhantomData }
264
+ TableBuilder { blocks : Default :: default ( ) , _marker : PhantomData }
304
265
}
305
266
}
306
267
307
268
impl < I : Idx , T > TableBuilder < I , T >
308
269
where
309
270
Option < T > : FixedSizeEncoding ,
310
271
{
311
- pub ( crate ) fn set ( & mut self , i : I , value : T ) {
272
+ pub ( crate ) fn set < const N : usize > ( & mut self , i : I , value : T )
273
+ where
274
+ Option < T > : FixedSizeEncoding < ByteArray = [ u8 ; N ] > ,
275
+ {
312
276
// FIXME(eddyb) investigate more compact encodings for sparse tables.
313
277
// On the PR @michaelwoerister mentioned:
314
278
// > Space requirements could perhaps be optimized by using the HAMT `popcnt`
315
279
// > trick (i.e. divide things into buckets of 32 or 64 items and then
316
280
// > store bit-masks of which item in each bucket is actually serialized).
317
- let i = i. index ( ) ;
318
- let needed = ( i + 1 ) * <Option < T > >:: BYTE_LEN ;
319
- if self . bytes . len ( ) < needed {
320
- self . bytes . resize ( needed, 0 ) ;
321
- }
322
-
323
- Some ( value) . write_to_bytes_at ( & mut self . bytes , i) ;
281
+ self . blocks . ensure_contains_elem ( i, || [ 0 ; N ] ) ;
282
+ Some ( value) . write_to_bytes ( & mut self . blocks [ i] ) ;
324
283
}
325
284
326
- pub ( crate ) fn encode ( & self , buf : & mut Encoder ) -> Lazy < Table < I , T > > {
285
+ pub ( crate ) fn encode < const N : usize > ( & self , buf : & mut Encoder ) -> Lazy < Table < I , T > >
286
+ where
287
+ Option < T > : FixedSizeEncoding < ByteArray = [ u8 ; N ] > ,
288
+ {
327
289
let pos = buf. position ( ) ;
328
- buf. emit_raw_bytes ( & self . bytes ) . unwrap ( ) ;
329
- Lazy :: from_position_and_meta ( NonZeroUsize :: new ( pos as usize ) . unwrap ( ) , self . bytes . len ( ) )
290
+ for block in & self . blocks {
291
+ buf. emit_raw_bytes ( block) . unwrap ( ) ;
292
+ }
293
+ let num_bytes = self . blocks . len ( ) * N ;
294
+ Lazy :: from_position_and_meta ( NonZeroUsize :: new ( pos as usize ) . unwrap ( ) , num_bytes)
330
295
}
331
296
}
332
297
333
298
impl < I : Idx , T > LazyMeta for Table < I , T >
334
299
where
335
300
Option < T > : FixedSizeEncoding ,
336
301
{
302
+ /// Number of bytes in the data stream.
337
303
type Meta = usize ;
338
304
}
339
305
@@ -343,16 +309,28 @@ where
343
309
{
344
310
/// Given the metadata, extract out the value at a particular index (if any).
345
311
#[ inline( never) ]
346
- pub ( super ) fn get < ' a , ' tcx , M : Metadata < ' a , ' tcx > > ( & self , metadata : M , i : I ) -> Option < T > {
312
+ pub ( super ) fn get < ' a , ' tcx , M : Metadata < ' a , ' tcx > , const N : usize > (
313
+ & self ,
314
+ metadata : M ,
315
+ i : I ,
316
+ ) -> Option < T >
317
+ where
318
+ Option < T > : FixedSizeEncoding < ByteArray = [ u8 ; N ] > ,
319
+ {
347
320
debug ! ( "Table::lookup: index={:?} len={:?}" , i, self . meta) ;
348
321
349
322
let start = self . position . get ( ) ;
350
323
let bytes = & metadata. blob ( ) [ start..start + self . meta ] ;
351
- <Option < T > >:: maybe_read_from_bytes_at ( bytes, i. index ( ) ) ?
324
+ let ( bytes, [ ] ) = bytes. as_chunks :: < N > ( ) else { panic ! ( ) } ;
325
+ let bytes = bytes. get ( i. index ( ) ) ?;
326
+ FixedSizeEncoding :: from_bytes ( bytes)
352
327
}
353
328
354
329
/// Size of the table in entries, including possible gaps.
355
- pub ( super ) fn size ( & self ) -> usize {
356
- self . meta / <Option < T > >:: BYTE_LEN
330
+ pub ( super ) fn size < const N : usize > ( & self ) -> usize
331
+ where
332
+ Option < T > : FixedSizeEncoding < ByteArray = [ u8 ; N ] > ,
333
+ {
334
+ self . meta / N
357
335
}
358
336
}
0 commit comments