<commit_before>extern crate mysqlclient_sys as ffi;

use std::mem;
use std::os::raw as libc;

use super::stmt::Statement;
use crate::mysql::{MysqlType, MysqlTypeMetadata, MysqlValue};
use crate::result::QueryResult;

pub struct Binds {
    data: Vec<BindData>,
}

impl Binds {
    pub fn from_input_data<Iter>(input: Iter) -> Self
    where
        Iter: IntoIterator<Item = (MysqlTypeMetadata, Option<Vec<u8>>)>,
    {
        let data = input
            .into_iter()
            .map(|(metadata, bytes)| {
                BindData::for_input(metadata.data_type, metadata.is_unsigned as _, bytes)
            })
            .collect();

        Binds { data }
    }

    pub fn from_output_types(types: Vec<MysqlTypeMetadata>) -> Self {
        let data = types
            .into_iter()
            .map(|metadata| (metadata.data_type.into(), metadata.is_unsigned as _))
            .map(BindData::for_output)
            .collect();

        Binds { data }
    }

    pub fn from_result_metadata(fields: &[ffi::MYSQL_FIELD]) -> Self {
        let data = fields
            .iter()
            .map(|field| (field.type_, is_field_unsigned(field)))
            .map(BindData::for_output)
            .collect();

        Binds { data }
    }

    pub fn with_mysql_binds<F, T>(&mut self, f: F) -> T
    where
        F: FnOnce(*mut ffi::MYSQL_BIND) -> T,
    {
        let mut binds = self
            .data
            .iter_mut()
            .map(|x| unsafe { x.mysql_bind() })
            .collect::<Vec<_>>();
        f(binds.as_mut_ptr())
    }

    pub fn populate_dynamic_buffers(&mut self, stmt: &Statement) -> QueryResult<()> {
        for (i, data) in self.data.iter_mut().enumerate() {
            data.did_numeric_overflow_occur()?;
            // This is safe because we are re-binding the invalidated buffers
            // at the end of this function
            unsafe {
                if let Some((mut bind, offset)) = data.bind_for_truncated_data() {
                    stmt.fetch_column(&mut bind, i, offset)?
                } else {
                    data.update_buffer_length()
                }
            }
        }

        unsafe { self.with_mysql_binds(|bind_ptr| stmt.bind_result(bind_ptr)) }
    }

    pub fn update_buffer_lengths(&mut self) {
        for data in &mut self.data {
            data.update_buffer_length();
        }
    }

    pub fn field_data(&self, idx: usize) -> Option<MysqlValue<'_>> {
        let data = &self.data[idx];
        let tpe = data.tpe.into();
        self.data[idx].bytes().map(|bytes| {
            MysqlValue::new(
                bytes,
                MysqlTypeMetadata {
                    data_type: tpe,
                    is_unsigned: data.is_unsigned != 0,
                },
            )
        })
    }
}

struct BindData {
    tpe: ffi::enum_field_types,
    bytes: Vec<u8>,
    length: libc::c_ulong,
    is_null: ffi::my_bool,
    is_truncated: Option<ffi::my_bool>,
    is_unsigned: ffi::my_bool,
}

impl BindData {
    fn for_input(tpe: MysqlType, is_unsigned: ffi::my_bool, data: Option<Vec<u8>>) -> Self {
        let is_null = if data.is_none() { 1 } else { 0 };
        let bytes = data.unwrap_or_default();
        let length = bytes.len() as libc::c_ulong;

        BindData {
            tpe: tpe.into(),
            bytes,
            length,
            is_null,
            is_truncated: None,
            is_unsigned,
        }
    }

    fn for_output((tpe, is_unsigned): (ffi::enum_field_types, ffi::my_bool)) -> Self {
        let bytes = known_buffer_size_for_ffi_type(tpe)
            .map(|len| vec![0; len])
            .unwrap_or_default();
        let length = bytes.len() as libc::c_ulong;

        BindData {
            tpe,
            bytes,
            length,
            is_null: 0,
            is_truncated: Some(0),
            is_unsigned,
        }
    }

    fn is_truncated(&self) -> bool {
        self.is_truncated.unwrap_or(0) != 0
    }

    fn is_fixed_size_buffer(&self) -> bool {
        known_buffer_size_for_ffi_type(self.tpe).is_some()
    }

    fn bytes(&self) -> Option<&[u8]> {
        if self.is_null == 0 {
            Some(&*self.bytes)
        } else {
            None
        }
    }

    fn update_buffer_length(&mut self) {
        use std::cmp::min;

        let actual_bytes_in_buffer = min(self.bytes.capacity(), self.length as usize);
        unsafe { self.bytes.set_len(actual_bytes_in_buffer) }
    }

    unsafe fn mysql_bind(&mut self) -> ffi::MYSQL_BIND {
        let mut bind: ffi::MYSQL_BIND = mem::zeroed();
        bind.buffer_type = self.tpe;
        bind.buffer = self.bytes.as_mut_ptr() as *mut libc::c_void;
        bind.buffer_length = self.bytes.capacity() as libc::c_ulong;
        bind.length = &mut self.length;
        bind.is_null = &mut self.is_null;
        bind.is_unsigned = self.is_unsigned;

        if let Some(ref mut is_truncated) = self.is_truncated {
            bind.error = is_truncated;
        }

        bind
    }

    /// Resizes the byte buffer to fit the value of `self.length`, and returns
    /// a tuple of a bind pointing at the truncated data, and the offset to use
    /// in order to read the truncated data into it.
    ///
    /// This invalidates the bind previously returned by `mysql_bind`. Calling
    /// this function is unsafe unless the binds are immediately rebound.
    unsafe fn bind_for_truncated_data(&mut self) -> Option<(ffi::MYSQL_BIND, usize)> {
        if self.is_truncated() {
            let offset = self.bytes.capacity();
            let truncated_amount = self.length as usize - offset;

            debug_assert!(
                truncated_amount > 0,
                "output buffers were invalidated \
                 without calling `mysql_stmt_bind_result`"
            );
            self.bytes.set_len(offset);
            self.bytes.reserve(truncated_amount);
            self.bytes.set_len(self.length as usize);

            let mut bind = self.mysql_bind();
            bind.buffer = self.bytes[offset..].as_mut_ptr() as *mut libc::c_void;
            bind.buffer_length = truncated_amount as libc::c_ulong;
            Some((bind, offset))
        } else {
            None
        }
    }

    fn did_numeric_overflow_occur(&self) -> QueryResult<()> {
        use crate::result::Error::DeserializationError;

        if self.is_truncated() && self.is_fixed_size_buffer() {
            Err(DeserializationError(
                "Numeric overflow/underflow occurred".into(),
            ))
        } else {
            Ok(())
        }
    }
}

impl From<MysqlType> for ffi::enum_field_types {
    fn from(tpe: MysqlType) -> Self {
        use self::ffi::enum_field_types::*;

        match tpe {
            MysqlType::Tiny => MYSQL_TYPE_TINY,
            MysqlType::Short => MYSQL_TYPE_SHORT,
            MysqlType::Long => MYSQL_TYPE_LONG,
            MysqlType::LongLong => MYSQL_TYPE_LONGLONG,
            MysqlType::Float => MYSQL_TYPE_FLOAT,
            MysqlType::Double => MYSQL_TYPE_DOUBLE,
            MysqlType::Time => MYSQL_TYPE_TIME,
            MysqlType::Date => MYSQL_TYPE_DATE,
            MysqlType::DateTime => MYSQL_TYPE_DATETIME,
            MysqlType::Timestamp => MYSQL_TYPE_TIMESTAMP,
            MysqlType::String => MYSQL_TYPE_STRING,
            MysqlType::Blob => MYSQL_TYPE_BLOB,
            MysqlType::Numeric => MYSQL_TYPE_NEWDECIMAL,
        }
    }
}

impl From<ffi::enum_field_types> for MysqlType {
    fn from(tpe: ffi::enum_field_types) -> Self {
        use self::ffi::enum_field_types::*;

        // https://docs.oracle.com/cd/E17952_01/mysql-8.0-en/c-api-data-structures.html
        // https://dev.mysql.com/doc/dev/mysql-server/8.0.12/binary__log__types_8h.html
        // https://dev.mysql.com/doc/internals/en/binary-protocol-value.html
        // https://mariadb.com/kb/en/packet_bindata/
        match tpe {
            MYSQL_TYPE_TINY => MysqlType::Tiny,
            MYSQL_TYPE_YEAR | MYSQL_TYPE_SHORT => MysqlType::Short,
            MYSQL_TYPE_INT24 | MYSQL_TYPE_LONG => MysqlType::Long,
            MYSQL_TYPE_LONGLONG => MysqlType::LongLong,
            MYSQL_TYPE_FLOAT => MysqlType::Float,
            MYSQL_TYPE_DOUBLE => MysqlType::Double,
            MYSQL_TYPE_TIME => MysqlType::Time,
            MYSQL_TYPE_DATE => MysqlType::Date,
            MYSQL_TYPE_DATETIME => MysqlType::DateTime,
            MYSQL_TYPE_TIMESTAMP => MysqlType::Timestamp,
            MYSQL_TYPE_VAR_STRING | MYSQL_TYPE_VARCHAR | MYSQL_TYPE_STRING => MysqlType::String,
            MYSQL_TYPE_BLOB
            | MYSQL_TYPE_TINY_BLOB
            | MYSQL_TYPE_MEDIUM_BLOB
            | MYSQL_TYPE_LONG_BLOB => MysqlType::Blob,
            MYSQL_TYPE_DECIMAL | MYSQL_TYPE_NEWDECIMAL => MysqlType::Numeric,
            // Null value
            MYSQL_TYPE_NULL |
            // bit type
            // same encoding as string
            MYSQL_TYPE_BIT |
            // enum type
            // same encoding as string
            MYSQL_TYPE_ENUM |
            // set type
            // same encoding as string
            MYSQL_TYPE_SET |
            // spatial type
            // same encoding as string
            MYSQL_TYPE_GEOMETRY |
            // json type, only available on newer mysql versions
            // same encoding as string
            MYSQL_TYPE_JSON => unimplemented!(
                "Hit currently unsupported type, those variants should \
                 probably be variants of MysqlType or at least be mapped \
                 to one of the existing types"
            ),
            MYSQL_TYPE_NEWDATE
            | MYSQL_TYPE_TIME2
            | MYSQL_TYPE_DATETIME2
            | MYSQL_TYPE_TIMESTAMP2 => panic!(
                "The mysql documentation states that this types are \
                 only used on server side, so if you see this error \
                 something has gone wrong. Please open a issue at \
                 the diesel github repo."
            ),
        }
    }
}

fn known_buffer_size_for_ffi_type(tpe: ffi::enum_field_types) -> Option<usize> {
    use self::ffi::enum_field_types as t;
    use std::mem::size_of;

    match tpe {
        t::MYSQL_TYPE_TINY => Some(1),
        t::MYSQL_TYPE_YEAR | t::MYSQL_TYPE_SHORT => Some(2),
        t::MYSQL_TYPE_INT24 | t::MYSQL_TYPE_LONG | t::MYSQL_TYPE_FLOAT => Some(4),
        t::MYSQL_TYPE_LONGLONG | t::MYSQL_TYPE_DOUBLE => Some(8),
        t::MYSQL_TYPE_TIME
        | t::MYSQL_TYPE_DATE
        | t::MYSQL_TYPE_DATETIME
        | t::MYSQL_TYPE_TIMESTAMP => Some(size_of::<ffi::MYSQL_TIME>()),
        _ => None,
    }
}

fn is_field_unsigned(field: &ffi::MYSQL_FIELD) -> ffi::my_bool {
    const UNSIGNED_FLAG: libc::c_uint = 32;
    (field.flags & UNSIGNED_FLAG > 0) as _
}
<commit_msg>Only convert the mysql ffi type if it's not null<commit_after>extern crate mysqlclient_sys as ffi;

use std::mem;
use std::os::raw as libc;

use super::stmt::Statement;
use crate::mysql::{MysqlType, MysqlTypeMetadata, MysqlValue};
use crate::result::QueryResult;

pub struct Binds {
    data: Vec<BindData>,
}

