//! Functions for parsing DWARF debugging abbreviations.

use alloc::collections::btree_map;
use alloc::vec::Vec;
use core::convert::TryFrom;
use core::fmt::{self, Debug};
use core::iter::FromIterator;
use core::ops::Deref;

use crate::common::{DebugAbbrevOffset, SectionId};
use crate::constants;
use crate::endianity::Endianity;
use crate::read::{EndianSlice, Error, Reader, Result, Section, UnitHeader};

/// The `DebugAbbrev` struct represents the abbreviations describing
/// `DebuggingInformationEntry`s' attribute names and forms found in the
/// `.debug_abbrev` section.
#[derive(Debug, Default, Clone, Copy)]
pub struct DebugAbbrev<R> {
    debug_abbrev_section: R,
}

impl<'input, Endian> DebugAbbrev<EndianSlice<'input, Endian>>
where
    Endian: Endianity,
{
    /// Construct a new `DebugAbbrev` instance from the data in the `.debug_abbrev`
    /// section.
    ///
    /// It is the caller's responsibility to read the `.debug_abbrev` section and
    /// present it as a `&[u8]` slice. That means using some ELF loader on
    /// Linux, a Mach-O loader on OSX, etc.
    ///
    /// ```
    /// use gimli::{DebugAbbrev, LittleEndian};
    ///
    /// # let buf = [0x00, 0x01, 0x02, 0x03];
    /// # let read_debug_abbrev_section_somehow = || &buf;
    /// let debug_abbrev = DebugAbbrev::new(read_debug_abbrev_section_somehow(), LittleEndian);
    /// ```
    pub fn new(debug_abbrev_section: &'input [u8], endian: Endian) -> Self {
        Self::from(EndianSlice::new(debug_abbrev_section, endian))
    }
}

impl<R: Reader> DebugAbbrev<R> {
    /// Parse the abbreviations at the given `offset` within this
    /// `.debug_abbrev` section.
    ///
    /// The `offset` should generally be retrieved from a unit header.
    pub fn abbreviations(
        &self,
        debug_abbrev_offset: DebugAbbrevOffset<R::Offset>,
    ) -> Result<Abbreviations> {
        let input = &mut self.debug_abbrev_section.clone();
        input.skip(debug_abbrev_offset.0)?;
        Abbreviations::parse(input)
    }
}

impl<T> DebugAbbrev<T> {
    /// Create a `DebugAbbrev` section that references the data in `self`.
    ///
    /// This is useful when `R` implements `Reader` but `T` does not.
    ///
    /// ## Example Usage
    ///
    /// ```rust,no_run
    /// # let load_section = || unimplemented!();
    /// // Read the DWARF section into a `Vec` with whatever object loader you're using.
    /// let owned_section: gimli::DebugAbbrev<Vec<u8>> = load_section();
    /// // Create a reference to the DWARF section.
    /// let section = owned_section.borrow(|section| {
    ///     gimli::EndianSlice::new(&section, gimli::LittleEndian)
    /// });
    /// ```
    pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugAbbrev<R>
    where
        F: FnMut(&'a T) -> R,
    {
        borrow(&self.debug_abbrev_section).into()
    }
}

impl<R> Section<R> for DebugAbbrev<R> {
    fn id() -> SectionId {
        SectionId::DebugAbbrev
    }

    fn reader(&self) -> &R {
        &self.debug_abbrev_section
    }
}

impl<R> From<R> for DebugAbbrev<R> {
    fn from(debug_abbrev_section: R) -> Self {
        DebugAbbrev {
            debug_abbrev_section,
        }
    }
}

/// A set of type abbreviations.
///
/// Construct an `Abbreviations` instance with the
/// [`abbreviations()`](struct.UnitHeader.html#method.abbreviations)
/// method.
#[derive(Debug, Default, Clone)]
pub struct Abbreviations {
    vec: Vec<Abbreviation>,
    map: btree_map::BTreeMap<u64, Abbreviation>,
}

impl Abbreviations {
    /// Construct a new, empty set of abbreviations.
    fn empty() -> Abbreviations {
        Abbreviations {
            vec: Vec::new(),
            map: btree_map::BTreeMap::new(),
        }
    }

