Skip to main content

vyre_reference/value/
mod.rs

1//! Runtime values accepted and returned by the core reference interpreter.
2
3/// A concrete value passed into or returned from the reference interpreter.
4#[non_exhaustive]
5#[derive(Debug, Clone)]
6pub enum Value {
7    /// Unsigned 32-bit integer.
8    U32(u32),
9    /// Signed 32-bit integer.
10    I32(i32),
11    /// Unsigned 64-bit integer.
12    U64(u64),
13    /// Boolean value.
14    Bool(bool),
15    /// Raw little-endian storage bytes.
16    Bytes(Vec<u8>),
17    /// Floating-point value represented with stable host bits.
18    Float(f64),
19    /// Fixed-size array of values.
20    Array(Vec<Value>),
21}
22
23impl PartialEq for Value {
24    fn eq(&self, other: &Self) -> bool {
25        match (self, other) {
26            (Self::U32(a), Self::U32(b)) => a == b,
27            (Self::I32(a), Self::I32(b)) => a == b,
28            (Self::U64(a), Self::U64(b)) => a == b,
29            (Self::Bool(a), Self::Bool(b)) => a == b,
30            (Self::Bytes(a), Self::Bytes(b)) => a == b,
31            (Self::Float(a), Self::Float(b)) => a.to_bits() == b.to_bits(),
32            (Self::Array(a), Self::Array(b)) => a == b,
33            _ => false,
34        }
35    }
36}
37
38impl Eq for Value {}
39
40impl Value {
41    /// Interpret the value using the IR truth convention.
42    #[must_use]
43    pub fn truthy(&self) -> bool {
44        match self {
45            Self::Array(values) => !values.is_empty(),
46            Self::Float(value) => value.to_bits() != 0,
47            _ => self.try_as_u32().unwrap_or(1) != 0,
48        }
49    }
50
51    /// Return this value as little-endian bytes for buffer initialization.
52    #[must_use]
53    pub fn to_bytes(&self) -> Vec<u8> {
54        match self {
55            Self::U32(value) => value.to_le_bytes().to_vec(),
56            Self::I32(value) => value.to_le_bytes().to_vec(),
57            Self::U64(value) => value.to_le_bytes().to_vec(),
58            Self::Bool(value) => u32::from(*value).to_le_bytes().to_vec(),
59            Self::Bytes(bytes) => bytes.clone(),
60            Self::Float(value) => value.to_le_bytes().to_vec(),
61            Self::Array(values) => values.iter().flat_map(Self::to_bytes).collect(),
62        }
63    }
64
65    /// Return this value encoded at the declared input width.
66    #[must_use]
67    pub fn to_bytes_width(&self, declared_width: usize) -> Vec<u8> {
68        let mut bytes = self.to_bytes();
69        if declared_width == 0 {
70            return bytes;
71        }
72        bytes.resize(declared_width, 0);
73        bytes.truncate(declared_width);
74        bytes
75    }
76
77    /// Try to interpret the value as the IR's scalar `u32` word.
78    #[must_use]
79    pub fn try_as_u32(&self) -> Option<u32> {
80        match self {
81            Self::U32(value) => Some(*value),
82            Self::I32(value) => u32::try_from(*value).ok(),
83            Self::U64(value) => u32::try_from(*value).ok(),
84            Self::Bool(value) => Some(u32::from(*value)),
85            Self::Bytes(bytes) => (bytes.len() <= 4).then(|| read_u32_prefix(bytes)),
86            Self::Float(value) => Some(*value as u32),
87            Self::Array(_) => None,
88        }
89    }
90
91    /// Interpret the value as the IR's scalar `u32` word.
92    #[must_use]
93    pub fn as_u32(&self) -> u32 {
94        self.try_as_u32().unwrap_or(0)
95    }
96
97    /// Try to interpret the value as a full `u64`.
98    #[must_use]
99    pub fn try_as_u64(&self) -> Option<u64> {
100        match self {
101            Self::U32(value) => Some(u64::from(*value)),
102            Self::I32(value) => u64::try_from(*value).ok(),
103            Self::U64(value) => Some(*value),
104            Self::Bool(value) => Some(u64::from(*value)),
105            Self::Bytes(bytes) => (bytes.len() <= 8).then(|| read_u64_prefix(bytes)),
106            Self::Float(value) => Some(*value as u64),
107            Self::Array(_) => None,
108        }
109    }
110
111    /// Interpret the value as a full `u64`.
112    #[must_use]
113    pub fn as_u64(&self) -> u64 {
114        self.try_as_u64().unwrap_or(0)
115    }
116
117    /// Return the full value payload as little-endian bytes.
118    #[must_use]
119    pub fn wide_bytes(&self) -> Vec<u8> {
120        self.to_bytes()
121    }
122
123    /// Create a zero value for the given data type.
124    #[must_use]
125    pub fn zero_for(ty: vyre::ir::DataType) -> Self {
126        Self::try_zero_for(ty).unwrap_or_else(|| Self::Bytes(Vec::new()))
127    }
128
129    /// Try to create a zero value for the given data type.
130    #[must_use]
131    pub fn try_zero_for(ty: vyre::ir::DataType) -> Option<Self> {
132        match ty {
133            vyre::ir::DataType::U32 => Some(Self::U32(0)),
134            vyre::ir::DataType::I32 => Some(Self::I32(0)),
135            vyre::ir::DataType::U64 => Some(Self::U64(0)),
136            vyre::ir::DataType::Bool => Some(Self::Bool(false)),
137            vyre::ir::DataType::Bytes => Some(Self::Bytes(Vec::new())),
138            vyre::ir::DataType::Vec2U32 => Some(Self::Bytes(vec![0; 8])),
139            vyre::ir::DataType::Vec4U32 => Some(Self::Bytes(vec![0; 16])),
140            _ => None,
141        }
142    }
143
144    /// Create a value from element bytes for the given data type.
145    ///
146    /// # Errors
147    ///
148    /// Returns an error if the byte slice is too short for the declared type.
149    pub fn from_element_bytes(ty: vyre::ir::DataType, bytes: &[u8]) -> Result<Self, String> {
150        match ty {
151            vyre::ir::DataType::U32 => {
152                if bytes.len() < 4 {
153                    return Err("u32 requires 4 bytes".to_string());
154                }
155                Ok(Self::U32(u32::from_le_bytes([
156                    bytes[0], bytes[1], bytes[2], bytes[3],
157                ])))
158            }
159            vyre::ir::DataType::I32 => {
160                if bytes.len() < 4 {
161                    return Err("i32 requires 4 bytes".to_string());
162                }
163                Ok(Self::I32(i32::from_le_bytes([
164                    bytes[0], bytes[1], bytes[2], bytes[3],
165                ])))
166            }
167            vyre::ir::DataType::U64 => {
168                if bytes.len() < 8 {
169                    return Err("u64 requires 8 bytes".to_string());
170                }
171                Ok(Self::U64(u64::from_le_bytes([
172                    bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
173                ])))
174            }
175            vyre::ir::DataType::Bool => {
176                if bytes.len() < 4 {
177                    return Err("bool requires 4 bytes".to_string());
178                }
179                Ok(Self::Bool(
180                    u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) != 0,
181                ))
182            }
183            vyre::ir::DataType::Vec2U32 => {
184                if bytes.len() < 8 {
185                    return Err("vec2u32 requires 8 bytes".to_string());
186                }
187                Ok(Self::Bytes(bytes[..8].to_vec()))
188            }
189            vyre::ir::DataType::Vec4U32 => {
190                if bytes.len() < 16 {
191                    return Err("vec4u32 requires 16 bytes".to_string());
192                }
193                Ok(Self::Bytes(bytes[..16].to_vec()))
194            }
195            vyre::ir::DataType::F32 => {
196                if bytes.len() < 4 {
197                    return Err("f32 requires 4 bytes".to_string());
198                }
199                Ok(Self::Float(f64::from(f32::from_le_bytes([
200                    bytes[0], bytes[1], bytes[2], bytes[3],
201                ]))))
202            }
203            vyre::ir::DataType::Bytes => Ok(Self::Bytes(bytes.to_vec())),
204            _ => Ok(Self::Bytes(bytes.to_vec())),
205        }
206    }
207}
208
209fn read_u32_prefix(bytes: &[u8]) -> u32 {
210    let mut padded = [0u8; 4];
211    let len = bytes.len().min(4);
212    padded[..len].copy_from_slice(&bytes[..len]);
213    u32::from_le_bytes(padded)
214}
215
216fn read_u64_prefix(bytes: &[u8]) -> u64 {
217    let mut padded = [0u8; 8];
218    let len = bytes.len().min(8);
219    padded[..len].copy_from_slice(&bytes[..len]);
220    u64::from_le_bytes(padded)
221}