mint_cli/layout/
conversions.rs1use super::entry::ScalarType;
2use super::error::LayoutError;
3use super::settings::{EndianBytes, Endianness};
4use super::value::DataValue;
5
6macro_rules! impl_try_from_data_value {
7 ($($t:ty),* $(,)?) => {$(
8 impl TryFrom<&DataValue> for $t {
9 type Error = LayoutError;
10 fn try_from(value: &DataValue) -> Result<Self, LayoutError> {
11 match value {
12 DataValue::Bool(val) => {
13 let n: u8 = if *val { 1 } else { 0 };
14 Ok(n as $t)
15 }
16 DataValue::U64(val) => Ok(*val as $t),
17 DataValue::I64(val) => Ok(*val as $t),
18 DataValue::F64(val) => Ok(*val as $t),
19 DataValue::Str(_) => {
20 return Err(LayoutError::DataValueExportFailed(
21 "Cannot convert string to scalar type.".to_string(),
22 ));
23 }
24 }
25 }
26 }
27 )* }; }
28
29impl_try_from_data_value!(u8, u16, u32, u64, i8, i16, i32, i64, i128, f32, f64);
30
31pub trait TryFromStrict<T>: Sized {
32 fn try_from_strict(value: T) -> Result<Self, LayoutError>;
33}
34
35macro_rules! err {
36 ($msg:expr) => {
37 LayoutError::DataValueExportFailed($msg.to_string())
38 };
39}
40
41macro_rules! impl_try_from_strict_unsigned {
42 ($($t:ty),* $(,)?) => {$(
43 impl TryFromStrict<&DataValue> for $t {
44 fn try_from_strict(value: &DataValue) -> Result<Self, LayoutError> {
45 match value {
46 DataValue::U64(v) => <Self as TryFrom<u64>>::try_from(*v)
47 .map_err(|_| err!(format!("u64 value {} out of range for {}", v, stringify!($t)))),
48 DataValue::I64(v) => {
49 if *v < 0 { return Err(err!("negative integer cannot convert to unsigned in strict mode")); }
50 <Self as TryFrom<u64>>::try_from(*v as u64)
51 .map_err(|_| err!(format!("i64 value {} out of range for {}", v, stringify!($t))))
52 }
53 DataValue::F64(v) => {
54 if !v.is_finite() { return Err(err!("non-finite float cannot convert to integer in strict mode")); }
55 if v.fract() != 0.0 { return Err(err!("float to integer conversion not allowed unless value is an exact integer")); }
56 if *v < 0.0 || *v > (<$t>::MAX as f64) { return Err(err!(format!("float value {} out of range for {}", v, stringify!($t)))); }
57 Ok(*v as $t)
58 }
59 DataValue::Bool(b) => {
60 let n: u8 = if *b { 1 } else { 0 };
61 Ok(n as $t)
62 }
63 DataValue::Str(_) => Err(err!("Cannot convert string to scalar type.")),
64 }
65 }
66 }
67 )*};
68}
69
70macro_rules! impl_try_from_strict_signed {
71 ($($t:ty),* $(,)?) => {$(
72 impl TryFromStrict<&DataValue> for $t {
73 fn try_from_strict(value: &DataValue) -> Result<Self, LayoutError> {
74 match value {
75 DataValue::U64(v) => {
76 <Self as TryFrom<i128>>::try_from(*v as i128)
77 .map_err(|_| err!(format!("u64 value {} out of range for {}", v, stringify!($t))))
78 }
79 DataValue::I64(v) => <Self as TryFrom<i64>>::try_from(*v)
80 .map_err(|_| err!(format!("i64 value {} out of range for {}", v, stringify!($t)))),
81 DataValue::F64(v) => {
82 if !v.is_finite() { return Err(err!("non-finite float cannot convert to integer in strict mode")); }
83 if v.fract() != 0.0 { return Err(err!("float to integer conversion not allowed unless value is an exact integer")); }
84 if *v < (<$t>::MIN as f64) || *v > (<$t>::MAX as f64) { return Err(err!(format!("float value {} out of range for {}", v, stringify!($t)))); }
85 Ok(*v as $t)
86 }
87 DataValue::Bool(b) => {
88 let n: u8 = if *b { 1 } else { 0 };
89 Ok(n as $t)
90 }
91 DataValue::Str(_) => Err(err!("Cannot convert string to scalar type.")),
92 }
93 }
94 }
95 )*};
96}
97
98macro_rules! impl_try_from_strict_float_targets {
99 ($t:ty) => {
100 impl TryFromStrict<&DataValue> for $t {
101 fn try_from_strict(value: &DataValue) -> Result<Self, LayoutError> {
102 match value {
103 DataValue::F64(v) => {
104 if !v.is_finite() {
105 return Err(err!("non-finite float not allowed in strict mode"));
106 }
107 let out = *v as $t;
108 if out.is_finite() {
109 Ok(out)
110 } else {
111 Err(err!(format!(
112 "float value {} out of range for {}",
113 v,
114 stringify!($t)
115 )))
116 }
117 }
118 DataValue::U64(v) => {
119 let out = (*v as $t);
120 if !out.is_finite() {
121 return Err(err!("integer to float produced non-finite value"));
122 }
123 if (out as u64) == *v {
125 Ok(out)
126 } else {
127 Err(err!(
128 "lossy integer to float conversion not allowed in strict mode"
129 ))
130 }
131 }
132 DataValue::I64(v) => {
133 let out = (*v as $t);
134 if !out.is_finite() {
135 return Err(err!("integer to float produced non-finite value"));
136 }
137 if (out as i64) == *v {
138 Ok(out)
139 } else {
140 Err(err!(
141 "lossy integer to float conversion not allowed in strict mode"
142 ))
143 }
144 }
145 DataValue::Bool(b) => {
146 let out: $t = if *b { 1.0 } else { 0.0 };
147 Ok(out)
148 }
149 DataValue::Str(_) => Err(err!("Cannot convert string to scalar type.")),
150 }
151 }
152 }
153 };
154}
155
156impl_try_from_strict_unsigned!(u8, u16, u32, u64);
157impl_try_from_strict_signed!(i8, i16, i32, i64, i128);
158impl_try_from_strict_float_targets!(f32);
159impl TryFromStrict<&DataValue> for f64 {
160 fn try_from_strict(value: &DataValue) -> Result<Self, LayoutError> {
161 match value {
162 DataValue::F64(v) => Ok(*v),
163 DataValue::U64(v) => {
164 let out = *v as f64;
165 if (out as u64) == *v {
166 Ok(out)
167 } else {
168 Err(err!(
169 "lossy integer to float conversion not allowed in strict mode"
170 ))
171 }
172 }
173 DataValue::I64(v) => {
174 let out = *v as f64;
175 if (out as i64) == *v {
176 Ok(out)
177 } else {
178 Err(err!(
179 "lossy integer to float conversion not allowed in strict mode"
180 ))
181 }
182 }
183 DataValue::Bool(b) => {
184 let out = if *b { 1.0 } else { 0.0 };
185 Ok(out)
186 }
187 DataValue::Str(_) => Err(err!("Cannot convert string to scalar type.")),
188 }
189 }
190}
191
192pub fn clamp_bitfield_value(
198 value: &DataValue,
199 bits: usize,
200 signed: bool,
201 strict: bool,
202) -> Result<i128, LayoutError> {
203 let raw: i128 = if strict {
204 i128::try_from_strict(value)?
205 } else {
206 i128::try_from(value)?
207 };
208
209 let (min, max) = if signed {
210 let half = 1i128 << (bits - 1);
211 (-half, half - 1)
212 } else {
213 (0, (1i128 << bits) - 1)
214 };
215
216 if strict && (raw < min || raw > max) {
217 return Err(LayoutError::BitfieldOutOfRange {
218 value: raw,
219 bits,
220 signedness: if signed { "signed" } else { "unsigned" },
221 min,
222 max,
223 });
224 }
225 Ok(raw.clamp(min, max))
226}
227
228pub fn convert_value_to_bytes(
229 value: &DataValue,
230 scalar_type: ScalarType,
231 endianness: &Endianness,
232 strict: bool,
233) -> Result<Vec<u8>, LayoutError> {
234 macro_rules! to_bytes {
235 ($t:ty) => {{
236 let val: $t = if strict {
237 <$t as TryFromStrict<&DataValue>>::try_from_strict(value)?
238 } else {
239 <$t as TryFrom<&DataValue>>::try_from(value)?
240 };
241 Ok(val.to_endian_bytes(endianness))
242 }};
243 }
244
245 match scalar_type {
246 ScalarType::U8 => to_bytes!(u8),
247 ScalarType::I8 => to_bytes!(i8),
248 ScalarType::U16 => to_bytes!(u16),
249 ScalarType::I16 => to_bytes!(i16),
250 ScalarType::U32 => to_bytes!(u32),
251 ScalarType::I32 => to_bytes!(i32),
252 ScalarType::U64 => to_bytes!(u64),
253 ScalarType::I64 => to_bytes!(i64),
254 ScalarType::F32 => to_bytes!(f32),
255 ScalarType::F64 => to_bytes!(f64),
256 }
257}