    /// Insert an abbreviation into the set.
    ///
    /// Returns `Ok` if it is the first abbreviation in the set with its code,
    /// `Err` if the code is a duplicate and there already exists an
    /// abbreviation in the set with the given abbreviation's code.
    fn insert(&mut self, abbrev: Abbreviation) -> ::core::result::Result<(), ()> {
        let code_usize = abbrev.code as usize;
        if code_usize as u64 == abbrev.code {
            // Optimize for sequential abbreviation codes by storing them
            // in a Vec, as long as the map doesn't already contain them.
            // A potential further optimization would be to allow some
            // holes in the Vec, but there's no need for that yet.
            if code_usize - 1 < self.vec.len() {
                return Err(());
            } else if code_usize - 1 == self.vec.len() {
                if !self.map.is_empty() && self.map.contains_key(&abbrev.code) {
                    return Err(());
                } else {
                    self.vec.push(abbrev);
                    return Ok(());
                }
            }
        }
        match self.map.entry(abbrev.code) {
            btree_map::Entry::Occupied(_) => Err(()),
            btree_map::Entry::Vacant(entry) => {
                entry.insert(abbrev);
                Ok(())
            }
        }
    }

    /// Get the abbreviation associated with the given code.
    #[inline]
    pub fn get(&self, code: u64) -> Option<&Abbreviation> {
        if let Ok(code) = usize::try_from(code) {
            let index = code.checked_sub(1)?;
            if index < self.vec.len() {
                return Some(&self.vec[index]);
            }
        }

        self.map.get(&code)
    }

    /// Parse a series of abbreviations, terminated by a null abbreviation.
    fn parse<R: Reader>(input: &mut R) -> Result<Abbreviations> {
        let mut abbrevs = Abbreviations::empty();

        while let Some(abbrev) = Abbreviation::parse(input)? {
            if abbrevs.insert(abbrev).is_err() {
                return Err(Error::DuplicateAbbreviationCode);
            }
        }

        Ok(abbrevs)
    }
}

/// An abbreviation describes the shape of a `DebuggingInformationEntry`'s type:
/// its code, tag type, whether it has children, and its set of attributes.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Abbreviation {
    code: u64,
    tag: constants::DwTag,
    has_children: constants::DwChildren,
    attributes: Attributes,
}

impl Abbreviation {
    /// Construct a new `Abbreviation`.
    ///
    /// ### Panics
    ///
    /// Panics if `code` is `0`.
    pub(crate) fn new(
        code: u64,
        tag: constants::DwTag,
        has_children: constants::DwChildren,
        attributes: Attributes,
    ) -> Abbreviation {
        assert_ne!(code, 0);
        Abbreviation {
            code,
            tag,
            has_children,
            attributes,
        }
    }

    /// Get this abbreviation's code.
    #[inline]
    pub fn code(&self) -> u64 {
        self.code
    }

    /// Get this abbreviation's tag.
    #[inline]
    pub fn tag(&self) -> constants::DwTag {
        self.tag
    }

    /// Return true if this abbreviation's type has children, false otherwise.
    #[inline]
    pub fn has_children(&self) -> bool {
        self.has_children == constants::DW_CHILDREN_yes
    }

    /// Get this abbreviation's attributes.
    #[inline]
    pub fn attributes(&self) -> &[AttributeSpecification] {
        &self.attributes[..]
    }

    /// Parse an abbreviation's tag.
    fn parse_tag<R: Reader>(input: &mut R) -> Result<constants::DwTag> {
        let val = input.read_uleb128_u16()?;
        if val == 0 {
            Err(Error::AbbreviationTagZero)
        } else {
            Ok(constants::DwTag(val))
        }
    }

    /// Parse an abbreviation's "does the type have children?" byte.
    fn parse_has_children<R: Reader>(input: &mut R) -> Result<constants::DwChildren> {
        let val = input.read_u8()?;
        let val = constants::DwChildren(val);
        if val == constants::DW_CHILDREN_no || val == constants::DW_CHILDREN_yes {
            Ok(val)
        } else {
            Err(Error::BadHasChildren)
        }
    }

