1use std::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
18use std::convert::From;
19use std::ops::{Add, Sub};
20
21use tracing::{Span, instrument};
22
23use crate::Result;
24use crate::error::HyperlightError;
25
26#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
30pub(crate) struct Offset(u64);
31
32impl Offset {
33 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
35 #[allow(dead_code)]
36 pub(super) fn zero() -> Self {
37 Self::default()
38 }
39
40 #[allow(dead_code)]
42 pub(super) fn round_up_to(self, alignment: u64) -> Self {
43 let remainder = self.0 % alignment;
44 let multiples = self.0 / alignment;
45 match remainder {
46 0 => self,
47 _ => Offset::from((multiples + 1) * alignment),
48 }
49 }
50}
51
52impl Default for Offset {
53 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
54 fn default() -> Self {
55 Offset::from(0_u64)
56 }
57}
58
59impl From<u64> for Offset {
60 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
61 fn from(val: u64) -> Self {
62 Self(val)
63 }
64}
65
66impl From<&Offset> for u64 {
67 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
68 fn from(val: &Offset) -> u64 {
69 val.0
70 }
71}
72
73impl From<Offset> for u64 {
74 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
75 fn from(val: Offset) -> u64 {
76 val.0
77 }
78}
79
80impl TryFrom<Offset> for i64 {
81 type Error = HyperlightError;
82 #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
83 fn try_from(val: Offset) -> Result<i64> {
84 Ok(i64::try_from(val.0)?)
85 }
86}
87
88impl TryFrom<i64> for Offset {
89 type Error = HyperlightError;
90 #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
91 fn try_from(val: i64) -> Result<Offset> {
92 let val_u64 = u64::try_from(val)?;
93 Ok(Offset::from(val_u64))
94 }
95}
96
97impl TryFrom<usize> for Offset {
98 type Error = HyperlightError;
99 #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
100 fn try_from(val: usize) -> Result<Offset> {
101 Ok(u64::try_from(val).map(Offset::from)?)
102 }
103}
104
105impl TryFrom<&Offset> for usize {
108 type Error = HyperlightError;
109 #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
110 fn try_from(val: &Offset) -> Result<usize> {
111 Ok(usize::try_from(val.0)?)
112 }
113}
114
115impl TryFrom<Offset> for usize {
116 type Error = HyperlightError;
117 #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
118 fn try_from(val: Offset) -> Result<usize> {
119 usize::try_from(&val)
120 }
121}
122
123impl Add<Offset> for Offset {
124 type Output = Offset;
125 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
126 fn add(self, rhs: Offset) -> Offset {
127 Offset::from(self.0 + rhs.0)
128 }
129}
130
131impl Add<usize> for Offset {
132 type Output = Offset;
133 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
134 fn add(self, rhs: usize) -> Offset {
135 Offset(self.0 + rhs as u64)
136 }
137}
138
139impl Add<Offset> for usize {
140 type Output = Offset;
141 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
142 fn add(self, rhs: Offset) -> Offset {
143 rhs.add(self)
144 }
145}
146
147impl Add<u64> for Offset {
148 type Output = Offset;
149 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
150 fn add(self, rhs: u64) -> Offset {
151 Offset(self.0 + rhs)
152 }
153}
154
155impl Add<Offset> for u64 {
156 type Output = Offset;
157 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
158 fn add(self, rhs: Offset) -> Offset {
159 rhs.add(self)
160 }
161}
162
163impl Sub<Offset> for Offset {
164 type Output = Offset;
165 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
166 fn sub(self, rhs: Offset) -> Offset {
167 Offset::from(self.0 - rhs.0)
168 }
169}
170
171impl Sub<usize> for Offset {
172 type Output = Offset;
173 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
174 fn sub(self, rhs: usize) -> Offset {
175 Offset(self.0 - rhs as u64)
176 }
177}
178
179impl Sub<Offset> for usize {
180 type Output = Offset;
181 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
182 fn sub(self, rhs: Offset) -> Offset {
183 rhs.sub(self)
184 }
185}
186
187impl Sub<u64> for Offset {
188 type Output = Offset;
189 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
190 fn sub(self, rhs: u64) -> Offset {
191 Offset(self.0 - rhs)
192 }
193}
194
195impl Sub<Offset> for u64 {
196 type Output = Offset;
197 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
198 fn sub(self, rhs: Offset) -> Offset {
199 rhs.sub(self)
200 }
201}
202
203impl PartialEq<usize> for Offset {
204 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
205 fn eq(&self, other: &usize) -> bool {
206 match usize::try_from(self) {
207 Ok(offset_usize) => offset_usize == *other,
208 _ => false,
209 }
210 }
211}
212
213impl PartialOrd<usize> for Offset {
214 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
215 fn partial_cmp(&self, rhs: &usize) -> Option<Ordering> {
216 match usize::try_from(self) {
217 Ok(offset_usize) if offset_usize > *rhs => Some(Ordering::Greater),
218 Ok(offset_usize) if offset_usize == *rhs => Some(Ordering::Equal),
219 Ok(_) => Some(Ordering::Less),
220 Err(_) => None,
221 }
222 }
223}
224
225impl PartialEq<u64> for Offset {
226 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
227 fn eq(&self, rhs: &u64) -> bool {
228 u64::from(self) == *rhs
229 }
230}
231
232impl PartialOrd<u64> for Offset {
233 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
234 fn partial_cmp(&self, rhs: &u64) -> Option<Ordering> {
235 let lhs: u64 = self.into();
236 match lhs > *rhs {
237 true => Some(Ordering::Greater),
238 false if lhs == *rhs => Some(Ordering::Equal),
239 false => Some(Ordering::Less),
240 }
241 }
242}
243
244#[cfg(test)]
245mod tests {
246 use proptest::prelude::*;
247
248 use super::Offset;
249
250 proptest! {
251 #[test]
252 fn i64_roundtrip(i64_val in (i64::MIN..i64::MAX)) {
253 let offset_res = Offset::try_from(i64_val);
254
255 if i64_val < 0 {
256 assert!(offset_res.is_err());
257 } else {
258 assert!(offset_res.is_ok());
259 let offset = offset_res.unwrap();
260 let ret_i64_val = {
261 let res = i64::try_from(offset);
262 assert!(res.is_ok());
263 res.unwrap()
264 };
265 assert_eq!(i64_val, ret_i64_val);
266 }
267 }
268 #[test]
269 fn usize_roundtrip(val in (usize::MIN..usize::MAX)) {
270 let offset = Offset::try_from(val).unwrap();
271 assert_eq!(val, usize::try_from(offset).unwrap());
272 }
273
274 #[test]
275 fn add_numeric_types(usize_val in (usize::MIN..usize::MAX), u64_val in (u64::MIN..u64::MAX)) {
276 let start = Offset::default();
277 {
278 assert_eq!(usize_val, usize::try_from(start + usize_val).unwrap());
280 }
281 {
282 assert_eq!(u64_val, u64::from(start + u64_val));
284 }
285 }
286 }
287
288 #[test]
289 fn round_up_to() {
290 let offset = Offset::from(0);
291 let rounded = offset.round_up_to(4);
292 assert_eq!(rounded, offset);
293
294 let offset = Offset::from(1);
295 let rounded = offset.round_up_to(4);
296 assert_eq!(rounded, Offset::from(4));
297
298 let offset = Offset::from(3);
299 let rounded = offset.round_up_to(4);
300 assert_eq!(rounded, Offset::from(4));
301
302 let offset = Offset::from(4);
303 let rounded = offset.round_up_to(4);
304 assert_eq!(rounded, Offset::from(4));
305
306 let offset = Offset::from(5);
307 let rounded = offset.round_up_to(4);
308 assert_eq!(rounded, Offset::from(8));
309 }
310}