2019-11-04 11:57:17 +03:00
|
|
|
use crate::rmeta::*;
|
2016-09-16 17:25:54 +03:00
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
use log::debug;
|
2019-11-13 22:10:58 +02:00
|
|
|
use rustc_index::vec::Idx;
|
2019-12-22 17:42:04 -05:00
|
|
|
use rustc_serialize::{opaque::Encoder, Encodable};
|
2019-04-11 19:56:59 +03:00
|
|
|
use std::convert::TryInto;
|
2019-04-11 10:22:02 +03:00
|
|
|
use std::marker::PhantomData;
|
2019-04-11 18:24:38 +03:00
|
|
|
use std::num::NonZeroUsize;
|
2015-09-03 01:22:31 +03:00
|
|
|
|
2019-04-11 20:05:36 +03:00
|
|
|
/// Helper trait, for encoding to, and decoding from, a fixed number of bytes.
|
2019-04-13 23:30:53 +03:00
|
|
|
/// Used mainly for Lazy positions and lengths.
|
|
|
|
/// Unchecked invariant: `Self::default()` should encode as `[0; BYTE_LEN]`,
|
|
|
|
/// but this has no impact on safety.
|
2019-11-03 17:33:27 +03:00
|
|
|
pub(super) trait FixedSizeEncoding: Default {
|
2019-04-11 20:05:36 +03:00
|
|
|
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.
|
2019-10-14 18:42:08 +03:00
|
|
|
|
|
|
|
/// 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.
|
2019-04-14 05:21:39 +03:00
|
|
|
fn maybe_read_from_bytes_at(b: &[u8], i: usize) -> Option<Self>;
|
2019-10-14 18:42:08 +03:00
|
|
|
/// Write a `Self` value (encoded as `Self::BYTE_LEN` bytes),
|
|
|
|
/// at `&mut b[i * Self::BYTE_LEN..]`, using `Self::write_to_bytes`.
|
2019-04-11 20:05:36 +03:00
|
|
|
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;
|
2019-04-14 05:21:39 +03:00
|
|
|
fn maybe_read_from_bytes_at(b: &[u8], i: usize) -> Option<Self> {
|
2019-04-11 20:05:36 +03:00
|
|
|
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,
|
|
|
|
)
|
|
|
|
};
|
2019-04-14 05:21:39 +03:00
|
|
|
b.get(i).map(|b| FixedSizeEncoding::from_bytes(b))
|
2019-04-11 20:05:36 +03:00
|
|
|
}
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-13 23:30:53 +03:00
|
|
|
// NOTE(eddyb) there could be an impl for `usize`, which would enable a more
|
|
|
|
// generic `Lazy<T>` impl, but in the general case we might not need / want to
|
|
|
|
// fit every `usize` in `u32`.
|
|
|
|
impl<T: Encodable> FixedSizeEncoding for Option<Lazy<T>> {
|
|
|
|
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<T: Encodable> FixedSizeEncoding for Option<Lazy<[T]>> {
|
|
|
|
fixed_size_encoding_byte_len_and_defaults!(u32::BYTE_LEN * 2);
|
|
|
|
|
|
|
|
fn from_bytes(b: &[u8]) -> Self {
|
|
|
|
Some(Lazy::from_position_and_meta(
|
|
|
|
<Option<Lazy<T>>>::from_bytes(b)?.position,
|
|
|
|
u32::from_bytes(&b[u32::BYTE_LEN..]) as usize,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_to_bytes(self, b: &mut [u8]) {
|
2019-12-22 17:42:04 -05:00
|
|
|
self.map(|lazy| Lazy::<T>::from_position(lazy.position)).write_to_bytes(b);
|
2019-04-13 23:30:53 +03:00
|
|
|
|
|
|
|
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..]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-14 02:44:24 +02:00
|
|
|
/// Random-access table (i.e. offering constant-time `get`/`set`), similar to
|
2019-10-14 18:42:08 +03:00
|
|
|
/// `Vec<Option<T>>`, but without requiring encoding or decoding all the values
|
|
|
|
/// eagerly and in-order.
|
|
|
|
/// A total of `(max_idx + 1) * <Option<T> as FixedSizeEncoding>::BYTE_LEN` bytes
|
2019-11-14 02:44:24 +02:00
|
|
|
/// are used for a table, where `max_idx` is the largest index passed to
|
|
|
|
/// `TableBuilder::set`.
|
2019-12-22 17:42:04 -05:00
|
|
|
pub(super) struct Table<I: Idx, T>
|
|
|
|
where
|
|
|
|
Option<T>: FixedSizeEncoding,
|
|
|
|
{
|
2019-11-14 02:44:24 +02:00
|
|
|
_marker: PhantomData<(fn(&I), T)>,
|
|
|
|
// NOTE(eddyb) this makes `Table` not implement `Sized`, but no
|
|
|
|
// value of `Table` is ever created (it's always behind `Lazy`).
|
|
|
|
_bytes: [u8],
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Helper for constructing a table's serialization (also see `Table`).
|
2019-12-22 17:42:04 -05:00
|
|
|
pub(super) struct TableBuilder<I: Idx, T>
|
|
|
|
where
|
|
|
|
Option<T>: FixedSizeEncoding,
|
|
|
|
{
|
2019-11-13 22:10:58 +02:00
|
|
|
// FIXME(eddyb) use `IndexVec<I, [u8; <Option<T>>::BYTE_LEN]>` instead of
|
|
|
|
// `Vec<u8>`, once that starts working (i.e. lazy normalization).
|
2019-11-14 02:44:24 +02:00
|
|
|
// Then again, that has the downside of not allowing `TableBuilder::encode` to
|
2019-11-13 22:10:58 +02:00
|
|
|
// obtain a `&[u8]` entirely in safe code, for writing the bytes out.
|
2019-04-13 19:18:12 +03:00
|
|
|
bytes: Vec<u8>,
|
2019-11-13 22:10:58 +02:00
|
|
|
_marker: PhantomData<(fn(&I), T)>,
|
2015-09-03 01:22:31 +03:00
|
|
|
}
|
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
impl<I: Idx, T> Default for TableBuilder<I, T>
|
|
|
|
where
|
|
|
|
Option<T>: FixedSizeEncoding,
|
|
|
|
{
|
2019-04-14 05:21:39 +03:00
|
|
|
fn default() -> Self {
|
2019-12-22 17:42:04 -05:00
|
|
|
TableBuilder { bytes: vec![], _marker: PhantomData }
|
2015-09-03 01:22:31 +03:00
|
|
|
}
|
2019-04-14 05:21:39 +03:00
|
|
|
}
|
2015-09-03 01:22:31 +03:00
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
impl<I: Idx, T> TableBuilder<I, T>
|
|
|
|
where
|
|
|
|
Option<T>: FixedSizeEncoding,
|
|
|
|
{
|
2019-11-13 22:10:58 +02:00
|
|
|
pub(super) fn set(&mut self, i: I, value: T) {
|
2019-10-14 18:42:08 +03:00
|
|
|
// 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).
|
2019-11-13 22:10:58 +02:00
|
|
|
let i = i.index();
|
2019-04-14 05:21:39 +03:00
|
|
|
let needed = (i + 1) * <Option<T>>::BYTE_LEN;
|
|
|
|
if self.bytes.len() < needed {
|
|
|
|
self.bytes.resize(needed, 0);
|
|
|
|
}
|
2019-10-14 18:42:08 +03:00
|
|
|
|
2019-04-13 23:30:53 +03:00
|
|
|
Some(value).write_to_bytes_at(&mut self.bytes, i);
|
2015-09-03 01:22:31 +03:00
|
|
|
}
|
|
|
|
|
2019-11-14 02:44:24 +02:00
|
|
|
pub(super) fn encode(&self, buf: &mut Encoder) -> Lazy<Table<I, T>> {
|
2016-09-16 17:25:54 +03:00
|
|
|
let pos = buf.position();
|
2019-04-13 19:18:12 +03:00
|
|
|
buf.emit_raw_bytes(&self.bytes);
|
2019-12-22 17:42:04 -05:00
|
|
|
Lazy::from_position_and_meta(NonZeroUsize::new(pos as usize).unwrap(), self.bytes.len())
|
2016-09-16 17:25:54 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
impl<I: Idx, T> LazyMeta for Table<I, T>
|
|
|
|
where
|
|
|
|
Option<T>: FixedSizeEncoding,
|
|
|
|
{
|
2019-04-11 23:36:00 +03:00
|
|
|
type Meta = usize;
|
|
|
|
|
|
|
|
fn min_size(len: usize) -> usize {
|
2019-04-13 19:18:12 +03:00
|
|
|
len
|
2019-04-11 23:36:00 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
impl<I: Idx, T> Lazy<Table<I, T>>
|
|
|
|
where
|
|
|
|
Option<T>: FixedSizeEncoding,
|
|
|
|
{
|
2019-04-13 23:30:53 +03:00
|
|
|
/// Given the metadata, extract out the value at a particular index (if any).
|
2016-09-16 17:25:54 +03:00
|
|
|
#[inline(never)]
|
2019-12-22 17:42:04 -05:00
|
|
|
pub(super) fn get<'a, 'tcx, M: Metadata<'a, 'tcx>>(&self, metadata: M, i: I) -> Option<T> {
|
2019-04-13 19:18:12 +03:00
|
|
|
debug!("Table::lookup: index={:?} len={:?}", i, self.meta);
|
2016-09-16 17:25:54 +03:00
|
|
|
|
2019-10-14 18:42:08 +03:00
|
|
|
let start = self.position.get();
|
|
|
|
let bytes = &metadata.raw_bytes()[start..start + self.meta];
|
2019-11-13 22:10:58 +02:00
|
|
|
<Option<T>>::maybe_read_from_bytes_at(bytes, i.index())?
|
2019-04-13 19:18:12 +03:00
|
|
|
}
|
|
|
|
}
|