impl Binds {
    pub fn from_input_data<Iter>(input: Iter) -> Self
    where
        Iter: IntoIterator<Item = (MysqlTypeMetadata, Option<Vec<u8>>)>,
    {
        let data = input
            .into_iter()
            .map(|(metadata, bytes)| {
                BindData::for_input(metadata.data_type, metadata.is_unsigned as _, bytes)
            })
            .collect();

        Binds { data }
    }

    pub fn from_output_types(types: Vec<MysqlTypeMetadata>) -> Self {
        let data = types
            .into_iter()
            .map(|metadata| (metadata.data_type.into(), metadata.is_unsigned as _))
            .map(BindData::for_output)
            .collect();

        Binds { data }
    }

    pub fn from_result_metadata(fields: &[ffi::MYSQL_FIELD]) -> Self {
        let data = fields
            .iter()
            .map(|field| (field.type_, is_field_unsigned(field)))
            .map(BindData::for_output)
            .collect();

        Binds { data }
    }

    pub fn with_mysql_binds<F, T>(&mut self, f: F) -> T
    where
        F: FnOnce(*mut ffi::MYSQL_BIND) -> T,
    {
        let mut binds = self
            .data
            .iter_mut()
            .map(|x| unsafe { x.mysql_bind() })
            .collect::<Vec<_>>();
        f(binds.as_mut_ptr())
    }

    pub fn populate_dynamic_buffers(&mut self, stmt: &Statement) -> QueryResult<()> {
        for (i, data) in self.data.iter_mut().enumerate() {
            data.did_numeric_overflow_occur()?;
            // This is safe because we are re-binding the invalidated buffers
            // at the end of this function
            unsafe {
                if let Some((mut bind, offset)) = data.bind_for_truncated_data() {
                    stmt.fetch_column(&mut bind, i, offset)?
                } else {
                    data.update_buffer_length()
                }
            }
        }

        unsafe { self.with_mysql_binds(|bind_ptr| stmt.bind_result(bind_ptr)) }
    }

    pub fn update_buffer_lengths(&mut self) {
        for data in &mut self.data {
            data.update_buffer_length();
        }
    }

    pub fn field_data(&self, idx: usize) -> Option<MysqlValue<'_>> {
        let data = &self.data[idx];
        self.data[idx].bytes().map(|bytes| {
            let tpe = data.tpe.into();
            MysqlValue::new(
                bytes,
                MysqlTypeMetadata {
                    data_type: tpe,
                    is_unsigned: data.is_unsigned != 0,
                },
            )
        })
    }
}

struct BindData {
    tpe: ffi::enum_field_types,
    bytes: Vec<u8>,
    length: libc::c_ulong,
    is_null: ffi::my_bool,
    is_truncated: Option<ffi::my_bool>,
    is_unsigned: ffi::my_bool,
}

impl BindData {
    fn for_input(tpe: MysqlType, is_unsigned: ffi::my_bool, data: Option<Vec<u8>>) -> Self {
        let is_null = if data.is_none() { 1 } else { 0 };
        let bytes = data.unwrap_or_default();
        let length = bytes.len() as libc::c_ulong;

        BindData {
            tpe: tpe.into(),
            bytes,
            length,
            is_null,
            is_truncated: None,
            is_unsigned,
        }
    }

    fn for_output((tpe, is_unsigned): (ffi::enum_field_types, ffi::my_bool)) -> Self {
        let bytes = known_buffer_size_for_ffi_type(tpe)
            .map(|len| vec![0; len])
            .unwrap_or_default();
        let length = bytes.len() as libc::c_ulong;

        BindData {
            tpe,
            bytes,
            length,
            is_null: 0,
            is_truncated: Some(0),
            is_unsigned,
        }
    }

    fn is_truncated(&self) -> bool {
        self.is_truncated.unwrap_or(0) != 0
    }

    fn is_fixed_size_buffer(&self) -> bool {
        known_buffer_size_for_ffi_type(self.tpe).is_some()
    }

    fn bytes(&self) -> Option<&[u8]> {
        if self.is_null == 0 {
            Some(&*self.bytes)
        } else {
            None
        }
    }

    fn update_buffer_length(&mut self) {
        use std::cmp::min;

        let actual_bytes_in_buffer = min(self.bytes.capacity(), self.length as usize);
        unsafe { self.bytes.set_len(actual_bytes_in_buffer) }
    }

    unsafe fn mysql_bind(&mut self) -> ffi::MYSQL_BIND {
        let mut bind: ffi::MYSQL_BIND = mem::zeroed();
        bind.buffer_type = self.tpe;
        bind.buffer = self.bytes.as_mut_ptr() as *mut libc::c_void;
        bind.buffer_length = self.bytes.capacity() as libc::c_ulong;
        bind.length = &mut self.length;
        bind.is_null = &mut self.is_null;
        bind.is_unsigned = self.is_unsigned;

        if let Some(ref mut is_truncated) = self.is_truncated {
            bind.error = is_truncated;
        }

        bind
    }

    /// Resizes the byte buffer to fit the value of `self.length`, and returns
    /// a tuple of a bind pointing at the truncated data, and the offset to use
    /// in order to read the truncated data into it.
    ///
    /// This invalidates the bind previously returned by `mysql_bind`. Calling
    /// this function is unsafe unless the binds are immediately rebound.
    unsafe fn bind_for_truncated_data(&mut self) -> Option<(ffi::MYSQL_BIND, usize)> {
        if self.is_truncated() {
            let offset = self.bytes.capacity();
            let truncated_amount = self.length as usize - offset;

            debug_assert!(
                truncated_amount > 0,
                "output buffers were invalidated \
                 without calling `mysql_stmt_bind_result`"
            );
            self.bytes.set_len(offset);
            self.bytes.reserve(truncated_amount);
            self.bytes.set_len(self.length as usize);

            let mut bind = self.mysql_bind();
            bind.buffer = self.bytes[offset..].as_mut_ptr() as *mut libc::c_void;
            bind.buffer_length = truncated_amount as libc::c_ulong;
            Some((bind, offset))
        } else {
            None
        }
    }

    fn did_numeric_overflow_occur(&self) -> QueryResult<()> {
        use crate::result::Error::DeserializationError;

        if self.is_truncated() && self.is_fixed_size_buffer() {
            Err(DeserializationError(
                "Numeric overflow/underflow occurred".into(),
            ))
        } else {
            Ok(())
        }
    }
}

impl From<MysqlType> for ffi::enum_field_types {
    fn from(tpe: MysqlType) -> Self {
        use self::ffi::enum_field_types::*;

        match tpe {
            MysqlType::Tiny => MYSQL_TYPE_TINY,
            MysqlType::Short => MYSQL_TYPE_SHORT,
            MysqlType::Long => MYSQL_TYPE_LONG,
            MysqlType::LongLong => MYSQL_TYPE_LONGLONG,
            MysqlType::Float => MYSQL_TYPE_FLOAT,
            MysqlType::Double => MYSQL_TYPE_DOUBLE,
            MysqlType::Time => MYSQL_TYPE_TIME,
            MysqlType::Date => MYSQL_TYPE_DATE,
            MysqlType::DateTime => MYSQL_TYPE_DATETIME,
            MysqlType::Timestamp => MYSQL_TYPE_TIMESTAMP,
            MysqlType::String => MYSQL_TYPE_STRING,
            MysqlType::Blob => MYSQL_TYPE_BLOB,
            MysqlType::Numeric => MYSQL_TYPE_NEWDECIMAL,
        }
    }
}

impl From<ffi::enum_field_types> for MysqlType {
    fn from(tpe: ffi::enum_field_types) -> Self {
        use self::ffi::enum_field_types::*;

        // https://docs.oracle.com/cd/E17952_01/mysql-8.0-en/c-api-data-structures.html
        // https://dev.mysql.com/doc/dev/mysql-server/8.0.12/binary__log__types_8h.html
        // https://dev.mysql.com/doc/internals/en/binary-protocol-value.html
        // https://mariadb.com/kb/en/packet_bindata/
        match tpe {
            MYSQL_TYPE_TINY => MysqlType::Tiny,
            MYSQL_TYPE_YEAR | MYSQL_TYPE_SHORT => MysqlType::Short,
            MYSQL_TYPE_INT24 | MYSQL_TYPE_LONG => MysqlType::Long,
            MYSQL_TYPE_LONGLONG => MysqlType::LongLong,
            MYSQL_TYPE_FLOAT => MysqlType::Float,
            MYSQL_TYPE_DOUBLE => MysqlType::Double,
            MYSQL_TYPE_TIME => MysqlType::Time,
            MYSQL_TYPE_DATE => MysqlType::Date,
            MYSQL_TYPE_DATETIME => MysqlType::DateTime,
            MYSQL_TYPE_TIMESTAMP => MysqlType::Timestamp,
            MYSQL_TYPE_VAR_STRING | MYSQL_TYPE_VARCHAR | MYSQL_TYPE_STRING => MysqlType::String,
            MYSQL_TYPE_BLOB
            | MYSQL_TYPE_TINY_BLOB
            | MYSQL_TYPE_MEDIUM_BLOB
            | MYSQL_TYPE_LONG_BLOB => MysqlType::Blob,
            MYSQL_TYPE_DECIMAL | MYSQL_TYPE_NEWDECIMAL => MysqlType::Numeric,
            // Null value
            MYSQL_TYPE_NULL => unreachable!(
                "We ensure at the call side that we do not hit this type here. \
                 If you ever see this error, something has gone very wrong. \
                 Please open a issue at the diesel github repo in this case"
            ),
            // bit type
            // same encoding as string
            MYSQL_TYPE_BIT |
            // enum type
            // same encoding as string
            MYSQL_TYPE_ENUM |
            // set type
            // same encoding as string
            MYSQL_TYPE_SET |
            // spatial type
            // same encoding as string
            MYSQL_TYPE_GEOMETRY |
            // json type, only available on newer mysql versions
            // same encoding as string
            MYSQL_TYPE_JSON => unimplemented!(
                "Hit currently unsupported type, those variants should \
                 probably be variants of MysqlType or at least be mapped \
                 to one of the existing types"
            ),
            MYSQL_TYPE_NEWDATE
            | MYSQL_TYPE_TIME2
            | MYSQL_TYPE_DATETIME2
            | MYSQL_TYPE_TIMESTAMP2 => unreachable!(
                "The mysql documentation states that this types are \
                 only used on server side, so if you see this error \
                 something has gone wrong. Please open a issue at \
                 the diesel github repo."
            ),
        }
    }
}

fn known_buffer_size_for_ffi_type(tpe: ffi::enum_field_types) -> Option<usize> {
    use self::ffi::enum_field_types as t;
    use std::mem::size_of;

    match tpe {
        t::MYSQL_TYPE_TINY => Some(1),
        t::MYSQL_TYPE_YEAR | t::MYSQL_TYPE_SHORT => Some(2),
        t::MYSQL_TYPE_INT24 | t::MYSQL_TYPE_LONG | t::MYSQL_TYPE_FLOAT => Some(4),
        t::MYSQL_TYPE_LONGLONG | t::MYSQL_TYPE_DOUBLE => Some(8),
        t::MYSQL_TYPE_TIME
        | t::MYSQL_TYPE_DATE
        | t::MYSQL_TYPE_DATETIME
        | t::MYSQL_TYPE_TIMESTAMP => Some(size_of::<ffi::MYSQL_TIME>()),
        _ => None,
    }
}

fn is_field_unsigned(field: &ffi::MYSQL_FIELD) -> ffi::my_bool {
    const UNSIGNED_FLAG: libc::c_uint = 32;
    (field.flags & UNSIGNED_FLAG > 0) as _
}
<|endoftext|>


<commit_before>use addressable::Addressable;
use addressable::AddressableUtil;
use ram::Ram;

enum Operand {
	Implied,							// OPC				// operand implied
	Immediate(u8),						// OPC #$BB			// operand is byte (BB)
	Accumulator,						// OPC A			// operand is AC
	Relative(i8),						// OPC $BB			// branch target is PC + offset (BB), bit 7 signifies negative offset
	Absolute(u16),						// OPC $HHLL		// operand is address $HHLL
	AbsoluteIndexedWithX(u16),			// OPC $HHLL,X		// operand is address $HHLL incremented by X with carry
	AbsoluteIndexedWithY(u16),			// OPC $HHLL,Y		// operand is address $HHLL incremented by Y with carry
	Indirect(u16),						// OPC ($HHLL)		// operand is effective address; effective address is value of address
	ZeroPage(u8),						// OPC $LL			// operand is address $00LL
	ZeroPageIndexedWithX(u8),			// OPC $LL,X		// operand is address $00LL incremented by X, no page transition
	ZeroPageIndexedWithY(u8),			// OPC $LL,Y		// operand is address $00LL incremented by Y, no page transition
	ZeroPageIndexedWithXIndirect(u8),	// OPC ($LL,X)		// operand is effective address; effective address is $00LL incremented by X without carry
	ZeroPageIndirectIndexedWithY(u8),	// OPC ($LL),Y		// operand is effective address incremented by Y with carry; effective address is word at $00LL
}