    /// Parse a series of attribute specifications, terminated by a null attribute
    /// specification.
    fn parse_attributes<R: Reader>(input: &mut R) -> Result<Attributes> {
        let mut attrs = Attributes::new();

        while let Some(attr) = AttributeSpecification::parse(input)? {
            attrs.push(attr);
        }

        Ok(attrs)
    }

    /// Parse an abbreviation. Return `None` for the null abbreviation, `Some`
    /// for an actual abbreviation.
    fn parse<R: Reader>(input: &mut R) -> Result<Option<Abbreviation>> {
        let code = input.read_uleb128()?;
        if code == 0 {
            return Ok(None);
        }

        let tag = Self::parse_tag(input)?;
        let has_children = Self::parse_has_children(input)?;
        let attributes = Self::parse_attributes(input)?;
        let abbrev = Abbreviation::new(code, tag, has_children, attributes);
        Ok(Some(abbrev))
    }
}

/// A list of attributes found in an `Abbreviation`
#[derive(Clone)]
pub(crate) enum Attributes {
    Inline {
        buf: [AttributeSpecification; MAX_ATTRIBUTES_INLINE],
        len: usize,
    },
    Heap(Vec<AttributeSpecification>),
}

// Length of 5 based on benchmark results for both x86-64 and i686.
const MAX_ATTRIBUTES_INLINE: usize = 5;

impl Attributes {
    /// Returns a new empty list of attributes
    fn new() -> Attributes {
        let default =
            AttributeSpecification::new(constants::DW_AT_null, constants::DW_FORM_null, None);
        Attributes::Inline {
            buf: [default; 5],
            len: 0,
        }
    }

    /// Pushes a new value onto this list of attributes.
    fn push(&mut self, attr: AttributeSpecification) {
        match self {
            Attributes::Heap(list) => return list.push(attr),
            Attributes::Inline {
                buf,
                len: MAX_ATTRIBUTES_INLINE,
            } => {
                let mut list = buf.to_vec();
                list.push(attr);
                *self = Attributes::Heap(list);
            }
            Attributes::Inline { buf, len } => {
                buf[*len] = attr;
                *len += 1;
            }
        }
    }
}

impl Debug for Attributes {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        (&**self).fmt(f)
    }
}

impl PartialEq for Attributes {
    fn eq(&self, other: &Attributes) -> bool {
        &**self == &**other
    }
}

impl Eq for Attributes {}

impl Deref for Attributes {
    type Target = [AttributeSpecification];
    fn deref(&self) -> &[AttributeSpecification] {
        match self {
            Attributes::Inline { buf, len } => &buf[..*len],
            Attributes::Heap(list) => list,
        }
    }
}

impl FromIterator<AttributeSpecification> for Attributes {
    fn from_iter<I>(iter: I) -> Attributes
    where
        I: IntoIterator<Item = AttributeSpecification>,
    {
        let mut list = Attributes::new();
        for item in iter {
            list.push(item);
        }
        return list;
    }
}

impl From<Vec<AttributeSpecification>> for Attributes {
    fn from(list: Vec<AttributeSpecification>) -> Attributes {
        Attributes::Heap(list)
    }
}

/// The description of an attribute in an abbreviated type. It is a pair of name
/// and form.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AttributeSpecification {
    name: constants::DwAt,
    form: constants::DwForm,
    implicit_const_value: i64,
}

impl AttributeSpecification {
    /// Construct a new `AttributeSpecification` from the given name and form
    /// and implicit const value.
    #[inline]
    pub fn new(
        name: constants::DwAt,
        form: constants::DwForm,
        implicit_const_value: Option<i64>,
    ) -> AttributeSpecification {
        debug_assert!(
            (form == constants::DW_FORM_implicit_const && implicit_const_value.is_some())
                || (form != constants::DW_FORM_implicit_const && implicit_const_value.is_none())
        );
        AttributeSpecification {
            name,
            form,
            implicit_const_value: implicit_const_value.unwrap_or(0),
        }
    }

