use endianity::{Endianity, EndianBuf};
use fallible_iterator::FallibleIterator;
use parser::{parse_null_terminated_string, parse_initial_length, parse_u16, parse_word, Format,
Result, Error};
use std::ffi;
use std::marker::PhantomData;
use std::rc::Rc;
pub trait LookupParser<'input, Endian>
where Endian: Endianity
{
type Header;
type Entry;
fn parse_header(input: EndianBuf<Endian>)
-> Result<(EndianBuf<Endian>, EndianBuf<Endian>, Rc<Self::Header>)>;
fn parse_entry(input: EndianBuf<'input, Endian>,
header: &Rc<Self::Header>)
-> Result<(EndianBuf<'input, Endian>, Option<Self::Entry>)>;
}
#[allow(missing_docs)]
#[derive(Clone, Debug)]
pub struct DebugLookup<'input, Endian, Parser>
where Endian: Endianity,
Parser: LookupParser<'input, Endian>
{
input_buffer: EndianBuf<'input, Endian>,
phantom: PhantomData<Parser>,
}
impl<'input, Endian, Parser> DebugLookup<'input, Endian, Parser>
where Endian: Endianity,
Parser: LookupParser<'input, Endian>
{
#[allow(missing_docs)]
pub fn new(input_buffer: &'input [u8]) -> DebugLookup<'input, Endian, Parser> {
DebugLookup {
input_buffer: EndianBuf(input_buffer, PhantomData),
phantom: PhantomData,
}
}
#[allow(missing_docs)]
pub fn items(&self) -> LookupEntryIter<'input, Endian, Parser> {
LookupEntryIter {
current_header: None,
current_set: EndianBuf::new(&[]),
remaining_input: self.input_buffer,
}
}
}
#[allow(missing_docs)]
#[derive(Clone, Debug)]
pub struct LookupEntryIter<'input, Endian, Parser>
where Endian: Endianity,
Parser: LookupParser<'input, Endian>
{
current_header: Option<Rc<Parser::Header>>, current_set: EndianBuf<'input, Endian>,
remaining_input: EndianBuf<'input, Endian>,
}
impl<'input, Endian, Parser> LookupEntryIter<'input, Endian, Parser>
where Endian: Endianity,
Parser: LookupParser<'input, Endian>
{
pub fn next(&mut self) -> Result<Option<Parser::Entry>> {
if self.current_set.is_empty() {
if self.remaining_input.is_empty() {
self.current_header = None;
Ok(None)
} else {
let (input, set, header) = try!(Parser::parse_header(self.remaining_input));
self.remaining_input = input;
self.current_set = set;
self.current_header = Some(header);
self.next()
}
} else {
let (remaining_set, entry) =
try!(Parser::parse_entry(self.current_set, self.current_header.as_ref().unwrap()));
self.current_set = remaining_set;
match entry {
None => self.next(),
Some(entry) => Ok(Some(entry)),
}
}
}
}
impl<'input, Endian, Parser> FallibleIterator for LookupEntryIter<'input, Endian, Parser>
where Endian: Endianity,
Parser: LookupParser<'input, Endian>
{
type Item = Parser::Entry;
type Error = Error;
fn next(&mut self) -> ::std::result::Result<Option<Self::Item>, Self::Error> {
LookupEntryIter::next(self)
}
}
pub trait NamesOrTypesSwitch<'input, Endian>
where Endian: Endianity
{
type Header;
type Entry;
type Offset;
fn new_header(format: Format,
set_length: u64,
version: u16,
offset: Self::Offset,
length: u64)
-> Rc<Self::Header>;
fn new_entry(offset: u64, name: &'input ffi::CStr, header: &Rc<Self::Header>) -> Self::Entry;
fn parse_offset(input: EndianBuf<Endian>,
format: Format)
-> Result<(EndianBuf<Endian>, Self::Offset)>;
fn format_from(header: &Self::Header) -> Format;
}
#[derive(Clone, Debug)]
pub struct PubStuffParser<'input, Endian, Switch>
where Endian: 'input + Endianity,
Switch: 'input + NamesOrTypesSwitch<'input, Endian>
{
phantom: PhantomData<&'input (Endian, Switch)>,
}
impl<'input, Endian, Switch> LookupParser<'input, Endian> for PubStuffParser<'input, Endian, Switch>
where Endian: Endianity,
Switch: NamesOrTypesSwitch<'input, Endian>
{
type Header = Switch::Header;
type Entry = Switch::Entry;
fn parse_header(input: EndianBuf<Endian>)
-> Result<(EndianBuf<Endian>, EndianBuf<Endian>, Rc<Self::Header>)> {
let (rest, (set_length, format)) = try!(parse_initial_length(input.into()));
let (rest, version) = try!(parse_u16(rest.into()));
if version != 2 {
return Err(Error::UnknownVersion);
}
let (rest, info_offset) = try!(Switch::parse_offset(rest.into(), format));
let (rest, info_length) = try!(parse_word(rest.into(), format));
let header_length = match format {
Format::Dwarf32 => 10,
Format::Dwarf64 => 18,
};
let dividing_line: usize = try!(set_length.checked_sub(header_length)
.ok_or(Error::BadLength)) as usize;
Ok((rest.range_from(dividing_line..),
rest.range_to(..dividing_line),
Switch::new_header(format, set_length, version, info_offset, info_length)))
}
fn parse_entry(input: EndianBuf<'input, Endian>,
header: &Rc<Self::Header>)
-> Result<(EndianBuf<'input, Endian>, Option<Self::Entry>)> {
let (rest, offset) = try!(parse_word(input.into(), Switch::format_from(header)));
if offset == 0 {
Ok((EndianBuf::new(&[]), None))
} else {
let (rest, name) = try!(parse_null_terminated_string(rest.into()));
Ok((EndianBuf::new(rest), Some(Switch::new_entry(offset, name, header))))
}
}
}