impl Operand {
	fn addr (&self, cpu: &CPU) -> u16 {
		match * self {
			Implied								=> fail!("mos6510: Implied operand is never targetted to an address"),
			Immediate(_)						=> fail!("mos6510: Immediade operand is never targetted to an address"),
			Accumulator							=> fail!("mos6510: Accumulator operand is never targetted to an address"),
			Relative(offset)					=> cpu.pc + offset as u16,
			Absolute(addr)						=> addr,
			AbsoluteIndexedWithX(addr)			=> addr + cpu.x as u16,
			AbsoluteIndexedWithY(addr)			=> addr + cpu.y as u16,
			Indirect(addr)						=> cpu.get_le(addr),
			ZeroPage(addr)						=> addr as u16,
			ZeroPageIndexedWithX(addr)			=> (addr + cpu.x) as u16,				// no page transition
			ZeroPageIndexedWithY(addr)			=> (addr + cpu.y) as u16,				// no page transition
			ZeroPageIndexedWithXIndirect(addr)	=> cpu.get_le((addr + cpu.x) as u16),	// no page transition
			ZeroPageIndirectIndexedWithY(addr)	=> { let iaddr: u16 = cpu.get_le(addr as u16); iaddr + cpu.y as u16 },
		}
	}

	fn get (&self, cpu: &CPU) -> u8 {
		match *self {
			Implied								=> fail!("mos6510: Implied operand never has a value"),
			Immediate(val)						=> val,
			Accumulator							=> cpu.ac,
			Relative(_target)					=> fail!("mos6510: Relative operand never has a value"),
			op									=> { let addr = op.addr(cpu); cpu.get(addr) },
		}
	}

	fn set (&self, val: u8, cpu: &mut CPU) {
		match *self {
			Implied								=> fail!("mos6510: Implied operand never sets a value"),
			Immediate(_)						=> fail!("mos6510: Immediate operand never sets a value"),
			Accumulator							=> cpu.ac = val,
			Relative(_target)					=> fail!("mos6510: Relative operand never sets a value"),
			op									=> { let addr = op.addr(cpu); cpu.set(addr, val); },
		}
	}
}

pub struct CPU {
	pc: u16,				// program counter
	ac: u8,					// accumulator
	x: u8,					// x register
	y: u8,					// y register
	sr: u8,					// status register (NV-BDIZC: Negative, oVerflow, Break, Decimal, Interrupt, Zero, Carry)
	sp: u8,					// stack pointer
	mem: ~Ram<u16>,
}

impl Addressable<u16> for CPU {
	pub fn get (&self, addr: u16) -> u8 {
		// TODO: addresses $0000 (data direction) and $0001 (data) are hardwired for the processor I/O port
		self.mem.get(addr)
	}

	pub fn set (&mut self, addr: u16, data: u8) {
		// TODO: addresses $0000 (data direction) and $0001 (data) are hardwired for the processor I/O port
		self.mem.set(addr, data);
	}
}
<commit_msg>Added nmi/reset/irq vectors<commit_after>use addressable::Addressable;
use addressable::AddressableUtil;
use ram::Ram;

enum Operand {
	Implied,							// OPC				// operand implied
	Immediate(u8),						// OPC #$BB			// operand is byte (BB)
	Accumulator,						// OPC A			// operand is AC
	Relative(i8),						// OPC $BB			// branch target is PC + offset (BB), bit 7 signifies negative offset
	Absolute(u16),						// OPC $HHLL		// operand is address $HHLL
	AbsoluteIndexedWithX(u16),			// OPC $HHLL,X		// operand is address $HHLL incremented by X with carry
	AbsoluteIndexedWithY(u16),			// OPC $HHLL,Y		// operand is address $HHLL incremented by Y with carry
	Indirect(u16),						// OPC ($HHLL)		// operand is effective address; effective address is value of address
	ZeroPage(u8),						// OPC $LL			// operand is address $00LL
	ZeroPageIndexedWithX(u8),			// OPC $LL,X		// operand is address $00LL incremented by X, no page transition
	ZeroPageIndexedWithY(u8),			// OPC $LL,Y		// operand is address $00LL incremented by Y, no page transition
	ZeroPageIndexedWithXIndirect(u8),	// OPC ($LL,X)		// operand is effective address; effective address is $00LL incremented by X without carry
	ZeroPageIndirectIndexedWithY(u8),	// OPC ($LL),Y		// operand is effective address incremented by Y with carry; effective address is word at $00LL
}

impl Operand {
	fn addr (&self, cpu: &CPU) -> u16 {
		match * self {
			Implied								=> fail!("mos6510: Implied operand is never targetted to an address"),
			Immediate(_)						=> fail!("mos6510: Immediade operand is never targetted to an address"),
			Accumulator							=> fail!("mos6510: Accumulator operand is never targetted to an address"),
			Relative(offset)					=> cpu.pc + offset as u16,
			Absolute(addr)						=> addr,
			AbsoluteIndexedWithX(addr)			=> addr + cpu.x as u16,
			AbsoluteIndexedWithY(addr)			=> addr + cpu.y as u16,
			Indirect(addr)						=> cpu.get_le(addr),
			ZeroPage(addr)						=> addr as u16,
			ZeroPageIndexedWithX(addr)			=> (addr + cpu.x) as u16,				// no page transition
			ZeroPageIndexedWithY(addr)			=> (addr + cpu.y) as u16,				// no page transition
			ZeroPageIndexedWithXIndirect(addr)	=> cpu.get_le((addr + cpu.x) as u16),	// no page transition
			ZeroPageIndirectIndexedWithY(addr)	=> { let iaddr: u16 = cpu.get_le(addr as u16); iaddr + cpu.y as u16 },
		}
	}

	fn get (&self, cpu: &CPU) -> u8 {
		match *self {
			Implied								=> fail!("mos6510: Implied operand never has a value"),
			Immediate(val)						=> val,
			Accumulator							=> cpu.ac,
			Relative(_target)					=> fail!("mos6510: Relative operand never has a value"),
			op									=> { let addr = op.addr(cpu); cpu.get(addr) },
		}
	}

	fn set (&self, val: u8, cpu: &mut CPU) {
		match *self {
			Implied								=> fail!("mos6510: Implied operand never sets a value"),
			Immediate(_)						=> fail!("mos6510: Immediate operand never sets a value"),
			Accumulator							=> cpu.ac = val,
			Relative(_target)					=> fail!("mos6510: Relative operand never sets a value"),
			op									=> { let addr = op.addr(cpu); cpu.set(addr, val); },
		}
	}
}

static nmi_vector: u16 = 0xfffa;
static reset_vector: u16 = 0xfffc;
static irq_vector: u16 = 0xfffe;

pub struct CPU {
	pc: u16,				// program counter
	ac: u8,					// accumulator
	x: u8,					// x register
	y: u8,					// y register
	sr: u8,					// status register (NV-BDIZC: Negative, oVerflow, Break, Decimal, Interrupt, Zero, Carry)
	sp: u8,					// stack pointer
	mem: ~Ram<u16>,
}

impl Addressable<u16> for CPU {
	pub fn get (&self, addr: u16) -> u8 {
		// TODO: addresses $0000 (data direction) and $0001 (data) are hardwired for the processor I/O port
		self.mem.get(addr)
	}

	pub fn set (&mut self, addr: u16, data: u8) {
		// TODO: addresses $0000 (data direction) and $0001 (data) are hardwired for the processor I/O port
		self.mem.set(addr, data);
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(never_type)]

fn foo(x: usize, y: !, z: usize) { }

fn call_foo_a() {
    foo(return, 22, 44);
    //~^ ERROR mismatched types
}

fn call_foo_b() {
    // Divergence happens in the argument itself, definitely ok.
    foo(22, return, 44);
}

fn call_foo_c() {
    // This test fails because the divergence happens **after** the
    // coercion to `!`:
    foo(22, 44, return); //~ ERROR mismatched types
}

fn call_foo_d() {
    // This test passes because `a` has type `!`:
    let a: ! = return;
    let b = 22;
    let c = 44;
    foo(a, b, c); // ... and hence a reference to `a` is expected to diverge.
    //~^ ERROR mismatched types
}

fn call_foo_e() {
    // This test probably could pass but we don't *know* that `a`
    // has type `!` so we don't let it work.
    let a = return;
    let b = 22;
    let c = 44;
    foo(a, b, c); //~ ERROR mismatched types
}

fn call_foo_f() {
    // This fn fails because `a` has type `usize`, and hence a
    // reference to is it **not** considered to diverge.
    let a: usize = return;
    let b = 22;
    let c = 44;
    foo(a, b, c); //~ ERROR mismatched types
}

fn array_a() {
    // Accepted: return is coerced to `!` just fine, but `22` cannot be.
    let x: [!; 2] = [return, 22]; //~ ERROR mismatched types
}

fn array_b() {
    // Error: divergence has not yet occurred.
    let x: [!; 2] = [22, return]; //~ ERROR mismatched types
}

fn tuple_a() {
    // No divergence at all.
    let x: (usize, !, usize) = (22, 44, 66); //~ ERROR mismatched types
}

fn tuple_b() {
    // Divergence happens before coercion: OK
    let x: (usize, !, usize) = (return, 44, 66);
    //~^ ERROR mismatched types
}

fn tuple_c() {
    // Divergence happens before coercion: OK
    let x: (usize, !, usize) = (22, return, 66);
}

fn tuple_d() {
    // Error: divergence happens too late
    let x: (usize, !, usize) = (22, 44, return); //~ ERROR mismatched types
}

fn main() { }
<commit_msg>Test is no longer "accepted"<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(never_type)]

fn foo(x: usize, y: !, z: usize) { }

fn call_foo_a() {
    foo(return, 22, 44);
    //~^ ERROR mismatched types
}

fn call_foo_b() {
    // Divergence happens in the argument itself, definitely ok.
    foo(22, return, 44);
}

fn call_foo_c() {
    // This test fails because the divergence happens **after** the
    // coercion to `!`:
    foo(22, 44, return); //~ ERROR mismatched types
}

fn call_foo_d() {
    // This test passes because `a` has type `!`:
    let a: ! = return;
    let b = 22;
    let c = 44;
    foo(a, b, c); // ... and hence a reference to `a` is expected to diverge.
    //~^ ERROR mismatched types
}

fn call_foo_e() {
    // This test probably could pass but we don't *know* that `a`
    // has type `!` so we don't let it work.
    let a = return;
    let b = 22;
    let c = 44;
    foo(a, b, c); //~ ERROR mismatched types
}

fn call_foo_f() {
    // This fn fails because `a` has type `usize`, and hence a
    // reference to is it **not** considered to diverge.
    let a: usize = return;
    let b = 22;
    let c = 44;
    foo(a, b, c); //~ ERROR mismatched types
}

fn array_a() {
    // Return is coerced to `!` just fine, but `22` cannot be.
    let x: [!; 2] = [return, 22]; //~ ERROR mismatched types
}

fn array_b() {
    // Error: divergence has not yet occurred.
    let x: [!; 2] = [22, return]; //~ ERROR mismatched types
}

fn tuple_a() {
    // No divergence at all.
    let x: (usize, !, usize) = (22, 44, 66); //~ ERROR mismatched types
}

fn tuple_b() {
    // Divergence happens before coercion: OK
    let x: (usize, !, usize) = (return, 44, 66);
    //~^ ERROR mismatched types
}

fn tuple_c() {
    // Divergence happens before coercion: OK
    let x: (usize, !, usize) = (22, return, 66);
}

fn tuple_d() {
    // Error: divergence happens too late
    let x: (usize, !, usize) = (22, 44, return); //~ ERROR mismatched types
}

fn main() { }
<|endoftext|>


