use crate::decoder::Metadata; use crate::schema::*; use rustc::hir::def_id::{DefId, DefIndex}; use rustc_serialize::{Encodable, opaque::Encoder}; use std::convert::TryInto; use std::marker::PhantomData; use std::num::NonZeroUsize; use log::debug; /// Helper trait, for encoding to, and decoding from, a fixed number of bytes. /// Used mainly for Lazy positions and lengths. /// Unchecked invariant: `Self::default()` should encode as `[0; BYTE_LEN]`, /// but this has no impact on safety. crate trait FixedSizeEncoding: Default { const BYTE_LEN: usize; // FIXME(eddyb) convert to and from `[u8; Self::BYTE_LEN]` instead, // once that starts being allowed by the compiler (i.e. lazy normalization). fn from_bytes(b: &[u8]) -> Self; fn write_to_bytes(self, b: &mut [u8]); // FIXME(eddyb) make these generic functions, or at least defaults here. // (same problem as above, needs `[u8; Self::BYTE_LEN]`) // For now, a macro (`fixed_size_encoding_byte_len_and_defaults`) is used. /// Read a `Self` value (encoded as `Self::BYTE_LEN` bytes), /// from `&b[i * Self::BYTE_LEN..]`, returning `None` if `i` /// is not in bounds, or `Some(Self::from_bytes(...))` otherwise. fn maybe_read_from_bytes_at(b: &[u8], i: usize) -> Option; /// Write a `Self` value (encoded as `Self::BYTE_LEN` bytes), /// at `&mut b[i * Self::BYTE_LEN..]`, using `Self::write_to_bytes`. fn write_to_bytes_at(self, b: &mut [u8], i: usize); } // HACK(eddyb) this shouldn't be needed (see comments on the methods above). macro_rules! fixed_size_encoding_byte_len_and_defaults { ($byte_len:expr) => { const BYTE_LEN: usize = $byte_len; fn maybe_read_from_bytes_at(b: &[u8], i: usize) -> Option { const BYTE_LEN: usize = $byte_len; // HACK(eddyb) ideally this would be done with fully safe code, // but slicing `[u8]` with `i * N..` is optimized worse, due to the // possibility of `i * N` overflowing, than indexing `[[u8; N]]`. let b = unsafe { std::slice::from_raw_parts( b.as_ptr() as *const [u8; BYTE_LEN], b.len() / BYTE_LEN, ) }; b.get(i).map(|b| FixedSizeEncoding::from_bytes(b)) } fn write_to_bytes_at(self, b: &mut [u8], i: usize) { const BYTE_LEN: usize = $byte_len; // HACK(eddyb) ideally this would be done with fully safe code, // see similar comment in `read_from_bytes_at` for why it can't yet. let b = unsafe { std::slice::from_raw_parts_mut( b.as_mut_ptr() as *mut [u8; BYTE_LEN], b.len() / BYTE_LEN, ) }; self.write_to_bytes(&mut b[i]); } } } impl FixedSizeEncoding for u32 { fixed_size_encoding_byte_len_and_defaults!(4); fn from_bytes(b: &[u8]) -> Self { let mut bytes = [0; Self::BYTE_LEN]; bytes.copy_from_slice(&b[..Self::BYTE_LEN]); Self::from_le_bytes(bytes) } fn write_to_bytes(self, b: &mut [u8]) { b[..Self::BYTE_LEN].copy_from_slice(&self.to_le_bytes()); } } // NOTE(eddyb) there could be an impl for `usize`, which would enable a more // generic `Lazy` impl, but in the general case we might not need / want to // fit every `usize` in `u32`. impl FixedSizeEncoding for Option> { fixed_size_encoding_byte_len_and_defaults!(u32::BYTE_LEN); fn from_bytes(b: &[u8]) -> Self { Some(Lazy::from_position(NonZeroUsize::new(u32::from_bytes(b) as usize)?)) } fn write_to_bytes(self, b: &mut [u8]) { let position = self.map_or(0, |lazy| lazy.position.get()); let position: u32 = position.try_into().unwrap(); position.write_to_bytes(b) } } impl FixedSizeEncoding for Option> { fixed_size_encoding_byte_len_and_defaults!(u32::BYTE_LEN * 2); fn from_bytes(b: &[u8]) -> Self { Some(Lazy::from_position_and_meta( >>::from_bytes(b)?.position, u32::from_bytes(&b[u32::BYTE_LEN..]) as usize, )) } fn write_to_bytes(self, b: &mut [u8]) { self.map(|lazy| Lazy::::from_position(lazy.position)) .write_to_bytes(b); let len = self.map_or(0, |lazy| lazy.meta); let len: u32 = len.try_into().unwrap(); len.write_to_bytes(&mut b[u32::BYTE_LEN..]); } } /// Random-access table (i.e. offeringconstant-time `get`/`set`), similar to /// `Vec>`, but without requiring encoding or decoding all the values /// eagerly and in-order. /// A total of `(max_idx + 1) * as FixedSizeEncoding>::BYTE_LEN` bytes /// are used for a table, where `max_idx` is the largest index passed to `set`. // FIXME(eddyb) replace `Vec` with `[_]` here, such that `Box>` would be used // when building it, and `Lazy>` or `&Table` when reading it. // (not sure if that is possible given that the `Vec` is being resized now) crate struct Table where Option: FixedSizeEncoding { // FIXME(eddyb) store `[u8; >::BYTE_LEN]` instead of `u8` in `Vec`, // once that starts being allowed by the compiler (i.e. lazy normalization). bytes: Vec, _marker: PhantomData, } impl Default for Table where Option: FixedSizeEncoding { fn default() -> Self { Table { bytes: vec![], _marker: PhantomData, } } } impl Table where Option: FixedSizeEncoding { crate fn set(&mut self, i: usize, value: T) { // FIXME(eddyb) investigate more compact encodings for sparse tables. // On the PR @michaelwoerister mentioned: // > Space requirements could perhaps be optimized by using the HAMT `popcnt` // > trick (i.e. divide things into buckets of 32 or 64 items and then // > store bit-masks of which item in each bucket is actually serialized). let needed = (i + 1) * >::BYTE_LEN; if self.bytes.len() < needed { self.bytes.resize(needed, 0); } Some(value).write_to_bytes_at(&mut self.bytes, i); } crate fn encode(&self, buf: &mut Encoder) -> Lazy { let pos = buf.position(); buf.emit_raw_bytes(&self.bytes); Lazy::from_position_and_meta( NonZeroUsize::new(pos as usize).unwrap(), self.bytes.len(), ) } } impl LazyMeta for Table where Option: FixedSizeEncoding { type Meta = usize; fn min_size(len: usize) -> usize { len } } impl Lazy> where Option: FixedSizeEncoding { /// Given the metadata, extract out the value at a particular index (if any). #[inline(never)] crate fn get<'a, 'tcx, M: Metadata<'a, 'tcx>>( &self, metadata: M, i: usize, ) -> Option { debug!("Table::lookup: index={:?} len={:?}", i, self.meta); let start = self.position.get(); let bytes = &metadata.raw_bytes()[start..start + self.meta]; >::maybe_read_from_bytes_at(bytes, i)? } } /// Like a `Table` but using `DefIndex` instead of `usize` as keys. // FIXME(eddyb) replace by making `Table` behave like `IndexVec`, // and by using `newtype_index!` to define `DefIndex`. crate struct PerDefTable(Table) where Option: FixedSizeEncoding; impl Default for PerDefTable where Option: FixedSizeEncoding { fn default() -> Self { PerDefTable(Table::default()) } } impl PerDefTable where Option: FixedSizeEncoding { crate fn set(&mut self, def_id: DefId, value: T) { assert!(def_id.is_local()); self.0.set(def_id.index.index(), value); } crate fn encode(&self, buf: &mut Encoder) -> Lazy { let lazy = self.0.encode(buf); Lazy::from_position_and_meta(lazy.position, lazy.meta) } } impl LazyMeta for PerDefTable where Option: FixedSizeEncoding { type Meta = as LazyMeta>::Meta; fn min_size(meta: Self::Meta) -> usize { Table::::min_size(meta) } } impl Lazy> where Option: FixedSizeEncoding { fn as_table(&self) -> Lazy> { Lazy::from_position_and_meta(self.position, self.meta) } /// Given the metadata, extract out the value at a particular DefIndex (if any). #[inline(never)] crate fn get<'a, 'tcx, M: Metadata<'a, 'tcx>>( &self, metadata: M, def_index: DefIndex, ) -> Option { self.as_table().get(metadata, def_index.index()) } }