    /// Get the attribute's name.
    #[inline]
    pub fn name(&self) -> constants::DwAt {
        self.name
    }

    /// Get the attribute's form.
    #[inline]
    pub fn form(&self) -> constants::DwForm {
        self.form
    }

    /// Get the attribute's implicit const value.
    #[inline]
    pub fn implicit_const_value(&self) -> Option<i64> {
        if self.form == constants::DW_FORM_implicit_const {
            Some(self.implicit_const_value)
        } else {
            None
        }
    }

    /// Return the size of the attribute, in bytes.
    ///
    /// Note that because some attributes are variably sized, the size cannot
    /// always be known without parsing, in which case we return `None`.
    pub fn size<R: Reader>(&self, header: &UnitHeader<R>) -> Option<usize> {
        match self.form {
            constants::DW_FORM_addr => Some(header.address_size() as usize),

            constants::DW_FORM_implicit_const => Some(0),

            constants::DW_FORM_flag |
            constants::DW_FORM_flag_present |
            constants::DW_FORM_data1 |
            constants::DW_FORM_ref1 => Some(1),

            constants::DW_FORM_data2 |
            constants::DW_FORM_ref2 => Some(2),

            constants::DW_FORM_data4 |
            constants::DW_FORM_ref4 => Some(4),

            constants::DW_FORM_data8 |
            constants::DW_FORM_ref8 => Some(8),

            constants::DW_FORM_sec_offset |
            constants::DW_FORM_ref_addr |
            constants::DW_FORM_ref_sig8 |
            constants::DW_FORM_strp => Some(header.format().word_size() as usize),

            // Variably sized forms.
            constants::DW_FORM_block |
            constants::DW_FORM_block1 |
            constants::DW_FORM_block2 |
            constants::DW_FORM_block4 |
            constants::DW_FORM_exprloc |
            constants::DW_FORM_ref_udata |
            constants::DW_FORM_string |
            constants::DW_FORM_sdata |
            constants::DW_FORM_udata |
            constants::DW_FORM_indirect |

            // We don't know the size of unknown forms.
            _ => None,
        }
    }

    /// Parse an attribute's form.
    fn parse_form<R: Reader>(input: &mut R) -> Result<constants::DwForm> {
        let val = input.read_uleb128_u16()?;
        if val == 0 {
            Err(Error::AttributeFormZero)
        } else {
            Ok(constants::DwForm(val))
        }
    }

    /// Parse an attribute specification. Returns `None` for the null attribute
    /// specification, `Some` for an actual attribute specification.
    fn parse<R: Reader>(input: &mut R) -> Result<Option<AttributeSpecification>> {
        let name = input.read_uleb128_u16()?;
        if name == 0 {
            // Parse the null attribute specification.
            let form = input.read_uleb128_u16()?;
            return if form == 0 {
                Ok(None)
            } else {
                Err(Error::ExpectedZero)
            };
        }

        let name = constants::DwAt(name);
        let form = Self::parse_form(input)?;
        let implicit_const_value = if form == constants::DW_FORM_implicit_const {
            Some(input.read_sleb128()?)
        } else {
            None
        };
        let spec = AttributeSpecification::new(name, form, implicit_const_value);
        Ok(Some(spec))
    }
}

#[cfg(test)]
pub mod tests {
    use super::*;
    use crate::constants;
    use crate::endianity::LittleEndian;
    use crate::read::{EndianSlice, Error};
    use crate::test_util::GimliSectionMethods;
    #[cfg(target_pointer_width = "32")]
    use core::u32;
    use test_assembler::Section;

    pub trait AbbrevSectionMethods {
        fn abbrev(self, code: u64, tag: constants::DwTag, children: constants::DwChildren) -> Self;
        fn abbrev_null(self) -> Self;
        fn abbrev_attr(self, name: constants::DwAt, form: constants::DwForm) -> Self;
        fn abbrev_attr_implicit_const(self, name: constants::DwAt, value: i64) -> Self;
        fn abbrev_attr_null(self) -> Self;
    }