<commit_before>#![feature(const_transmute)]
#![allow(const_err)] // make sure we cannot allow away the errors tested here

use std::mem;

const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) };
//~^ ERROR it is undefined behavior to use this value
//~^^ type validation failed: encountered unaligned reference (required 2 byte alignment but found 1)

const NULL: &u16 = unsafe { mem::transmute(0usize) };
//~^ ERROR it is undefined behavior to use this value

const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) };
//~^ ERROR it is undefined behavior to use this value

const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }];
//~^ ERROR it is undefined behavior to use this value

const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) };
//~^ ERROR it is undefined behavior to use this value

fn main() {}
<commit_msg>tidy line length override #58617<commit_after>// ignore-tidy-linelength
#![feature(const_transmute)]
#![allow(const_err)] // make sure we cannot allow away the errors tested here

use std::mem;

const UNALIGNED: &u16 = unsafe { mem::transmute(&[0u8; 4]) };
//~^ ERROR it is undefined behavior to use this value
//~^^ type validation failed: encountered unaligned reference (required 2 byte alignment but found 1)

const NULL: &u16 = unsafe { mem::transmute(0usize) };
//~^ ERROR it is undefined behavior to use this value

const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) };
//~^ ERROR it is undefined behavior to use this value

const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }];
//~^ ERROR it is undefined behavior to use this value

const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) };
//~^ ERROR it is undefined behavior to use this value

fn main() {}
<|endoftext|>


<commit_before>// Copyright 2020 lowRISC contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

//! SPI flash protocol.

use crate::io::BeInt;
use crate::io::Read;
use crate::io::Write;
use crate::protocol::wire::FromWireError;
use crate::protocol::wire::FromWire;
use crate::protocol::wire::ToWireError;
use crate::protocol::wire::ToWire;
use crate::protocol::wire::WireEnum;

use core::convert::TryFrom;
use core::result::Result;

/// SPI flash address modes.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum AddressMode {
    /// Address is represented by 3 bytes.
    ThreeByte = 0,

    /// Address is represented by 4 bytes.
    FourByte = 1,
}

/// Error for invalid address mode conversion.
pub struct InvalidAddressMode;

impl TryFrom<usize> for AddressMode {
    type Error = InvalidAddressMode;

    fn try_from(item: usize) -> Result<AddressMode, Self::Error> {
        match item {
            0 => Ok(AddressMode::ThreeByte),
            1 => Ok(AddressMode::FourByte),
            _ => Err(InvalidAddressMode),
        }
    }
}

impl From<AddressMode> for usize {
    fn from(item: AddressMode) -> usize {
        item as usize
    }
}

wire_enum! {
    /// SPI flash op codes
    pub enum OpCode: u8 {
        /// No operation
        Nop = 0x00,

        ////////////////////////////////////////////////////////////
        // Status commands

        /// Write to eeprom_status register.
        WriteStatusRegister = 0x01,

        /// Returns contents of eeprom_status register.
        /// Implemented in hardware.
        ReadStatusRegister = 0x05,

        /// Disables writes to device, sets WEL = 0 in hardware.
        WriteDisable = 0x04,

        /// Enables writes to device, sets WEL = 1 in hardware.
        WriteEnable = 0x06,

        /// Suspend write. Software should set WSP or WSE = 1.
        WriteSuspend = 0xb0,

        /// Resumes write. Software should set WSP or WSE = 0.
        WriteResume = 0x30,

        ////////////////////////////////////////////////////////////
        // Erase and program commands

        /// Clears bits of a particular 4KB sector to '1'.
        /// Must be implemented in software. HW sets BUSY bit.
        SectorErase = 0x20,

        /// Clears bits of a particular 32KB block to '1'.
        /// Must be implemented in software. HW sets BUSY bit.
        BlockErase32KB = 0x52,

        /// Clears bits of a particular 64KB block to '1'.
        /// Must be implemented in software. HW sets BUSY bit.
        BlockErase64KB = 0xd8,

        /// Clears all bits to '1'.
        /// Must be implemented in software. HW sets BUSY bit.
        ChipErase = 0xc7,

        /// Alternative op code for ChipErase. HW sets BUSY bit.
        ChipErase2 = 0x60,

        /// Programs up to 256 bytes of memory.
        /// Must be implemented in software. HW sets BUSY bit.
        PageProgram = 0x02,

        ////////////////////////////////////////////////////////////
        // ID commands

        /// Retrieves JEDEC-ID as configured in jedec_id registers.
        /// Implemented in hardware.
        ReadJedec = 0x9f,

        /// Retrieves SFDP as configured in sfdp registers.
        /// Implemented in hardware.
        ReadSfdp = 0x5a,

        ////////////////////////////////////////////////////////////
        // Read commands

        /// Retrieves data. The behavior of this command depends on the selected
        /// mode.
        NormalRead = 0x03,

        /// Retrieves data. The behavior of this command depends on the selected
        /// mode. Fast read includes a 1 byte delay after retrieving the last
        /// bit of the addrees before the first bit of data is delivered.
        FastRead = 0x0b,

        /// Similar to FastReads but uses explicit 4 byte addressing.
        FastRead4B = 0x0c,

        /// Similar to FastRead with output on both MISO and MOSI.
        FastReadDualOutput = 0x3b,

        ////////////////////////////////////////////////////////////
        // Address mode commands

        /// Enable 4 byte address mode.
        /// Must be implemented in software.
        Enter4ByteAddressMode = 0xb7,

        /// Disable 4 byte address mode and revert to 3 byte address mode.
        /// Must be implemented in software.
        Exit4ByteAddressMode = 0xe9,
    }
}

impl<'a> OpCode {
    /// Returns true iff the OpCode requires an address.
    pub fn has_address(&self) -> bool {
        match self {
            Self::SectorErase => true,
            Self::BlockErase32KB => true,
            Self::BlockErase64KB => true,
            Self::PageProgram => true,
            Self::ReadSfdp => true,
            Self::NormalRead => true,
            Self::FastRead => true,
            Self::FastRead4B => true,
            Self::FastReadDualOutput => true,
            _ => false,
        }
    }

    /// Returns true iff the OpCode requires a dummy byte.
    pub fn has_dummy_byte(&self) -> bool {
        match self {
            Self::FastRead => true,
            Self::FastRead4B => true,
            Self::FastReadDualOutput => true,
            _ => false,
        }
    }

    /// Returns true iff the OpCode requires data.
    pub fn has_data(&self) -> bool {
        match self {
            Self::PageProgram => true,
            _ => false,
        }
    }

    /// Returns true iff the OpCode requires for the BUSY bit to clear.
    pub fn wait_busy_clear(&self) -> bool {
        match self {
            Self::WriteSuspend => true,
            Self::WriteResume => true,
            Self::SectorErase => true,
            Self::BlockErase32KB => true,
            Self::BlockErase64KB => true,
            Self::ChipErase => true,
            Self::ChipErase2 => true,
            Self::PageProgram => true,
            _ => false,
        }
    }
}

const DUMMY_BYTE_VALUE: u8 = 0xff;

/// A parsed SPI flash protocol header.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Header<AddrType> {
    /// The SPI op code
    pub opcode: OpCode,

    /// The address.
    ///
    /// Note that not all SPI op codes require an address.
    pub address: Option<AddrType>,
}

impl<'a, AddrType> Header<AddrType>
where AddrType: BeInt {
    fn from_wire<R: Read<'a>>(mut r: R) -> Result<Self, FromWireError> {
        let opcode_u8 = r.read_be::<u8>()?;
        let opcode = OpCode::from_wire_value(opcode_u8).ok_or(FromWireError::OutOfRange)?;

        let mut address: Option<AddrType> = None;
        if opcode.has_address() {
            address = Some(r.read_be::<AddrType>()?);
        }

        if opcode.has_dummy_byte() {
            // We don't actually care about the value, we just need to consume it.
            let _ = r.read_be::<u8>()?;
        }

        Ok(Self {
            opcode,
            address,
        })
    }

    fn to_wire<W: Write>(&self, mut w: W) -> Result<(), ToWireError> {
        w.write_be(self.opcode.to_wire_value())?;
        if self.opcode.has_address() {
            if ! self.address.is_some() {
                return Err(ToWireError::InvalidData)
            }
            w.write_be(self.address.unwrap())?;
        }
        if self.opcode.has_dummy_byte() {
            w.write_be(DUMMY_BYTE_VALUE)?;
        }

        Ok(())
    }
}

/// Non-generic accessor trait for Header<AddrType>
pub trait SpiHeader {
    /// Get SPI op code
    fn get_opcode(&self) -> OpCode;

    /// Get address.
    fn get_address(&self) -> Option<u32>;
}

impl SpiHeader for Header<ux::u24> {
    fn get_opcode(&self) -> OpCode { return self.opcode }
    fn get_address(&self) -> Option<u32> {
        self.address.map(|val| u32::from(val))
    }
}

impl SpiHeader for Header<u32> {
    fn get_opcode(&self) -> OpCode { return self.opcode }
    fn get_address(&self) -> Option<u32> {
        self.address
    }
}

impl<'a> FromWire<'a> for Header<ux::u24> {
    fn from_wire<R: Read<'a>>(r: R) -> Result<Self, FromWireError> {
        Self::from_wire(r)
    }
}

impl ToWire for Header<ux::u24> {
    fn to_wire<W: Write>(&self, w: W) -> Result<(), ToWireError> {
        self.to_wire(w)
    }
}

impl<'a> FromWire<'a> for Header<u32> {
    fn from_wire<R: Read<'a>>(r: R) -> Result<Self, FromWireError> {
        Self::from_wire(r)
    }
}

impl ToWire for Header<u32> {
    fn to_wire<W: Write>(&self, w: W) -> Result<(), ToWireError> {
        self.to_wire(w)
    }
}

<commit_msg>spiutils: Replace SpiHeader trait with Address trait<commit_after>// Copyright 2020 lowRISC contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

//! SPI flash protocol.

use crate::io::BeInt;
use crate::io::Read;
use crate::io::Write;
use crate::protocol::wire::FromWireError;
use crate::protocol::wire::FromWire;
use crate::protocol::wire::ToWireError;
use crate::protocol::wire::ToWire;
use crate::protocol::wire::WireEnum;

use core::convert::Into;
use core::convert::TryFrom;
use core::result::Result;

/// SPI flash address modes.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum AddressMode {
    /// Address is represented by 3 bytes.
    ThreeByte = 0,

    /// Address is represented by 4 bytes.
    FourByte = 1,
}

/// Error for invalid address mode conversion.
pub struct InvalidAddressMode;

impl TryFrom<usize> for AddressMode {
    type Error = InvalidAddressMode;

    fn try_from(item: usize) -> Result<AddressMode, Self::Error> {
        match item {
            0 => Ok(AddressMode::ThreeByte),
            1 => Ok(AddressMode::FourByte),
            _ => Err(InvalidAddressMode),
        }
    }
}

impl From<AddressMode> for usize {
    fn from(item: AddressMode) -> usize {
        item as usize
    }
}

