donet_core/
dcnumeric.rs

1/*
2    This file is part of Donet.
3
4    Copyright © 2024 Max Rodriguez <[email protected]>
5
6    Donet is free software; you can redistribute it and/or modify
7    it under the terms of the GNU Affero General Public License,
8    as published by the Free Software Foundation, either version 3
9    of the License, or (at your option) any later version.
10
11    Donet is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14    GNU Affero General Public License for more details.
15
16    You should have received a copy of the GNU Affero General Public
17    License along with Donet. If not, see <https://www.gnu.org/licenses/>.
18*/
19
20//! Structure representing data types supported in the DC
21//! language and enforcing numeric limits through constraints.
22
23use crate::datagram::datagram::*;
24use crate::datagram::iterator::*;
25use crate::dctype::*;
26use crate::hashgen::*;
27use std::mem::{discriminant, size_of};
28
29/// Numeric Range structs are used to represent a range of signed/unsigned
30/// integers or floating point numbers. Used for enforcing numeric limits
31/// within constraints of array, string, or blob sized types.
32#[derive(Clone)]
33pub struct DCNumericRange {
34    pub min: DCNumber,
35    pub max: DCNumber,
36}
37
38impl From<std::ops::Range<i64>> for DCNumericRange {
39    fn from(value: std::ops::Range<i64>) -> Self {
40        Self {
41            min: DCNumber::Integer(value.start),
42            max: DCNumber::Integer(value.end),
43        }
44    }
45}
46
47impl From<std::ops::Range<u64>> for DCNumericRange {
48    fn from(value: std::ops::Range<u64>) -> Self {
49        Self {
50            min: DCNumber::UnsignedInteger(value.start),
51            max: DCNumber::UnsignedInteger(value.end),
52        }
53    }
54}
55
56impl From<std::ops::Range<f64>> for DCNumericRange {
57    fn from(value: std::ops::Range<f64>) -> Self {
58        Self {
59            min: DCNumber::FloatingPoint(value.start),
60            max: DCNumber::FloatingPoint(value.end),
61        }
62    }
63}
64
65impl DCNumericRange {
66    pub fn contains(&self, num: DCNumber) -> bool {
67        // Check that `num` is of the same data type as this numeric range.
68        if discriminant(&self.min) == discriminant(&num) {
69            return false;
70        }
71
72        match self.min {
73            DCNumber::Integer(min) => {
74                let num = match num {
75                    DCNumber::Integer(i) => i,
76                    _ => panic!("Check above makes this panic unreachable."),
77                };
78
79                let max = match self.max {
80                    DCNumber::Integer(i) => i,
81                    _ => panic!("Check above makes this panic unreachable."),
82                };
83
84                min <= num && num <= max
85            }
86            DCNumber::UnsignedInteger(min) => {
87                let num = match num {
88                    DCNumber::UnsignedInteger(i) => i,
89                    _ => panic!("Check above makes this panic unreachable."),
90                };
91
92                let max = match self.max {
93                    DCNumber::UnsignedInteger(i) => i,
94                    _ => panic!("Check above makes this panic unreachable."),
95                };
96
97                min <= num && num <= max
98            }
99            DCNumber::FloatingPoint(min) => {
100                let num = match num {
101                    DCNumber::FloatingPoint(i) => i,
102                    _ => panic!("Check above makes this panic unreachable."),
103                };
104
105                let max = match self.max {
106                    DCNumber::FloatingPoint(i) => i,
107                    _ => panic!("Check above makes this panic unreachable."),
108                };
109
110                min <= num && num <= max
111            }
112        }
113    }
114}
115
116pub struct DCNumericType {
117    base_type: DCTypeDefinition,
118    divisor: u16,
119    /// These are the original range and modulus values from the file, unscaled by the divisor.
120    orig_modulus: f64,
121    orig_range: Option<DCNumericRange>,
122    /// These are the range and modulus values after scaling by the divisor.
123    modulus: f64,
124    range: Option<DCNumericRange>,
125    /// Specific to Donet's DC language
126    explicit_cast: Option<DCTypeDefinition>,
127}
128
129impl From<DCTypeEnum> for DCNumericType {
130    fn from(value: DCTypeEnum) -> Self {
131        Self {
132            base_type: {
133                let mut parent_struct = DCTypeDefinition::from(value);
134
135                macro_rules! set_parent_size {
136                    ($t:ty) => {
137                        parent_struct.size = size_of::<$t>().try_into().unwrap()
138                    };
139                }
140
141                match parent_struct.data_type {
142                    DCTypeEnum::TChar | DCTypeEnum::TInt8 | DCTypeEnum::TUInt8 => {
143                        set_parent_size!(u8)
144                    }
145                    DCTypeEnum::TInt16 | DCTypeEnum::TUInt16 => {
146                        set_parent_size!(u16)
147                    }
148                    DCTypeEnum::TInt32 | DCTypeEnum::TUInt32 => {
149                        set_parent_size!(u32)
150                    }
151                    DCTypeEnum::TInt64 | DCTypeEnum::TUInt64 => {
152                        set_parent_size!(u64)
153                    }
154                    DCTypeEnum::TFloat32 => {
155                        set_parent_size!(f32)
156                    }
157                    DCTypeEnum::TFloat64 => {
158                        set_parent_size!(f64)
159                    }
160                    _ => panic!("Invalid data type!"),
161                }
162                parent_struct
163            },
164            divisor: 1_u16,
165            orig_modulus: 0.0_f64,
166            orig_range: None,
167            modulus: 0.0_f64,
168            range: None,
169            explicit_cast: None,
170        }
171    }
172}
173
174impl LegacyDCHash for DCNumericType {
175    fn generate_hash(&self, hashgen: &mut DCHashGenerator) {
176        self.base_type.generate_hash(hashgen);
177
178        hashgen.add_int(self.divisor.into());
179
180        if self.has_modulus() {
181            hashgen.add_int(self.modulus as i32);
182        }
183        if let Some(range) = &self.range {
184            hashgen.add_int(range.min.into());
185            hashgen.add_int(range.max.into());
186        }
187    }
188}
189
190impl DCNumericType {
191    #[inline]
192    pub fn has_modulus(&self) -> bool {
193        self.orig_modulus != 0.0
194    }
195
196    #[inline]
197    pub fn has_range(&self) -> bool {
198        self.orig_range.is_some()
199    }
200
201    #[inline]
202    pub fn get_divisor(&self) -> u16 {
203        self.divisor
204    }
205
206    #[inline]
207    pub fn get_modulus(&self) -> f64 {
208        self.orig_modulus
209    }
210
211    #[inline]
212    pub fn get_range(&self) -> Option<DCNumericRange> {
213        self.orig_range.clone()
214    }
215
216    #[inline]
217    pub fn get_explicit_cast(&self) -> Option<DCTypeDefinition> {
218        self.explicit_cast.clone()
219    }
220
221    pub fn set_divisor(&mut self, divisor: u16) -> Result<(), String> {
222        if divisor == 0 {
223            return Err("Cannot set the divisor to 0.".into());
224        }
225        self.divisor = divisor;
226
227        if self.has_range() {
228            self.set_range(self.orig_range.clone().unwrap())?;
229        }
230
231        if self.has_modulus() {
232            self.set_modulus(self.orig_modulus)?;
233        }
234        Ok(())
235    }
236
237    pub fn set_modulus(&mut self, modulus: f64) -> Result<(), String> {
238        if modulus <= 0.0_f64 {
239            return Err("Modulus value cannot be less than or equal to 0.0.".into());
240        }
241        self.orig_modulus = modulus;
242        self.modulus = modulus * f64::from(self.divisor);
243
244        Ok(()) // TODO: properly validate modulus range
245    }
246
247    pub fn set_range(&mut self, range: DCNumericRange) -> Result<(), String> {
248        self.range = Some(range); // TODO: validate
249        Ok(())
250    }
251
252    pub fn set_explicit_cast(&mut self, dtype: DCTypeDefinition) -> Result<(), String> {
253        self.explicit_cast = Some(dtype);
254        Ok(()) // TODO: do some sort of type check
255    }
256
257    pub fn within_range(&self, _data: Vec<u8>, _length: u64) -> Result<(), String> {
258        todo!();
259    }
260
261    fn data_to_number(&self, data: Vec<u8>) -> Result<(bool, DCNumber), IteratorError> {
262        if self.base_type.size != data.len().try_into().unwrap() {
263            return Ok((false, DCNumber::Integer(0_i64)));
264        }
265
266        let mut dg = Datagram::default();
267        dg.add_data(data).expect("Failed to convert data to datagram.");
268
269        let mut dgi: DatagramIterator = dg.into();
270
271        match self.base_type.data_type {
272            DCTypeEnum::TInt8 => Ok((true, DCNumber::Integer(i64::from(dgi.read_i8()?)))),
273            DCTypeEnum::TInt16 => Ok((true, DCNumber::Integer(i64::from(dgi.read_i16()?)))),
274            DCTypeEnum::TInt32 => Ok((true, DCNumber::Integer(i64::from(dgi.read_i32()?)))),
275            DCTypeEnum::TInt64 => Ok((true, DCNumber::Integer(dgi.read_i64()?))),
276            DCTypeEnum::TChar | DCTypeEnum::TUInt8 => {
277                Ok((true, DCNumber::UnsignedInteger(u64::from(dgi.read_u8()?))))
278            }
279            DCTypeEnum::TUInt16 => Ok((true, DCNumber::UnsignedInteger(u64::from(dgi.read_u16()?)))),
280            DCTypeEnum::TUInt32 => Ok((true, DCNumber::UnsignedInteger(u64::from(dgi.read_u32()?)))),
281            DCTypeEnum::TUInt64 => Ok((true, DCNumber::UnsignedInteger(dgi.read_u64()?))),
282            DCTypeEnum::TFloat32 => Ok((true, DCNumber::FloatingPoint(f64::from(dgi.read_f32()?)))),
283            DCTypeEnum::TFloat64 => Ok((true, DCNumber::FloatingPoint(dgi.read_f64()?))),
284            _ => Ok((false, DCNumber::Integer(0_i64))),
285        }
286    }
287}