    impl AbbrevSectionMethods for Section {
        fn abbrev(self, code: u64, tag: constants::DwTag, children: constants::DwChildren) -> Self {
            self.uleb(code).uleb(tag.0.into()).D8(children.0)
        }

        fn abbrev_null(self) -> Self {
            self.D8(0)
        }

        fn abbrev_attr(self, name: constants::DwAt, form: constants::DwForm) -> Self {
            self.uleb(name.0.into()).uleb(form.0.into())
        }

        fn abbrev_attr_implicit_const(self, name: constants::DwAt, value: i64) -> Self {
            self.uleb(name.0.into())
                .uleb(constants::DW_FORM_implicit_const.0.into())
                .sleb(value)
        }

        fn abbrev_attr_null(self) -> Self {
            self.D8(0).D8(0)
        }
    }

    #[test]
    fn test_debug_abbrev_ok() {
        let extra_start = [1, 2, 3, 4];
        let expected_rest = [5, 6, 7, 8];
        #[rustfmt::skip]
        let buf = Section::new()
            .append_bytes(&extra_start)
            .abbrev(2, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no)
                .abbrev_attr(constants::DW_AT_name, constants::DW_FORM_string)
                .abbrev_attr_null()
            .abbrev(1, constants::DW_TAG_compile_unit, constants::DW_CHILDREN_yes)
                .abbrev_attr(constants::DW_AT_producer, constants::DW_FORM_strp)
                .abbrev_attr(constants::DW_AT_language, constants::DW_FORM_data2)
                .abbrev_attr_null()
            .abbrev_null()
            .append_bytes(&expected_rest)
            .get_contents()
            .unwrap();

        let abbrev1 = Abbreviation::new(
            1,
            constants::DW_TAG_compile_unit,
            constants::DW_CHILDREN_yes,
            vec![
                AttributeSpecification::new(
                    constants::DW_AT_producer,
                    constants::DW_FORM_strp,
                    None,
                ),
                AttributeSpecification::new(
                    constants::DW_AT_language,
                    constants::DW_FORM_data2,
                    None,
                ),
            ]
            .into(),
        );

        let abbrev2 = Abbreviation::new(
            2,
            constants::DW_TAG_subprogram,
            constants::DW_CHILDREN_no,
            vec![AttributeSpecification::new(
                constants::DW_AT_name,
                constants::DW_FORM_string,
                None,
            )]
            .into(),
        );

        let debug_abbrev = DebugAbbrev::new(&buf, LittleEndian);
        let debug_abbrev_offset = DebugAbbrevOffset(extra_start.len());
        let abbrevs = debug_abbrev
            .abbreviations(debug_abbrev_offset)
            .expect("Should parse abbreviations");
        assert_eq!(abbrevs.get(1), Some(&abbrev1));
        assert_eq!(abbrevs.get(2), Some(&abbrev2));
    }

