| use schema::*; |
| |
| use rustc::hir::def_id::{DefId, DefIndex, DefIndexAddressSpace}; |
| use rustc_serialize::opaque::Encoder; |
| use std::slice; |
| use std::u32; |
| |
| /// While we are generating the metadata, we also track the position |
| /// of each DefIndex. It is not required that all definitions appear |
| /// in the metadata, nor that they are serialized in order, and |
| /// therefore we first allocate the vector here and fill it with |
| /// `u32::MAX`. Whenever an index is visited, we fill in the |
| /// appropriate spot by calling `record_position`. We should never |
| /// visit the same index twice. |
| pub struct Index { |
| positions: [Vec<u32>; 2] |
| } |
| |
| impl Index { |
| pub fn new((max_index_lo, max_index_hi): (usize, usize)) -> Index { |
| Index { |
| positions: [vec![u32::MAX; max_index_lo], |
| vec![u32::MAX; max_index_hi]], |
| } |
| } |
| |
| pub fn record(&mut self, def_id: DefId, entry: Lazy<Entry>) { |
| assert!(def_id.is_local()); |
| self.record_index(def_id.index, entry); |
| } |
| |
| pub fn record_index(&mut self, item: DefIndex, entry: Lazy<Entry>) { |
| assert!(entry.position < (u32::MAX as usize)); |
| let position = entry.position as u32; |
| let space_index = item.address_space().index(); |
| let array_index = item.as_array_index(); |
| |
| assert!(self.positions[space_index][array_index] == u32::MAX, |
| "recorded position for item {:?} twice, first at {:?} and now at {:?}", |
| item, |
| self.positions[space_index][array_index], |
| position); |
| |
| self.positions[space_index][array_index] = position.to_le(); |
| } |
| |
| pub fn write_index(&self, buf: &mut Encoder) -> LazySeq<Index> { |
| let pos = buf.position(); |
| |
| // First we write the length of the lower range ... |
| buf.emit_raw_bytes(words_to_bytes(&[(self.positions[0].len() as u32).to_le()])); |
| // ... then the values in the lower range ... |
| buf.emit_raw_bytes(words_to_bytes(&self.positions[0][..])); |
| // ... then the values in the higher range. |
| buf.emit_raw_bytes(words_to_bytes(&self.positions[1][..])); |
| LazySeq::with_position_and_length(pos as usize, |
| self.positions[0].len() + self.positions[1].len() + 1) |
| } |
| } |
| |
| impl<'tcx> LazySeq<Index> { |
| /// Given the metadata, extract out the offset of a particular |
| /// DefIndex (if any). |
| #[inline(never)] |
| pub fn lookup(&self, bytes: &[u8], def_index: DefIndex) -> Option<Lazy<Entry<'tcx>>> { |
| let words = &bytes_to_words(&bytes[self.position..])[..self.len]; |
| |
| debug!("Index::lookup: index={:?} words.len={:?}", |
| def_index, |
| words.len()); |
| |
| let positions = match def_index.address_space() { |
| DefIndexAddressSpace::Low => &words[1..], |
| DefIndexAddressSpace::High => { |
| // This is a DefIndex in the higher range, so find out where |
| // that starts: |
| let lo_count = u32::from_le(words[0].get()) as usize; |
| &words[lo_count + 1 .. ] |
| } |
| }; |
| |
| let array_index = def_index.as_array_index(); |
| let position = u32::from_le(positions[array_index].get()); |
| if position == u32::MAX { |
| debug!("Index::lookup: position=u32::MAX"); |
| None |
| } else { |
| debug!("Index::lookup: position={:?}", position); |
| Some(Lazy::with_position(position as usize)) |
| } |
| } |
| } |
| |
| #[repr(packed)] |
| #[derive(Copy)] |
| struct Unaligned<T>(T); |
| |
| // The derived Clone impl is unsafe for this packed struct since it needs to pass a reference to |
| // the field to `T::clone`, but this reference may not be properly aligned. |
| impl<T: Copy> Clone for Unaligned<T> { |
| fn clone(&self) -> Self { |
| *self |
| } |
| } |
| |
| impl<T> Unaligned<T> { |
| fn get(self) -> T { self.0 } |
| } |
| |
| fn bytes_to_words(b: &[u8]) -> &[Unaligned<u32>] { |
| unsafe { slice::from_raw_parts(b.as_ptr() as *const Unaligned<u32>, b.len() / 4) } |
| } |
| |
| fn words_to_bytes(w: &[u32]) -> &[u8] { |
| unsafe { slice::from_raw_parts(w.as_ptr() as *const u8, w.len() * 4) } |
| } |