wire_enum! {
    /// SPI flash op codes
    pub enum OpCode: u8 {
        /// No operation
        Nop = 0x00,

        ////////////////////////////////////////////////////////////
        // Status commands

        /// Write to eeprom_status register.
        WriteStatusRegister = 0x01,

        /// Returns contents of eeprom_status register.
        /// Implemented in hardware.
        ReadStatusRegister = 0x05,

        /// Disables writes to device, sets WEL = 0 in hardware.
        WriteDisable = 0x04,

        /// Enables writes to device, sets WEL = 1 in hardware.
        WriteEnable = 0x06,

        /// Suspend write. Software should set WSP or WSE = 1.
        WriteSuspend = 0xb0,

        /// Resumes write. Software should set WSP or WSE = 0.
        WriteResume = 0x30,

        ////////////////////////////////////////////////////////////
        // Erase and program commands

        /// Clears bits of a particular 4KB sector to '1'.
        /// Must be implemented in software. HW sets BUSY bit.
        SectorErase = 0x20,

        /// Clears bits of a particular 32KB block to '1'.
        /// Must be implemented in software. HW sets BUSY bit.
        BlockErase32KB = 0x52,

        /// Clears bits of a particular 64KB block to '1'.
        /// Must be implemented in software. HW sets BUSY bit.
        BlockErase64KB = 0xd8,

        /// Clears all bits to '1'.
        /// Must be implemented in software. HW sets BUSY bit.
        ChipErase = 0xc7,

        /// Alternative op code for ChipErase. HW sets BUSY bit.
        ChipErase2 = 0x60,

        /// Programs up to 256 bytes of memory.
        /// Must be implemented in software. HW sets BUSY bit.
        PageProgram = 0x02,

        ////////////////////////////////////////////////////////////
        // ID commands

        /// Retrieves JEDEC-ID as configured in jedec_id registers.
        /// Implemented in hardware.
        ReadJedec = 0x9f,

        /// Retrieves SFDP as configured in sfdp registers.
        /// Implemented in hardware.
        ReadSfdp = 0x5a,

        ////////////////////////////////////////////////////////////
        // Read commands

        /// Retrieves data. The behavior of this command depends on the selected
        /// mode.
        NormalRead = 0x03,

        /// Retrieves data. The behavior of this command depends on the selected
        /// mode. Fast read includes a 1 byte delay after retrieving the last
        /// bit of the addrees before the first bit of data is delivered.
        FastRead = 0x0b,

        /// Similar to FastReads but uses explicit 4 byte addressing.
        FastRead4B = 0x0c,

        /// Similar to FastRead with output on both MISO and MOSI.
        FastReadDualOutput = 0x3b,

        ////////////////////////////////////////////////////////////
        // Address mode commands

        /// Enable 4 byte address mode.
        /// Must be implemented in software.
        Enter4ByteAddressMode = 0xb7,

        /// Disable 4 byte address mode and revert to 3 byte address mode.
        /// Must be implemented in software.
        Exit4ByteAddressMode = 0xe9,
    }
}

impl<'a> OpCode {
    /// Returns true iff the OpCode requires an address.
    pub fn has_address(&self) -> bool {
        match self {
            Self::SectorErase => true,
            Self::BlockErase32KB => true,
            Self::BlockErase64KB => true,
            Self::PageProgram => true,
            Self::ReadSfdp => true,
            Self::NormalRead => true,
            Self::FastRead => true,
            Self::FastRead4B => true,
            Self::FastReadDualOutput => true,
            _ => false,
        }
    }

    /// Returns true iff the OpCode requires a dummy byte.
    pub fn has_dummy_byte(&self) -> bool {
        match self {
            Self::FastRead => true,
            Self::FastRead4B => true,
            Self::FastReadDualOutput => true,
            _ => false,
        }
    }

    /// Returns true iff the OpCode requires data.
    pub fn has_data(&self) -> bool {
        match self {
            Self::PageProgram => true,
            _ => false,
        }
    }

    /// Returns true iff the OpCode requires for the BUSY bit to clear.
    pub fn wait_busy_clear(&self) -> bool {
        match self {
            Self::WriteSuspend => true,
            Self::WriteResume => true,
            Self::SectorErase => true,
            Self::BlockErase32KB => true,
            Self::BlockErase64KB => true,
            Self::ChipErase => true,
            Self::ChipErase2 => true,
            Self::PageProgram => true,
            _ => false,
        }
    }
}

const DUMMY_BYTE_VALUE: u8 = 0xff;

/// Address in SPI flash protocol header
pub trait Address: BeInt + Into::<u32> {
    /// Convert from a u32.
    /// Note: This will become redundant once types from `ux` crate implement
    /// `core::convert::TryFrom`.
    fn try_from(val: u32) -> Result<Self, core::num::TryFromIntError>;
}

impl Address for ux::u24 {
    fn try_from(val: u32) -> Result<Self, core::num::TryFromIntError> {
        Ok(ux::u24::new(val))
    }
}

impl Address for u32 {
    fn try_from(val: u32) -> Result<Self, core::num::TryFromIntError> {
        Ok(val)
    }
}

/// A parsed SPI flash protocol header.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Header<AddrType> {
    /// The SPI op code
    pub opcode: OpCode,

    /// The address.
    ///
    /// Note that not all SPI op codes require an address.
    pub address: Option<AddrType>,
}

impl<'a, AddrType> Header<AddrType>
where AddrType: Address {
    /// Get the address as an Option<u32>
    pub fn get_address(&self) -> Option<u32> {
        self.address.map(|val| val.into())
    }

    /// Deserializes a `Header` from `r`.
    pub fn from_wire<R: Read<'a>>(mut r: R) -> Result<Self, FromWireError> {
        let opcode_u8 = r.read_be::<u8>()?;
        let opcode = OpCode::from_wire_value(opcode_u8).ok_or(FromWireError::OutOfRange)?;

        let address =
            if opcode.has_address() {
                Some(r.read_be::<AddrType>()?)
            } else {
                None
            };

        if opcode.has_dummy_byte() {
            // We don't actually care about the value, we just need to consume it.
            let _ = r.read_be::<u8>()?;
        }

        Ok(Self {
            opcode,
            address,
        })
    }

    /// Serializes `self` into `w`.
    pub fn to_wire<W: Write>(&self, mut w: W) -> Result<(), ToWireError> {
        w.write_be(self.opcode.to_wire_value())?;
        if self.opcode.has_address() {
            if ! self.address.is_some() {
                return Err(ToWireError::InvalidData)
            }
            w.write_be(self.address.unwrap())?;
        }
        if self.opcode.has_dummy_byte() {
            w.write_be(DUMMY_BYTE_VALUE)?;
        }

        Ok(())
    }
}

impl<'a> FromWire<'a> for Header<ux::u24> {
    fn from_wire<R: Read<'a>>(r: R) -> Result<Self, FromWireError> {
        Self::from_wire(r)
    }
}

impl ToWire for Header<ux::u24> {
    fn to_wire<W: Write>(&self, w: W) -> Result<(), ToWireError> {
        self.to_wire(w)
    }
}

impl<'a> FromWire<'a> for Header<u32> {
    fn from_wire<R: Read<'a>>(r: R) -> Result<Self, FromWireError> {
        Self::from_wire(r)
    }
}

impl ToWire for Header<u32> {
    fn to_wire<W: Write>(&self, w: W) -> Result<(), ToWireError> {
        self.to_wire(w)
    }
}

<|endoftext|>


<commit_before>use polynomial::Polynomial;
use nalgebra::{Vector2};
use std::cmp::{max};
use roots::find_roots_quartic;

//TODO: polymorphic in number of dimensions, and numeric type
//TODO: optimize away the unnecessary heap-allocation and other inefficiencies of Polynomial, and other pointless inefficiencies I introduced
//TODO replace f64 with a deterministic type (probably mpfr)
//note: the third time is not acceleration, but the coefficient of X squared, which is 2 times acceleration
pub type Coordinate = i64;
#[derive (Clone,)]
pub struct QuadraticTrajectory {
  data: Vector2 <Polynomial <Coordinate>>,
  time_scale_shift: i32,
  }
fn update_by (poly: &mut Polynomial <Coordinate> , time: Coordinate, time_scale_shift: i32) {
  let changed =&*poly + Polynomial::new (vec![((poly.data() [1]*time) >> time_scale_shift) + ((poly.data() [2]*time*time) >> (time_scale_shift*2)), ((poly.data() [2]*time*2) >> time_scale_shift), 0i64]);
  poly.clone_from (& changed);
  }
impl QuadraticTrajectory {
  pub fn new (time_scale_shift: i32, coordinates: [Coordinate; 6])->QuadraticTrajectory {QuadraticTrajectory {
  time_scale_shift:time_scale_shift,
  data: Vector2::new (
  Polynomial::new (
  vec![ coordinates [0], coordinates [2], coordinates [4]])
  , Polynomial::new (vec![coordinates [1], coordinates [3], coordinates [5]]
  ))}}

  pub fn update_by (&mut self, time: Coordinate) {
    update_by (&mut self.data [0], time, self.time_scale_shift);
    update_by (&mut self.data [1], time, self.time_scale_shift);
  }
  pub fn add_acceleration (&mut self, acceleration: Vector2 <Coordinate>) {
    let changed =&self.data [0]+ Polynomial::new (vec![0, 0, acceleration [0]]);
    self.data [0].clone_from (& changed);
    let changed =&self.data [1]+ Polynomial::new (vec![0, 0, acceleration [1]]);
    self.data [1].clone_from (& changed);
  }

  //direction == -1->"when the distance between the trajectories drops below the distance argument"
  //direction == 1->"when the distance between the trajectories exceeds the distance argument"
  pub fn approximately_when_distance_passes (
distance: Coordinate, direction: Coordinate,
first: (Coordinate, & QuadraticTrajectory), second: (Coordinate, & QuadraticTrajectory),)->Option <Coordinate> {
assert! (first.1.time_scale_shift == second.1.time_scale_shift, "we don't actually support interactions between trajectories with different scales");
let error =//distance can't be off by more than 3, so distance*distance can't be off by more than 3 + distance*6
distance*6 + 3;
let ratio = (1i64 << first.1.time_scale_shift) as f64;

    let base = max (first.0, second.0);
let mut third = first.1.clone(); third.update_by (base - first.0);
let mut more = second.1.clone(); more.update_by (base - second.0);
let displacement_function:Vector2 <Polynomial <Coordinate>> = third.data - more.data;
let distance_function = & displacement_function [0]*& displacement_function [0] + & displacement_function [1]*& displacement_function [1];
let roots = find_roots_quartic (
distance_function .data() [4] as f64/ratio.powi(4),
distance_function .data() [3] as f64/ratio.powi(3),
distance_function .data() [2] as f64/ratio.powi(2),
distance_function .data() [1] as f64/ratio.powi(1),
 (distance_function .data() [0] - (distance*distance + error*direction)) as f64);

for root in roots.as_ref() {
let result =root.ceil() as Coordinate;
if result >0 {
let mut checker = displacement_function.clone(); update_by (&mut checker [0], result,first.1.time_scale_shift); update_by (&mut checker [1], result,first.1.time_scale_shift);

let real_distance_squared = checker [0].data() [0]*checker [0].data() [0] + checker [1].data() [0]*checker [1].data() [0];

if (real_distance_squared - distance*distance)*direction >0 {
return Some (base + result);
}}}
 None
}
}
<commit_msg>Fixed bug<commit_after>use polynomial::Polynomial;
use nalgebra::{Vector2};
use std::cmp::{max};
use roots::find_roots_quartic;

//TODO: polymorphic in number of dimensions, and numeric type
//TODO: optimize away the unnecessary heap-allocation and other inefficiencies of Polynomial, and other pointless inefficiencies I introduced
//TODO replace f64 with a deterministic type (probably mpfr)
//note: the third time is not acceleration, but the coefficient of X squared, which is 2 times acceleration
pub type Coordinate = i64;
#[derive (Clone,)]
pub struct QuadraticTrajectory {
  data: Vector2 <Polynomial <Coordinate>>,
  time_scale_shift: i32,
  }
fn update_by (poly: &mut Polynomial <Coordinate> , time: Coordinate, time_scale_shift: i32) {
  let changed =&*poly + Polynomial::new (vec![((poly.data().get (1).cloned().unwrap_or (0)*time) >> time_scale_shift) + ((poly.data().get (2).cloned().unwrap_or (0)*time*time) >> (time_scale_shift*2)), ((poly.data().get (2).cloned().unwrap_or (0)*time*2) >> time_scale_shift), 0i64]);
  poly.clone_from (& changed);
  }
impl QuadraticTrajectory {
  pub fn new (time_scale_shift: i32, coordinates: [Coordinate; 6])->QuadraticTrajectory {QuadraticTrajectory {
  time_scale_shift:time_scale_shift,
  data: Vector2::new (
  Polynomial::new (
  vec![ coordinates [0], coordinates [2], coordinates [4]])
  , Polynomial::new (vec![coordinates [1], coordinates [3], coordinates [5]]
  ))}}

  pub fn update_by (&mut self, time: Coordinate) {
    update_by (&mut self.data [0], time, self.time_scale_shift);
    update_by (&mut self.data [1], time, self.time_scale_shift);
  }
  pub fn add_acceleration (&mut self, acceleration: Vector2 <Coordinate>) {
    let changed =&self.data [0]+ Polynomial::new (vec![0, 0, acceleration [0]]);
    self.data [0].clone_from (& changed);
    let changed =&self.data [1]+ Polynomial::new (vec![0, 0, acceleration [1]]);
    self.data [1].clone_from (& changed);
  }

