//! This module defines the intermediate representation used to represent a binary
//! and all its contained executable code.
//!
//! The main data structure is the `Project` struct,
//! which contains all information recovered about a binary during the disassembly step.
//! To learn how individual instructions are encoded,
//! you should first take a look at the `Expression` type and then at the `Def` and `Jmp` data types,
//! which form the basis of the basic block `Blk` struct.

use crate::prelude::*;
use derive_more::*;
use std::convert::TryFrom;

mod variable;
pub use variable::*;
mod expression;
pub use expression::*;
mod term;
pub use term::*;

/// An unsigned number of bytes.
///
/// Used to represent sizes of values in registers or in memory.
/// Can also be used for other byte-valued numbers, like offsets,
/// as long as the number is guaranteed to be non-negative.
#[derive(
    Serialize,
    Deserialize,
    Debug,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    Hash,
    Clone,
    Copy,
    Display,
    Binary,
    Octal,
    LowerHex,
    UpperHex,
    From,
    Into,
    Not,
    Add,
    Sub,
    Mul,
    Div,
    Rem,
    Shr,
    Shl,
    AddAssign,
    SubAssign,
    MulAssign,
    DivAssign,
    RemAssign,
    ShrAssign,
    ShlAssign,
    Sum,
)]
#[serde(transparent)]
pub struct ByteSize(u64);

impl From<ByteSize> for BitSize {
    fn from(bytesize: ByteSize) -> BitSize {
        u16::try_from(u64::from(bytesize) * 8).unwrap()
    }
}

impl From<ByteSize> for apint::BitWidth {
    fn from(bytesize: ByteSize) -> apint::BitWidth {
        apint::BitWidth::from((u64::from(bytesize) * 8) as usize)
    }
}

impl From<apint::BitWidth> for ByteSize {
    /// Convert to `ByteSize`, while always rounding up to the nearest full byte.
    fn from(bitwidth: apint::BitWidth) -> ByteSize {
        ByteSize::new((bitwidth.to_usize() + 7) as u64 / 8)
    }
}

impl ByteSize {
    pub fn new(value: u64) -> ByteSize {
        ByteSize(value)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn check_bit_to_byte_conversion() {
        let bits: BitSize = 8;
        let bytes: ByteSize = bits.into();
        assert_eq!(u64::from(bytes), 1);
        let bits: BitSize = bytes.into();
        assert_eq!(bits, 8);
    }
}
