1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//! Types to help constrain inputs to functions to only what is used.
use std::ops::{
    Deref,
    DerefMut,
};

use fuel_asm::Word;
use fuel_types::ContractId;

use crate::{
    consts::MEM_SIZE,
    prelude::{
        MemoryRange,
        RuntimeError,
    },
};

pub mod reg_key;

/// A range of memory that has been checked that it fits into the VM memory.
#[derive(Clone)]
// TODO: Replace `LEN` constant with a generic object that implements some trait that
// knows  the static size of the generic.
pub struct CheckedMemConstLen<const LEN: usize>(MemoryRange);

/// A range of memory that has been checked that it fits into the VM memory.
/// This range can be used to read a value of type `T` from memory.
#[derive(Clone)]
// TODO: Merge this type with `CheckedMemConstLen`.
pub struct CheckedMemValue<T>(MemoryRange, core::marker::PhantomData<T>);

impl<T> CheckedMemValue<T> {
    /// Create a new const sized memory range.
    pub fn new<const SIZE: usize>(address: Word) -> Result<Self, RuntimeError> {
        Ok(Self(
            MemoryRange::new_const::<_, SIZE>(address)?,
            core::marker::PhantomData,
        ))
    }

    /// Try to read a value of type `T` from memory.
    pub fn try_from(self, memory: &[u8; MEM_SIZE]) -> Result<T, RuntimeError>
    where
        T: for<'a> TryFrom<&'a [u8]>,
        RuntimeError: for<'a> From<<T as TryFrom<&'a [u8]>>::Error>,
    {
        Ok(T::try_from(&memory[self.0.usizes()])?)
    }

    /// The start of the range.
    pub fn start(&self) -> usize {
        self.0.start
    }

    /// The end of the range.
    pub fn end(&self) -> usize {
        self.0.end
    }

    #[cfg(test)]
    /// Inspect a value of type `T` from memory.
    pub fn inspect(self, memory: &[u8; MEM_SIZE]) -> T
    where
        T: std::io::Write + Default,
    {
        let mut t = T::default();
        t.write_all(&memory[self.0.usizes()]).unwrap();
        t
    }
}

impl<const LEN: usize> CheckedMemConstLen<LEN> {
    /// Create a new const sized memory range.
    pub fn new(address: Word) -> Result<Self, RuntimeError> {
        Ok(Self(MemoryRange::new_const::<_, LEN>(address)?))
    }

    /// Get the memory slice for this range.
    pub fn read(self, memory: &[u8; MEM_SIZE]) -> &[u8; LEN] {
        (&memory[self.0.usizes()]).try_into().expect(
            "This is always correct as the address and LEN are checked on construction.",
        )
    }

    /// Get the mutable memory slice for this range.
    pub fn write(self, memory: &mut [u8; MEM_SIZE]) -> &mut [u8; LEN] {
        (&mut memory[self.0.usizes()]).try_into().expect(
            "This is always correct as the address and LEN are checked on construction.",
        )
    }
}

/// Location of an instructing collected during runtime
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct InstructionLocation {
    /// Context, i.e. current contract. None if running a script.
    pub context: Option<ContractId>,
    /// Offset from the IS register
    pub offset: u64,
}

impl<const LEN: usize> Deref for CheckedMemConstLen<LEN> {
    type Target = MemoryRange;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<const LEN: usize> DerefMut for CheckedMemConstLen<LEN> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}