  //direction == -1->"when the distance between the trajectories drops below the distance argument"
  //direction == 1->"when the distance between the trajectories exceeds the distance argument"
  pub fn approximately_when_distance_passes (
distance: Coordinate, direction: Coordinate,
first: (Coordinate, & QuadraticTrajectory), second: (Coordinate, & QuadraticTrajectory),)->Option <Coordinate> {
assert! (first.1.time_scale_shift == second.1.time_scale_shift, "we don't actually support interactions between trajectories with different scales");
let error =//distance can't be off by more than 3, so distance*distance can't be off by more than 3 + distance*6
distance*6 + 3;
let ratio = (1i64 << first.1.time_scale_shift) as f64;

    let base = max (first.0, second.0);
let mut third = first.1.clone(); third.update_by (base - first.0);
let mut more = second.1.clone(); more.update_by (base - second.0);
let displacement_function:Vector2 <Polynomial <Coordinate>> = third.data - more.data;
let distance_function = & displacement_function [0]*& displacement_function [0] + & displacement_function [1]*& displacement_function [1];
let roots = find_roots_quartic (
distance_function .data().get (4).cloned().unwrap_or (0) as f64/ratio.powi(4),
distance_function .data().get (3).cloned().unwrap_or (0) as f64/ratio.powi(3),
distance_function .data().get (2).cloned().unwrap_or (0) as f64/ratio.powi(2),
distance_function .data().get (1).cloned().unwrap_or (0) as f64/ratio.powi(1),
 (distance_function .data().get (0).cloned().unwrap_or (0) - (distance*distance + error*direction)) as f64);

for root in roots.as_ref() {
let result =root.ceil() as Coordinate;
if result >0 {
let mut checker = displacement_function.clone(); update_by (&mut checker [0], result,first.1.time_scale_shift); update_by (&mut checker [1], result,first.1.time_scale_shift);

let real_distance_squared = checker [0].data().get (0).cloned().unwrap_or (0)*checker [0].data().get (0).cloned().unwrap_or (0) + checker [1].data().get (0).cloned().unwrap_or (0)*checker [1].data().get (0).cloned().unwrap_or (0);

if (real_distance_squared - distance*distance)*direction >0 {
return Some (base + result);
}}}
 None
}
}
<|endoftext|>


<commit_before>use gfx;
use std::sync::mpsc;

#[derive(Debug)]
pub struct EncoderQueue<D: gfx::Device> {
  pub sender: mpsc::Sender<gfx::Encoder<D::Resources, D::CommandBuffer>>,
  pub receiver: mpsc::Receiver<gfx::Encoder<D::Resources, D::CommandBuffer>>,
}

pub struct DeviceRenderer<D: gfx::Device> {
  queue: EncoderQueue<D>,
}

impl<D: gfx::Device> DeviceRenderer<D> {
  pub fn new(buffers: Vec<D::CommandBuffer>) -> (DeviceRenderer<D>, EncoderQueue<D>) {
    let (a_send, b_recv) = mpsc::channel();
    let (b_send, a_recv) = mpsc::channel();

    for cb in buffers {
      let encoder = gfx::Encoder::from(cb);
      a_send
        .send(encoder)
        .expect("command buffers send failed");
    }

    (DeviceRenderer {
      queue: EncoderQueue {
        sender: a_send,
        receiver: a_recv,
      },
    },
     EncoderQueue {
       sender: b_send,
       receiver: b_recv,
     })
  }

  pub fn draw(&mut self, device: &mut D) -> bool {
    match self.queue.receiver.recv() {
      Ok(mut encoder) => {
        encoder.flush(device);
        match self.queue.sender.send(encoder) {
          Ok(_) => {
            true
          },
          Err(e) => {
            false
            panic!("Unable to send {}", e);
          }
        }
      }
      Err(e) => {
        println!("Unable to receive {}", e);
        false
      }
    }
  }
}
<commit_msg>Simplify draw function<commit_after>use gfx;
use std::sync::mpsc;

#[derive(Debug)]
pub struct EncoderQueue<D: gfx::Device> {
  pub sender: mpsc::Sender<gfx::Encoder<D::Resources, D::CommandBuffer>>,
  pub receiver: mpsc::Receiver<gfx::Encoder<D::Resources, D::CommandBuffer>>,
}

pub struct DeviceRenderer<D: gfx::Device> {
  queue: EncoderQueue<D>,
}

impl<D: gfx::Device> DeviceRenderer<D> {
  pub fn new(buffers: Vec<D::CommandBuffer>) -> (DeviceRenderer<D>, EncoderQueue<D>) {
    let (a_send, b_recv) = mpsc::channel();
    let (b_send, a_recv) = mpsc::channel();

    for cb in buffers {
      let encoder = gfx::Encoder::from(cb);
      a_send
        .send(encoder)
        .expect("command buffers send failed");
    }

    (DeviceRenderer {
      queue: EncoderQueue {
        sender: a_send,
        receiver: a_recv,
      },
    },
     EncoderQueue {
       sender: b_send,
       receiver: b_recv,
     })
  }

  pub fn draw(&mut self, device: &mut D) {
    match self.queue.receiver.recv() {
      Ok(mut encoder) => {
        encoder.flush(device);
        match self.queue.sender.send(encoder) {
          Ok(_) => {},
          Err(e) => {
            panic!("Unable to send {}", e);
          }
        }
      }
      Err(e) => {
        panic!("Unable to receive {}", e);
      }
    }
  }
}
<|endoftext|>


<commit_before>use godcoin::{prelude::*, script::*};
use std::{error::Error, num::ParseIntError};

#[derive(Clone, Debug)]
pub enum BuildError {
    EmptyScript,
    ScriptSizeOverflow,
    UnknownOp(String),
    MissingArgForOp(String),
    WifError(WifError),
    Other(String),
}

pub fn build(ops: &[String]) -> Result<Script, BuildError> {
    let mut builder = Builder::new();

    let mut iter = ops.iter();
    while let Some(op) = iter.next() {
        builder = match op.as_ref() {
            // Push value
            "OP_FALSE" => builder.try_push(OpFrame::False),
            "OP_TRUE" => builder.try_push(OpFrame::True),
            "OP_PUBKEY" => {
                let key = iter.next();
                if let Some(key) = key {
                    let key = PublicKey::from_wif(key).map_err(|e| BuildError::WifError(e))?;
                    builder.try_push(OpFrame::PubKey(key))
                } else {
                    return Err(BuildError::MissingArgForOp(op.to_owned()));
                }
            }
            // Stack manipulation
            "OP_NOT" => builder.try_push(OpFrame::OpNot),
            // Control
            "OP_IF" => builder.try_push(OpFrame::OpIf),
            "OP_ELSE" => builder.try_push(OpFrame::OpElse),
            "OP_ENDIF" => builder.try_push(OpFrame::OpEndIf),
            "OP_RETURN" => builder.try_push(OpFrame::OpReturn),
            // Crypto
            "OP_CHECKSIG" => builder.try_push(OpFrame::OpCheckSig),
            "OP_CHECKSIGFASTFAIL" => builder.try_push(OpFrame::OpCheckSigFastFail),
            "OP_CHECKMULTISIG" => {
                let threshold = iter
                    .next()
                    .ok_or_else(|| BuildError::MissingArgForOp(op.to_owned()))?
                    .parse()
                    .map_err(|e: ParseIntError| BuildError::Other(e.description().to_owned()))?;
                let key_count = iter
                    .next()
                    .ok_or_else(|| BuildError::MissingArgForOp(op.to_owned()))?
                    .parse()
                    .map_err(|e: ParseIntError| BuildError::Other(e.description().to_owned()))?;
                builder.try_push(OpFrame::OpCheckMultiSig(threshold, key_count))
            }
            "OP_CHECKMULTISIGFASTFAIL" => {
                let threshold = iter
                    .next()
                    .ok_or_else(|| BuildError::MissingArgForOp(op.to_owned()))?
                    .parse()
                    .map_err(|e: ParseIntError| BuildError::Other(e.description().to_owned()))?;
                let key_count = iter
                    .next()
                    .ok_or_else(|| BuildError::MissingArgForOp(op.to_owned()))?
                    .parse()
                    .map_err(|e: ParseIntError| BuildError::Other(e.description().to_owned()))?;
                builder.try_push(OpFrame::OpCheckMultiSigFastFail(threshold, key_count))
            }
            _ => return Err(BuildError::UnknownOp(op.to_owned())),
        }
        .ok_or(BuildError::ScriptSizeOverflow)?;
    }

    if !builder.as_ref().is_empty() {
        Ok(builder.build())
    } else {
        Err(BuildError::EmptyScript)
    }
}
<commit_msg>Remove redundant closure<commit_after>use godcoin::{prelude::*, script::*};
use std::{error::Error, num::ParseIntError};

#[derive(Clone, Debug)]
pub enum BuildError {
    EmptyScript,
    ScriptSizeOverflow,
    UnknownOp(String),
    MissingArgForOp(String),
    WifError(WifError),
    Other(String),
}

pub fn build(ops: &[String]) -> Result<Script, BuildError> {
    let mut builder = Builder::new();

    let mut iter = ops.iter();
    while let Some(op) = iter.next() {
        builder = match op.as_ref() {
            // Push value
            "OP_FALSE" => builder.try_push(OpFrame::False),
            "OP_TRUE" => builder.try_push(OpFrame::True),
            "OP_PUBKEY" => {
                let key = iter.next();
                if let Some(key) = key {
                    let key = PublicKey::from_wif(key).map_err(BuildError::WifError)?;
                    builder.try_push(OpFrame::PubKey(key))
                } else {
                    return Err(BuildError::MissingArgForOp(op.to_owned()));
                }
            }
            // Stack manipulation
            "OP_NOT" => builder.try_push(OpFrame::OpNot),
            // Control
            "OP_IF" => builder.try_push(OpFrame::OpIf),
            "OP_ELSE" => builder.try_push(OpFrame::OpElse),
            "OP_ENDIF" => builder.try_push(OpFrame::OpEndIf),
            "OP_RETURN" => builder.try_push(OpFrame::OpReturn),
            // Crypto
            "OP_CHECKSIG" => builder.try_push(OpFrame::OpCheckSig),
            "OP_CHECKSIGFASTFAIL" => builder.try_push(OpFrame::OpCheckSigFastFail),
            "OP_CHECKMULTISIG" => {
                let threshold = iter
                    .next()
                    .ok_or_else(|| BuildError::MissingArgForOp(op.to_owned()))?
                    .parse()
                    .map_err(|e: ParseIntError| BuildError::Other(e.description().to_owned()))?;
                let key_count = iter
                    .next()
                    .ok_or_else(|| BuildError::MissingArgForOp(op.to_owned()))?
                    .parse()
                    .map_err(|e: ParseIntError| BuildError::Other(e.description().to_owned()))?;
                builder.try_push(OpFrame::OpCheckMultiSig(threshold, key_count))
            }
            "OP_CHECKMULTISIGFASTFAIL" => {
                let threshold = iter
                    .next()
                    .ok_or_else(|| BuildError::MissingArgForOp(op.to_owned()))?
                    .parse()
                    .map_err(|e: ParseIntError| BuildError::Other(e.description().to_owned()))?;
                let key_count = iter
                    .next()
                    .ok_or_else(|| BuildError::MissingArgForOp(op.to_owned()))?
                    .parse()
                    .map_err(|e: ParseIntError| BuildError::Other(e.description().to_owned()))?;
                builder.try_push(OpFrame::OpCheckMultiSigFastFail(threshold, key_count))
            }
            _ => return Err(BuildError::UnknownOp(op.to_owned())),
        }
        .ok_or(BuildError::ScriptSizeOverflow)?;
    }

    if !builder.as_ref().is_empty() {
        Ok(builder.build())
    } else {
        Err(BuildError::EmptyScript)
    }
}
<|endoftext|>


<commit_before>use num_traits::ToPrimitive;
use num_bigint::BigInt;
use std::str::FromStr;
use std::cmp::max;

mod precision;
use self::precision::*;

pub mod asset_symbol;
pub use self::asset_symbol::*;

pub mod cmp;
pub use self::cmp::*;

pub mod error;
pub use self::error::*;

pub const MAX_STR_LEN: usize = 32;
pub const MAX_PRECISION: u8 = 8;

