3 use rustc_data_structures::fingerprint::Fingerprint;
4 use rustc_hir::def::{CtorKind, CtorOf};
5 use rustc_index::vec::Idx;
6 use rustc_middle::ty::ParameterizedOverTcx;
7 use rustc_serialize::opaque::FileEncoder;
8 use rustc_serialize::Encoder as _;
9 use rustc_span::hygiene::MacroKind;
10 use std::marker::PhantomData;
11 use std::num::NonZeroUsize;
13 /// Helper trait, for encoding to, and decoding from, a fixed number of bytes.
14 /// Used mainly for Lazy positions and lengths.
15 /// Unchecked invariant: `Self::default()` should encode as `[0; BYTE_LEN]`,
16 /// but this has no impact on safety.
17 pub(super) trait FixedSizeEncoding: Default {
18 /// This should be `[u8; BYTE_LEN]`;
21 fn from_bytes(b: &Self::ByteArray) -> Self;
22 fn write_to_bytes(self, b: &mut Self::ByteArray);
25 impl FixedSizeEncoding for u32 {
26 type ByteArray = [u8; 4];
29 fn from_bytes(b: &[u8; 4]) -> Self {
30 Self::from_le_bytes(*b)
34 fn write_to_bytes(self, b: &mut [u8; 4]) {
35 *b = self.to_le_bytes();
39 macro_rules! fixed_size_enum {
40 ($ty:ty { $(($($pat:tt)*))* }) => {
41 impl FixedSizeEncoding for Option<$ty> {
42 type ByteArray = [u8;1];
45 fn from_bytes(b: &[u8;1]) -> Self {
51 $(${index()} => Some($($pat)*),)*
52 _ => panic!("Unexpected {} code: {:?}", stringify!($ty), b[0]),
57 fn write_to_bytes(self, b: &mut [u8;1]) {
61 $(Some($($pat)*) => 1 + ${index()},)*
92 ( ImplTraitPlaceholder )
99 ( Static(ast::Mutability::Not) )
100 ( Static(ast::Mutability::Mut) )
101 ( Ctor(CtorOf::Struct, CtorKind::Fn) )
102 ( Ctor(CtorOf::Struct, CtorKind::Const) )
103 ( Ctor(CtorOf::Variant, CtorKind::Fn) )
104 ( Ctor(CtorOf::Variant, CtorKind::Const) )
105 ( Macro(MacroKind::Bang) )
106 ( Macro(MacroKind::Attr) )
107 ( Macro(MacroKind::Derive) )
129 ( Default { has_value: false } )
130 ( Default { has_value: true } )
142 ty::AssocItemContainer {
156 // We directly encode `DefPathHash` because a `LazyValue` would incur a 25% cost.
157 impl FixedSizeEncoding for Option<DefPathHash> {
158 type ByteArray = [u8; 16];
161 fn from_bytes(b: &[u8; 16]) -> Self {
162 Some(DefPathHash(Fingerprint::from_le_bytes(*b)))
166 fn write_to_bytes(self, b: &mut [u8; 16]) {
167 let Some(DefPathHash(fingerprint)) = self else {
168 panic!("Trying to encode absent DefPathHash.")
170 *b = fingerprint.to_le_bytes();
174 // We directly encode RawDefId because using a `LazyValue` would incur a 50% overhead in the worst case.
175 impl FixedSizeEncoding for Option<RawDefId> {
176 type ByteArray = [u8; 8];
179 fn from_bytes(b: &[u8; 8]) -> Self {
180 let krate = u32::from_le_bytes(b[0..4].try_into().unwrap());
181 let index = u32::from_le_bytes(b[4..8].try_into().unwrap());
185 Some(RawDefId { krate: krate - 1, index })
189 fn write_to_bytes(self, b: &mut [u8; 8]) {
192 Some(RawDefId { krate, index }) => {
193 // CrateNum is less than `CrateNum::MAX_AS_U32`.
194 debug_assert!(krate < u32::MAX);
195 b[0..4].copy_from_slice(&(1 + krate).to_le_bytes());
196 b[4..8].copy_from_slice(&index.to_le_bytes());
202 impl FixedSizeEncoding for Option<AttrFlags> {
203 type ByteArray = [u8; 1];
206 fn from_bytes(b: &[u8; 1]) -> Self {
207 (b[0] != 0).then(|| AttrFlags::from_bits_truncate(b[0]))
211 fn write_to_bytes(self, b: &mut [u8; 1]) {
212 b[0] = self.map_or(0, |flags| flags.bits())
216 impl FixedSizeEncoding for Option<()> {
217 type ByteArray = [u8; 1];
220 fn from_bytes(b: &[u8; 1]) -> Self {
221 (b[0] != 0).then(|| ())
225 fn write_to_bytes(self, b: &mut [u8; 1]) {
226 b[0] = self.is_some() as u8
230 // NOTE(eddyb) there could be an impl for `usize`, which would enable a more
231 // generic `LazyValue<T>` impl, but in the general case we might not need / want
232 // to fit every `usize` in `u32`.
233 impl<T> FixedSizeEncoding for Option<LazyValue<T>> {
234 type ByteArray = [u8; 4];
237 fn from_bytes(b: &[u8; 4]) -> Self {
238 let position = NonZeroUsize::new(u32::from_bytes(b) as usize)?;
239 Some(LazyValue::from_position(position))
243 fn write_to_bytes(self, b: &mut [u8; 4]) {
244 let position = self.map_or(0, |lazy| lazy.position.get());
245 let position: u32 = position.try_into().unwrap();
246 position.write_to_bytes(b)
250 impl<T> FixedSizeEncoding for Option<LazyArray<T>> {
251 type ByteArray = [u8; 8];
254 fn from_bytes(b: &[u8; 8]) -> Self {
255 let ([ref position_bytes, ref meta_bytes],[])= b.as_chunks::<4>() else { panic!() };
256 let position = NonZeroUsize::new(u32::from_bytes(position_bytes) as usize)?;
257 let len = u32::from_bytes(meta_bytes) as usize;
258 Some(LazyArray::from_position_and_num_elems(position, len))
262 fn write_to_bytes(self, b: &mut [u8; 8]) {
263 let ([ref mut position_bytes, ref mut meta_bytes],[])= b.as_chunks_mut::<4>() else { panic!() };
265 let position = self.map_or(0, |lazy| lazy.position.get());
266 let position: u32 = position.try_into().unwrap();
267 position.write_to_bytes(position_bytes);
269 let len = self.map_or(0, |lazy| lazy.num_elems);
270 let len: u32 = len.try_into().unwrap();
271 len.write_to_bytes(meta_bytes);
275 /// Helper for constructing a table's serialization (also see `Table`).
276 pub(super) struct TableBuilder<I: Idx, T>
278 Option<T>: FixedSizeEncoding,
280 blocks: IndexVec<I, <Option<T> as FixedSizeEncoding>::ByteArray>,
281 _marker: PhantomData<T>,
284 impl<I: Idx, T> Default for TableBuilder<I, T>
286 Option<T>: FixedSizeEncoding,
288 fn default() -> Self {
289 TableBuilder { blocks: Default::default(), _marker: PhantomData }
293 impl<I: Idx, T> TableBuilder<I, T>
295 Option<T>: FixedSizeEncoding,
297 pub(crate) fn set<const N: usize>(&mut self, i: I, value: T)
299 Option<T>: FixedSizeEncoding<ByteArray = [u8; N]>,
301 // FIXME(eddyb) investigate more compact encodings for sparse tables.
302 // On the PR @michaelwoerister mentioned:
303 // > Space requirements could perhaps be optimized by using the HAMT `popcnt`
304 // > trick (i.e. divide things into buckets of 32 or 64 items and then
305 // > store bit-masks of which item in each bucket is actually serialized).
306 self.blocks.ensure_contains_elem(i, || [0; N]);
307 Some(value).write_to_bytes(&mut self.blocks[i]);
310 pub(crate) fn encode<const N: usize>(&self, buf: &mut FileEncoder) -> LazyTable<I, T>
312 Option<T>: FixedSizeEncoding<ByteArray = [u8; N]>,
314 let pos = buf.position();
315 for block in &self.blocks {
316 buf.emit_raw_bytes(block);
318 let num_bytes = self.blocks.len() * N;
319 LazyTable::from_position_and_encoded_size(
320 NonZeroUsize::new(pos as usize).unwrap(),
326 impl<I: Idx, T: ParameterizedOverTcx> LazyTable<I, T>
328 Option<T>: FixedSizeEncoding,
330 /// Given the metadata, extract out the value at a particular index (if any).
332 pub(super) fn get<'a, 'tcx, M: Metadata<'a, 'tcx>, const N: usize>(
336 ) -> Option<T::Value<'tcx>>
338 Option<T::Value<'tcx>>: FixedSizeEncoding<ByteArray = [u8; N]>,
340 debug!("LazyTable::lookup: index={:?} len={:?}", i, self.encoded_size);
342 let start = self.position.get();
343 let bytes = &metadata.blob()[start..start + self.encoded_size];
344 let (bytes, []) = bytes.as_chunks::<N>() else { panic!() };
345 let bytes = bytes.get(i.index())?;
346 FixedSizeEncoding::from_bytes(bytes)
349 /// Size of the table in entries, including possible gaps.
350 pub(super) fn size<const N: usize>(&self) -> usize
352 for<'tcx> Option<T::Value<'tcx>>: FixedSizeEncoding<ByteArray = [u8; N]>,
354 self.encoded_size / N