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