pub const EMPTY_GOLD: Asset = Asset {
    amount: 0,
    decimals: 0,
    symbol: AssetSymbol::GOLD
};

pub const EMPTY_SILVER: Asset = Asset {
    amount: 0,
    decimals: 0,
    symbol: AssetSymbol::SILVER
};

#[derive(Clone, Debug)]
pub struct Asset {
    pub amount: i64,
    pub decimals: u8,
    pub symbol: AssetSymbol
}

impl Asset {
    #[inline(always)]
    pub fn new(amount: i64, decimals: u8, symbol: AssetSymbol) -> Option<Asset> {
        if decimals > MAX_PRECISION { return None }
        Some(Asset {
            amount,
            decimals,
            symbol
        })
    }

    pub fn add(&self, other: &Self) -> Option<Self> {
        if self.symbol != other.symbol { return None }
        let decimals = max(self.decimals, other.decimals);
        let a = set_decimals_i64(self.amount, self.decimals, decimals)?;
        let b = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(Asset { amount: a.checked_add(b)?, decimals, symbol: self.symbol })
    }

    pub fn sub(&self, other: &Self) -> Option<Self> {
        if self.symbol != other.symbol { return None }
        let decimals = max(self.decimals, other.decimals);
        let a = set_decimals_i64(self.amount, self.decimals, decimals)?;
        let b = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(Asset { amount: a.checked_sub(b)?, decimals, symbol: self.symbol })
    }

    pub fn mul(&self, other: &Self, precision: u8) -> Option<Self> {
        if self.symbol != other.symbol || precision > MAX_PRECISION { return None }
        let decimals = self.decimals + other.decimals;

        let mul = i128::from(self.amount).checked_mul(i128::from(other.amount))?;
        let final_mul = set_decimals_i128(mul, decimals, precision)?;
        if final_mul > i128::from(::std::i64::MAX) { return None }
        Some(Asset {
            amount: final_mul as i64,
            decimals: precision,
            symbol: self.symbol
        })
    }

    pub fn div(&self, other: &Self, precision: u8) -> Option<Self> {
        if self.symbol != other.symbol
            || other.amount == 0
            || precision > MAX_PRECISION { return None }
        let decimals = max(max(self.decimals, other.decimals), precision);
        let a = set_decimals_i64(self.amount, self.decimals, decimals * 2)?;
        let b = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(Asset {
            amount: set_decimals_i64(a.checked_div(b)?, decimals, precision)?,
            decimals: precision,
            symbol: self.symbol
        })
    }

    pub fn pow(&self, num: u16, precision: u8) -> Option<Self> {
        if precision > MAX_PRECISION { return None }
        if num == 0 {
            return Some(Asset {
                amount: set_decimals_i64(1, 0, precision)?,
                decimals: precision,
                symbol: self.symbol
            })
        }
        let big_zero = BigInt::from(0);
        let big_one = BigInt::from(1);

        let decimals = u16::from(self.decimals).checked_mul(num)?;
        let mut res = BigInt::from(1);
        {
            let mut base = BigInt::from(self.amount);
            let mut exp = BigInt::from(num);
            loop {
                if &exp & &big_one == big_one { res = &res * &base; }
                exp >>= 1;
                if exp == big_zero { break };
                base = &base * &base;
            }
        }

        res = set_decimals_big(&res, decimals, u16::from(precision));
        Some(Asset {
            amount: res.to_i64()?,
            decimals: precision,
            symbol: self.symbol
        })
    }
}

impl ToString for Asset {
    fn to_string(&self) -> String {
        let mut s = self.amount.to_string();
        if self.decimals > 0 {
            let len = s.len();
            if len < self.decimals as usize {
                let start = if self.amount < 0 { 1 } else { 0 };
                let diff = self.decimals as usize - len + start;
                s.insert_str(start, "0.");
                s.insert_str(start + 2, &"0".repeat(diff));
            } else if len == self.decimals as usize {
                s.insert_str(0, "0.");
            } else {
                s.insert(len - (self.decimals as usize), '.');
            }
        }
        s.push(' ');
        s.push_str(self.symbol.as_str());
        s
    }
}

impl FromStr for Asset {
    type Err = AssetError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.len() > MAX_STR_LEN {
            return Err(AssetError { kind: AssetErrorKind::StrTooLarge })
        }
        let mut split = s.trim().splitn(2, ' ');

        let amount: i64;
        let mut decimals: u8 = 0;
        match split.next() {
            Some(x) => {
                if let Some(pos) = x.find('.') {
                    let len = x.len() - 1;
                    if pos > 0 { decimals = (len - pos) as u8; }
                    else { decimals = len as u8; }

                    if decimals > MAX_PRECISION {
                        return Err(AssetError { kind: AssetErrorKind::InvalidAmount })
                    }

                    amount = match x.replace('.', "").parse() {
                        Ok(x) => x,
                        Err(_) => return Err(AssetError { kind: AssetErrorKind::InvalidAmount })
                    }
                } else {
                    amount = match x.parse() {
                        Ok(x) => x,
                        Err(_) => return Err(AssetError { kind: AssetErrorKind::InvalidAmount })
                    }
                }
            },
            None => return Err(AssetError { kind: AssetErrorKind::InvalidFormat })
        };

        let symbol = match split.next() {
            Some(x) => {
                match AssetSymbol::parse_str(x) {
                    Some(x) => x,
                    None => return Err(AssetError { kind: AssetErrorKind::InvalidAssetType })
                }
            },
            None => return Err(AssetError { kind: AssetErrorKind::InvalidFormat })
        };

        Ok(Asset { amount, decimals, symbol })
    }
}

#[derive(Clone, Debug)]
pub struct Balance {
    pub gold: Asset,
    pub silver: Asset
}

macro_rules! agnostic_op {
    ($op:ident) => {
        impl Balance {
            #[inline]
            pub fn $op(&mut self, asset: &Asset) -> Option<&mut Balance> {
                match asset.symbol {
                    AssetSymbol::GOLD => {
                        self.gold = self.gold.$op(asset)?;
                    },
                    AssetSymbol::SILVER => {
                        self.silver = self.silver.$op(asset)?;
                    }
                }
                Some(self)
            }
        }
    };
}

agnostic_op!(add);
agnostic_op!(sub);

impl Default for Balance {
    fn default() -> Balance {
        Balance {
            gold: EMPTY_GOLD,
            silver: EMPTY_SILVER
        }
    }
}

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

    #[test]
    fn test_parse_valid_input() {
        let c = |asset: Asset, amount: &str, decimals: u8, symbol: AssetSymbol| {
            assert_eq!(asset.amount.to_string(), amount);
            assert_eq!(asset.decimals, decimals);
            assert_eq!(asset.symbol, symbol);
        };

        c(get_asset("1 GOLD"), "1", 0, AssetSymbol::GOLD);
        c(get_asset("1. GOLD"), "1", 0, AssetSymbol::GOLD);
        c(get_asset(".1 GOLD"), "1", 1, AssetSymbol::GOLD);
        c(get_asset("-.1 GOLD"), "-1", 1, AssetSymbol::GOLD);
        c(get_asset("0.1 GOLD"), "1", 1, AssetSymbol::GOLD);
        c(get_asset("1.0 SILVER"), "10", 1, AssetSymbol::SILVER);
        c(get_asset("0 SILVER"), "0", 0, AssetSymbol::SILVER);
        c(get_asset("-0.0 SILVER"), "0", 1, AssetSymbol::SILVER);
        c(get_asset("-1.0 SILVER"), "-10", 1, AssetSymbol::SILVER);
    }

    #[test]
    fn test_asset_to_str() {
        let c = |asset: Asset, s: &str| {
            assert_eq!(asset.to_string(), s);
        };
        c(get_asset("1.00001 GOLD"), "1.00001 GOLD");
        c(get_asset("0.00001 GOLD"), "0.00001 GOLD");
        c(get_asset("-0.00001 GOLD"), "-0.00001 GOLD");
        c(get_asset(".00001 GOLD"), "0.00001 GOLD");
        c(get_asset(".1 GOLD"), "0.1 GOLD");
        c(get_asset("1.0 GOLD"), "1.0 GOLD");
    }

    #[test]
    fn test_fail_parsing_invalid_input() {
        let c = |asset: &str, err: AssetErrorKind| {
            let e = Asset::from_str(asset).err().unwrap();
            assert_eq!(e.kind, err);
        };

        c("1e10 GOLD", AssetErrorKind::InvalidAmount);
        c("a100 GOLD", AssetErrorKind::InvalidAmount);
        c("100a GOLD", AssetErrorKind::InvalidAmount);

        c("1234567890123456789012345678 GOLD", AssetErrorKind::StrTooLarge);
        c("1", AssetErrorKind::InvalidFormat);

        c("1.0 GOLD a", AssetErrorKind::InvalidAssetType);
        c("1.0 gold", AssetErrorKind::InvalidAssetType);
    }

    #[test]
    fn test_set_precision() {
        let a = get_asset("1.5678 GOLD");
        assert_eq!(a.decimals, 4);
        assert_eq!(a.amount.to_string(), "15678");

        let a = a.mul(&get_asset("10000 GOLD"), 0).unwrap();
        assert_eq!(a.decimals, 0);
        assert_eq!(a.amount.to_string(), "15678");

        let a = a.div(&get_asset("100 GOLD"), 0).unwrap();
        assert_eq!(a.decimals, 0);
        assert_eq!(a.amount.to_string(), "156");

        let a = a.div(&get_asset("100 GOLD"), 2).unwrap();
        assert_eq!(a.decimals, 2);
        assert_eq!(a.amount.to_string(), "156");

        let a = a.div(&get_asset("100 GOLD"), 2).unwrap();
        assert_eq!(a.decimals, 2);
        assert_eq!(a.amount.to_string(), "1");
    }

    #[test]
    fn test_perform_arithmetic() {
        let c = |asset: &Asset, amount: &str| {
            assert_eq!(asset.to_string(), amount);
        };

        let a = get_asset("123.456 GOLD");
        c(&a.add(&get_asset("2.0 GOLD")).unwrap(), "125.456 GOLD");
        c(&a.add(&get_asset("-2.0 GOLD")).unwrap(), "121.456 GOLD");
        c(&a.add(&get_asset(".00000001 GOLD")).unwrap(), "123.45600001 GOLD");
        c(&a.sub(&get_asset("2.0 GOLD")).unwrap(), "121.456 GOLD");
        c(&a.sub(&get_asset("-2.0 GOLD")).unwrap(), "125.456 GOLD");
        c(&a.mul(&get_asset("100000.11111111 GOLD"), 8).unwrap(), "12345613.71733319 GOLD");
        c(&a.mul(&get_asset("-100000.11111111 GOLD"), 8).unwrap(), "-12345613.71733319 GOLD");
        c(&a.div(&get_asset("23 GOLD"), 3).unwrap(), "5.367 GOLD");
        c(&a.div(&get_asset("-23 GOLD"), 8).unwrap(), "-5.36765217 GOLD");
        c(&a.pow(2, 8).unwrap(), "15241.38393600 GOLD");
        c(&a.pow(3, 8).unwrap(), "1881640.29520281 GOLD");
        c(&a, "123.456 GOLD");

        c(&get_asset("1.0002 GOLD").pow(1000, 8).unwrap(), "1.22137833 GOLD");
        c(&get_asset("10 GOLD").div(&get_asset("2 GOLD"), 0).unwrap(), "5 GOLD");
        c(&get_asset("5 GOLD").div(&get_asset("10 GOLD"), 1).unwrap(), "0.5 GOLD");

        assert!(&a.div(&get_asset("0 GOLD"), 1).is_none());
    }

    #[test]
    fn test_invalid_arithmetic() {
        let a = &get_asset("10 GOLD");
        let b = &get_asset("10 SILVER");

        assert!(a.add(b).is_none());
        assert!(a.sub(b).is_none());
        assert!(a.div(b, 8).is_none());
        assert!(a.mul(b, 8).is_none());
    }

    fn get_asset(s: &str) -> Asset {
        Asset::from_str(s).unwrap()
    }
}
<commit_msg>Add must_use attribute to Balance operations<commit_after>use num_traits::ToPrimitive;
use num_bigint::BigInt;
use std::str::FromStr;
use std::cmp::max;

mod precision;
use self::precision::*;

pub mod asset_symbol;
pub use self::asset_symbol::*;