    #[test]
    fn test_abbreviations_insert() {
        fn abbrev(code: u16) -> Abbreviation {
            Abbreviation::new(
                code.into(),
                constants::DwTag(code),
                constants::DW_CHILDREN_no,
                vec![].into(),
            )
        }

        fn assert_abbrev(abbrevs: &Abbreviations, code: u16) {
            let abbrev = abbrevs.get(code.into()).unwrap();
            assert_eq!(abbrev.tag(), constants::DwTag(code));
        }

        // Sequential insert.
        let mut abbrevs = Abbreviations::empty();
        abbrevs.insert(abbrev(1)).unwrap();
        abbrevs.insert(abbrev(2)).unwrap();
        assert_eq!(abbrevs.vec.len(), 2);
        assert!(abbrevs.map.is_empty());
        assert_abbrev(&abbrevs, 1);
        assert_abbrev(&abbrevs, 2);

        // Out of order insert.
        let mut abbrevs = Abbreviations::empty();
        abbrevs.insert(abbrev(2)).unwrap();
        abbrevs.insert(abbrev(3)).unwrap();
        assert!(abbrevs.vec.is_empty());
        assert_abbrev(&abbrevs, 2);
        assert_abbrev(&abbrevs, 3);

        // Mixed order insert.
        let mut abbrevs = Abbreviations::empty();
        abbrevs.insert(abbrev(1)).unwrap();
        abbrevs.insert(abbrev(3)).unwrap();
        abbrevs.insert(abbrev(2)).unwrap();
        assert_eq!(abbrevs.vec.len(), 2);
        assert_abbrev(&abbrevs, 1);
        assert_abbrev(&abbrevs, 2);
        assert_abbrev(&abbrevs, 3);

        // Duplicate code in vec.
        let mut abbrevs = Abbreviations::empty();
        abbrevs.insert(abbrev(1)).unwrap();
        abbrevs.insert(abbrev(2)).unwrap();
        assert_eq!(abbrevs.insert(abbrev(1)), Err(()));
        assert_eq!(abbrevs.insert(abbrev(2)), Err(()));

        // Duplicate code in map when adding to map.
        let mut abbrevs = Abbreviations::empty();
        abbrevs.insert(abbrev(2)).unwrap();
        assert_eq!(abbrevs.insert(abbrev(2)), Err(()));

        // Duplicate code in map when adding to vec.
        let mut abbrevs = Abbreviations::empty();
        abbrevs.insert(abbrev(2)).unwrap();
        abbrevs.insert(abbrev(1)).unwrap();
        assert_eq!(abbrevs.insert(abbrev(2)), Err(()));

        // 32-bit usize conversions.
        let mut abbrevs = Abbreviations::empty();
        abbrevs.insert(abbrev(2)).unwrap();
    }

    #[test]
    #[cfg(target_pointer_width = "32")]
    fn test_abbreviations_insert_32() {
        fn abbrev(code: u64) -> Abbreviation {
            Abbreviation::new(
                code,
                constants::DwTag(code as u16),
                constants::DW_CHILDREN_no,
                vec![].into(),
            )
        }

        fn assert_abbrev(abbrevs: &Abbreviations, code: u64) {
            let abbrev = abbrevs.get(code).unwrap();
            assert_eq!(abbrev.tag(), constants::DwTag(code as u16));
        }

        let mut abbrevs = Abbreviations::empty();
        abbrevs.insert(abbrev(1)).unwrap();

        let wrap_code = (u32::MAX as u64 + 1) + 1;
        // `get` should not treat the wrapped code as `1`.
        assert_eq!(abbrevs.get(wrap_code), None);
        // `insert` should not treat the wrapped code as `1`.
        abbrevs.insert(abbrev(wrap_code)).unwrap();
        assert_abbrev(&abbrevs, 1);
        assert_abbrev(&abbrevs, wrap_code);
    }