pub mod cmp;
pub use self::cmp::*;

pub mod error;
pub use self::error::*;

pub const MAX_STR_LEN: usize = 32;
pub const MAX_PRECISION: u8 = 8;

pub const EMPTY_GOLD: Asset = Asset {
    amount: 0,
    decimals: 0,
    symbol: AssetSymbol::GOLD
};

pub const EMPTY_SILVER: Asset = Asset {
    amount: 0,
    decimals: 0,
    symbol: AssetSymbol::SILVER
};

#[derive(Clone, Debug)]
pub struct Asset {
    pub amount: i64,
    pub decimals: u8,
    pub symbol: AssetSymbol
}

impl Asset {
    #[inline(always)]
    pub fn new(amount: i64, decimals: u8, symbol: AssetSymbol) -> Option<Asset> {
        if decimals > MAX_PRECISION { return None }
        Some(Asset {
            amount,
            decimals,
            symbol
        })
    }

    pub fn add(&self, other: &Self) -> Option<Self> {
        if self.symbol != other.symbol { return None }
        let decimals = max(self.decimals, other.decimals);
        let a = set_decimals_i64(self.amount, self.decimals, decimals)?;
        let b = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(Asset { amount: a.checked_add(b)?, decimals, symbol: self.symbol })
    }

    pub fn sub(&self, other: &Self) -> Option<Self> {
        if self.symbol != other.symbol { return None }
        let decimals = max(self.decimals, other.decimals);
        let a = set_decimals_i64(self.amount, self.decimals, decimals)?;
        let b = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(Asset { amount: a.checked_sub(b)?, decimals, symbol: self.symbol })
    }

    pub fn mul(&self, other: &Self, precision: u8) -> Option<Self> {
        if self.symbol != other.symbol || precision > MAX_PRECISION { return None }
        let decimals = self.decimals + other.decimals;

        let mul = i128::from(self.amount).checked_mul(i128::from(other.amount))?;
        let final_mul = set_decimals_i128(mul, decimals, precision)?;
        if final_mul > i128::from(::std::i64::MAX) { return None }
        Some(Asset {
            amount: final_mul as i64,
            decimals: precision,
            symbol: self.symbol
        })
    }

    pub fn div(&self, other: &Self, precision: u8) -> Option<Self> {
        if self.symbol != other.symbol
            || other.amount == 0
            || precision > MAX_PRECISION { return None }
        let decimals = max(max(self.decimals, other.decimals), precision);
        let a = set_decimals_i64(self.amount, self.decimals, decimals * 2)?;
        let b = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(Asset {
            amount: set_decimals_i64(a.checked_div(b)?, decimals, precision)?,
            decimals: precision,
            symbol: self.symbol
        })
    }

    pub fn pow(&self, num: u16, precision: u8) -> Option<Self> {
        if precision > MAX_PRECISION { return None }
        if num == 0 {
            return Some(Asset {
                amount: set_decimals_i64(1, 0, precision)?,
                decimals: precision,
                symbol: self.symbol
            })
        }
        let big_zero = BigInt::from(0);
        let big_one = BigInt::from(1);

        let decimals = u16::from(self.decimals).checked_mul(num)?;
        let mut res = BigInt::from(1);
        {
            let mut base = BigInt::from(self.amount);
            let mut exp = BigInt::from(num);
            loop {
                if &exp & &big_one == big_one { res = &res * &base; }
                exp >>= 1;
                if exp == big_zero { break };
                base = &base * &base;
            }
        }

        res = set_decimals_big(&res, decimals, u16::from(precision));
        Some(Asset {
            amount: res.to_i64()?,
            decimals: precision,
            symbol: self.symbol
        })
    }
}

impl ToString for Asset {
    fn to_string(&self) -> String {
        let mut s = self.amount.to_string();
        if self.decimals > 0 {
            let len = s.len();
            if len < self.decimals as usize {
                let start = if self.amount < 0 { 1 } else { 0 };
                let diff = self.decimals as usize - len + start;
                s.insert_str(start, "0.");
                s.insert_str(start + 2, &"0".repeat(diff));
            } else if len == self.decimals as usize {
                s.insert_str(0, "0.");
            } else {
                s.insert(len - (self.decimals as usize), '.');
            }
        }
        s.push(' ');
        s.push_str(self.symbol.as_str());
        s
    }
}

impl FromStr for Asset {
    type Err = AssetError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.len() > MAX_STR_LEN {
            return Err(AssetError { kind: AssetErrorKind::StrTooLarge })
        }
        let mut split = s.trim().splitn(2, ' ');

        let amount: i64;
        let mut decimals: u8 = 0;
        match split.next() {
            Some(x) => {
                if let Some(pos) = x.find('.') {
                    let len = x.len() - 1;
                    if pos > 0 { decimals = (len - pos) as u8; }
                    else { decimals = len as u8; }

                    if decimals > MAX_PRECISION {
                        return Err(AssetError { kind: AssetErrorKind::InvalidAmount })
                    }

                    amount = match x.replace('.', "").parse() {
                        Ok(x) => x,
                        Err(_) => return Err(AssetError { kind: AssetErrorKind::InvalidAmount })
                    }
                } else {
                    amount = match x.parse() {
                        Ok(x) => x,
                        Err(_) => return Err(AssetError { kind: AssetErrorKind::InvalidAmount })
                    }
                }
            },
            None => return Err(AssetError { kind: AssetErrorKind::InvalidFormat })
        };

        let symbol = match split.next() {
            Some(x) => {
                match AssetSymbol::parse_str(x) {
                    Some(x) => x,
                    None => return Err(AssetError { kind: AssetErrorKind::InvalidAssetType })
                }
            },
            None => return Err(AssetError { kind: AssetErrorKind::InvalidFormat })
        };

        Ok(Asset { amount, decimals, symbol })
    }
}

#[derive(Clone, Debug)]
pub struct Balance {
    pub gold: Asset,
    pub silver: Asset
}

macro_rules! agnostic_op {
    ($op:ident) => {
        impl Balance {
            #[inline]
            #[must_use = "operation can fail"]
            pub fn $op(&mut self, asset: &Asset) -> Option<&mut Balance> {
                match asset.symbol {
                    AssetSymbol::GOLD => {
                        self.gold = self.gold.$op(asset)?;
                    },
                    AssetSymbol::SILVER => {
                        self.silver = self.silver.$op(asset)?;
                    }
                }
                Some(self)
            }
        }
    };
}

agnostic_op!(add);
agnostic_op!(sub);

impl Default for Balance {
    fn default() -> Balance {
        Balance {
            gold: EMPTY_GOLD,
            silver: EMPTY_SILVER
        }
    }
}

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

    #[test]
    fn test_parse_valid_input() {
        let c = |asset: Asset, amount: &str, decimals: u8, symbol: AssetSymbol| {
            assert_eq!(asset.amount.to_string(), amount);
            assert_eq!(asset.decimals, decimals);
            assert_eq!(asset.symbol, symbol);
        };

        c(get_asset("1 GOLD"), "1", 0, AssetSymbol::GOLD);
        c(get_asset("1. GOLD"), "1", 0, AssetSymbol::GOLD);
        c(get_asset(".1 GOLD"), "1", 1, AssetSymbol::GOLD);
        c(get_asset("-.1 GOLD"), "-1", 1, AssetSymbol::GOLD);
        c(get_asset("0.1 GOLD"), "1", 1, AssetSymbol::GOLD);
        c(get_asset("1.0 SILVER"), "10", 1, AssetSymbol::SILVER);
        c(get_asset("0 SILVER"), "0", 0, AssetSymbol::SILVER);
        c(get_asset("-0.0 SILVER"), "0", 1, AssetSymbol::SILVER);
        c(get_asset("-1.0 SILVER"), "-10", 1, AssetSymbol::SILVER);
    }

    #[test]
    fn test_asset_to_str() {
        let c = |asset: Asset, s: &str| {
            assert_eq!(asset.to_string(), s);
        };
        c(get_asset("1.00001 GOLD"), "1.00001 GOLD");
        c(get_asset("0.00001 GOLD"), "0.00001 GOLD");
        c(get_asset("-0.00001 GOLD"), "-0.00001 GOLD");
        c(get_asset(".00001 GOLD"), "0.00001 GOLD");
        c(get_asset(".1 GOLD"), "0.1 GOLD");
        c(get_asset("1.0 GOLD"), "1.0 GOLD");
    }

    #[test]
    fn test_fail_parsing_invalid_input() {
        let c = |asset: &str, err: AssetErrorKind| {
            let e = Asset::from_str(asset).err().unwrap();
            assert_eq!(e.kind, err);
        };

        c("1e10 GOLD", AssetErrorKind::InvalidAmount);
        c("a100 GOLD", AssetErrorKind::InvalidAmount);
        c("100a GOLD", AssetErrorKind::InvalidAmount);

        c("1234567890123456789012345678 GOLD", AssetErrorKind::StrTooLarge);
        c("1", AssetErrorKind::InvalidFormat);

        c("1.0 GOLD a", AssetErrorKind::InvalidAssetType);
        c("1.0 gold", AssetErrorKind::InvalidAssetType);
    }

    #[test]
    fn test_set_precision() {
        let a = get_asset("1.5678 GOLD");
        assert_eq!(a.decimals, 4);
        assert_eq!(a.amount.to_string(), "15678");

        let a = a.mul(&get_asset("10000 GOLD"), 0).unwrap();
        assert_eq!(a.decimals, 0);
        assert_eq!(a.amount.to_string(), "15678");

        let a = a.div(&get_asset("100 GOLD"), 0).unwrap();
        assert_eq!(a.decimals, 0);
        assert_eq!(a.amount.to_string(), "156");

        let a = a.div(&get_asset("100 GOLD"), 2).unwrap();
        assert_eq!(a.decimals, 2);
        assert_eq!(a.amount.to_string(), "156");

        let a = a.div(&get_asset("100 GOLD"), 2).unwrap();
        assert_eq!(a.decimals, 2);
        assert_eq!(a.amount.to_string(), "1");
    }

    #[test]
    fn test_perform_arithmetic() {
        let c = |asset: &Asset, amount: &str| {
            assert_eq!(asset.to_string(), amount);
        };

        let a = get_asset("123.456 GOLD");
        c(&a.add(&get_asset("2.0 GOLD")).unwrap(), "125.456 GOLD");
        c(&a.add(&get_asset("-2.0 GOLD")).unwrap(), "121.456 GOLD");
        c(&a.add(&get_asset(".00000001 GOLD")).unwrap(), "123.45600001 GOLD");
        c(&a.sub(&get_asset("2.0 GOLD")).unwrap(), "121.456 GOLD");
        c(&a.sub(&get_asset("-2.0 GOLD")).unwrap(), "125.456 GOLD");
        c(&a.mul(&get_asset("100000.11111111 GOLD"), 8).unwrap(), "12345613.71733319 GOLD");
        c(&a.mul(&get_asset("-100000.11111111 GOLD"), 8).unwrap(), "-12345613.71733319 GOLD");
        c(&a.div(&get_asset("23 GOLD"), 3).unwrap(), "5.367 GOLD");
        c(&a.div(&get_asset("-23 GOLD"), 8).unwrap(), "-5.36765217 GOLD");
        c(&a.pow(2, 8).unwrap(), "15241.38393600 GOLD");
        c(&a.pow(3, 8).unwrap(), "1881640.29520281 GOLD");
        c(&a, "123.456 GOLD");

        c(&get_asset("1.0002 GOLD").pow(1000, 8).unwrap(), "1.22137833 GOLD");
        c(&get_asset("10 GOLD").div(&get_asset("2 GOLD"), 0).unwrap(), "5 GOLD");
        c(&get_asset("5 GOLD").div(&get_asset("10 GOLD"), 1).unwrap(), "0.5 GOLD");

        assert!(&a.div(&get_asset("0 GOLD"), 1).is_none());
    }

    #[test]
    fn test_invalid_arithmetic() {
        let a = &get_asset("10 GOLD");
        let b = &get_asset("10 SILVER");

        assert!(a.add(b).is_none());
        assert!(a.sub(b).is_none());
        assert!(a.div(b, 8).is_none());
        assert!(a.mul(b, 8).is_none());
    }

    fn get_asset(s: &str) -> Asset {
        Asset::from_str(s).unwrap()
    }
}
<|endoftext|>