    #[test]
    fn test_parse_abbreviations_ok() {
        let expected_rest = [1, 2, 3, 4];
        #[rustfmt::skip]
        let buf = Section::new()
            .abbrev(2, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no)
                .abbrev_attr(constants::DW_AT_name, constants::DW_FORM_string)
                .abbrev_attr_null()
            .abbrev(1, constants::DW_TAG_compile_unit, constants::DW_CHILDREN_yes)
                .abbrev_attr(constants::DW_AT_producer, constants::DW_FORM_strp)
                .abbrev_attr(constants::DW_AT_language, constants::DW_FORM_data2)
                .abbrev_attr_null()
            .abbrev_null()
            .append_bytes(&expected_rest)
            .get_contents()
            .unwrap();
        let rest = &mut EndianSlice::new(&*buf, LittleEndian);

        let abbrev1 = Abbreviation::new(
            1,
            constants::DW_TAG_compile_unit,
            constants::DW_CHILDREN_yes,
            vec![
                AttributeSpecification::new(
                    constants::DW_AT_producer,
                    constants::DW_FORM_strp,
                    None,
                ),
                AttributeSpecification::new(
                    constants::DW_AT_language,
                    constants::DW_FORM_data2,
                    None,
                ),
            ]
            .into(),
        );

        let abbrev2 = Abbreviation::new(
            2,
            constants::DW_TAG_subprogram,
            constants::DW_CHILDREN_no,
            vec![AttributeSpecification::new(
                constants::DW_AT_name,
                constants::DW_FORM_string,
                None,
            )]
            .into(),
        );

        let abbrevs = Abbreviations::parse(rest).expect("Should parse abbreviations");
        assert_eq!(abbrevs.get(1), Some(&abbrev1));
        assert_eq!(abbrevs.get(2), Some(&abbrev2));
        assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian));
    }

    #[test]
    fn test_parse_abbreviations_duplicate() {
        let expected_rest = [1, 2, 3, 4];
        #[rustfmt::skip]
        let buf = Section::new()
            .abbrev(1, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no)
                .abbrev_attr(constants::DW_AT_name, constants::DW_FORM_string)
                .abbrev_attr_null()
            .abbrev(1, constants::DW_TAG_compile_unit, constants::DW_CHILDREN_yes)
                .abbrev_attr(constants::DW_AT_producer, constants::DW_FORM_strp)
                .abbrev_attr(constants::DW_AT_language, constants::DW_FORM_data2)
                .abbrev_attr_null()
            .abbrev_null()
            .append_bytes(&expected_rest)
            .get_contents()
            .unwrap();
        let buf = &mut EndianSlice::new(&*buf, LittleEndian);

        match Abbreviations::parse(buf) {
            Err(Error::DuplicateAbbreviationCode) => {}
            otherwise => panic!("Unexpected result: {:?}", otherwise),
        };
    }

    #[test]
    fn test_parse_abbreviation_tag_ok() {
        let buf = [0x01, 0x02];
        let rest = &mut EndianSlice::new(&buf, LittleEndian);
        let tag = Abbreviation::parse_tag(rest).expect("Should parse tag");
        assert_eq!(tag, constants::DW_TAG_array_type);
        assert_eq!(*rest, EndianSlice::new(&buf[1..], LittleEndian));
    }

    #[test]
    fn test_parse_abbreviation_tag_zero() {
        let buf = [0x00];
        let buf = &mut EndianSlice::new(&buf, LittleEndian);
        match Abbreviation::parse_tag(buf) {
            Err(Error::AbbreviationTagZero) => {}
            otherwise => panic!("Unexpected result: {:?}", otherwise),
        };
    }

    #[test]
    fn test_parse_abbreviation_has_children() {
        let buf = [0x00, 0x01, 0x02];
        let rest = &mut EndianSlice::new(&buf, LittleEndian);
        let val = Abbreviation::parse_has_children(rest).expect("Should parse children");
        assert_eq!(val, constants::DW_CHILDREN_no);
        let val = Abbreviation::parse_has_children(rest).expect("Should parse children");
        assert_eq!(val, constants::DW_CHILDREN_yes);
        match Abbreviation::parse_has_children(rest) {
            Err(Error::BadHasChildren) => {}
            otherwise => panic!("Unexpected result: {:?}", otherwise),
        };
    }

    #[test]
    fn test_parse_abbreviation_ok() {
        let expected_rest = [0x01, 0x02, 0x03, 0x04];
        let buf = Section::new()
            .abbrev(1, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no)
            .abbrev_attr(constants::DW_AT_name, constants::DW_FORM_string)
            .abbrev_attr_null()
            .append_bytes(&expected_rest)
            .get_contents()
            .unwrap();
        let rest = &mut EndianSlice::new(&*buf, LittleEndian);

        let expect = Some(Abbreviation::new(
            1,
            constants::DW_TAG_subprogram,
            constants::DW_CHILDREN_no,
            vec![AttributeSpecification::new(
                constants::DW_AT_name,
                constants::DW_FORM_string,
                None,
            )]
            .into(),
        ));

        let abbrev = Abbreviation::parse(rest).expect("Should parse abbreviation");
        assert_eq!(abbrev, expect);
        assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian));
    }

    #[test]
    fn test_parse_abbreviation_implicit_const_ok() {
        let expected_rest = [0x01, 0x02, 0x03, 0x04];
        let buf = Section::new()
            .abbrev(1, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no)
            .abbrev_attr_implicit_const(constants::DW_AT_name, -42)
            .abbrev_attr_null()
            .append_bytes(&expected_rest)
            .get_contents()
            .unwrap();
        let rest = &mut EndianSlice::new(&*buf, LittleEndian);

        let expect = Some(Abbreviation::new(
            1,
            constants::DW_TAG_subprogram,
            constants::DW_CHILDREN_no,
            vec![AttributeSpecification::new(
                constants::DW_AT_name,
                constants::DW_FORM_implicit_const,
                Some(-42),
            )]
            .into(),
        ));

        let abbrev = Abbreviation::parse(rest).expect("Should parse abbreviation");
        assert_eq!(abbrev, expect);
        assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian));
    }

    #[test]
    fn test_parse_abbreviation_implicit_const_no_const() {
        let buf = Section::new()
            .abbrev(1, constants::DW_TAG_subprogram, constants::DW_CHILDREN_no)
            .abbrev_attr(constants::DW_AT_name, constants::DW_FORM_implicit_const)
            .get_contents()
            .unwrap();
        let buf = &mut EndianSlice::new(&*buf, LittleEndian);

        match Abbreviation::parse(buf) {
            Err(Error::UnexpectedEof(_)) => {}
            otherwise => panic!("Unexpected result: {:?}", otherwise),
        }
    }

    #[test]
    fn test_parse_null_abbreviation_ok() {
        let expected_rest = [0x01, 0x02, 0x03, 0x04];
        let buf = Section::new()
            .abbrev_null()
            .append_bytes(&expected_rest)
            .get_contents()
            .unwrap();
        let rest = &mut EndianSlice::new(&*buf, LittleEndian);

        let abbrev = Abbreviation::parse(rest).expect("Should parse null abbreviation");
        assert!(abbrev.is_none());
        assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian));
    }

    #[test]
    fn test_parse_attribute_form_ok() {
        let buf = [0x01, 0x02];
        let rest = &mut EndianSlice::new(&buf, LittleEndian);
        let tag = AttributeSpecification::parse_form(rest).expect("Should parse form");
        assert_eq!(tag, constants::DW_FORM_addr);
        assert_eq!(*rest, EndianSlice::new(&buf[1..], LittleEndian));
    }

    #[test]
    fn test_parse_attribute_form_zero() {
        let buf = [0x00];
        let buf = &mut EndianSlice::new(&buf, LittleEndian);
        match AttributeSpecification::parse_form(buf) {
            Err(Error::AttributeFormZero) => {}
            otherwise => panic!("Unexpected result: {:?}", otherwise),
        };
    }

    #[test]
    fn test_parse_null_attribute_specification_ok() {
        let buf = [0x00, 0x00, 0x01];
        let rest = &mut EndianSlice::new(&buf, LittleEndian);
        let attr =
            AttributeSpecification::parse(rest).expect("Should parse null attribute specification");
        assert!(attr.is_none());
        assert_eq!(*rest, EndianSlice::new(&buf[2..], LittleEndian));
    }

    #[test]
    fn test_parse_attribute_specifications_name_zero() {
        let buf = [0x00, 0x01, 0x00, 0x00];
        let buf = &mut EndianSlice::new(&buf, LittleEndian);
        match AttributeSpecification::parse(buf) {
            Err(Error::ExpectedZero) => {}
            otherwise => panic!("Unexpected result: {:?}", otherwise),
        };
    }

    #[test]
    fn test_parse_attribute_specifications_form_zero() {
        let buf = [0x01, 0x00, 0x00, 0x00];
        let buf = &mut EndianSlice::new(&buf, LittleEndian);
        match AttributeSpecification::parse(buf) {
            Err(Error::AttributeFormZero) => {}
            otherwise => panic!("Unexpected result: {:?}", otherwise),
        };
    }

    #[test]
    fn test_get_abbrev_zero() {
        let mut abbrevs = Abbreviations::empty();
        abbrevs
            .insert(Abbreviation::new(
                1,
                constants::DwTag(1),
                constants::DW_CHILDREN_no,
                vec![].into(),
            ))
            .unwrap();
        assert!(abbrevs.get(0).is_none());
    }
}
