1use super::position::Position;
2use crate::mmr::MAX_POSITION;
3use core::{
4 convert::TryFrom,
5 fmt,
6 ops::{Add, AddAssign, Deref, Range, Sub, SubAssign},
7};
8use thiserror::Error;
9
10pub const MAX_LOCATION: u64 = 0x3FFF_FFFF_FFFF_FFFF; #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Default, Debug)]
51pub struct Location(u64);
52
53#[cfg(feature = "arbitrary")]
54impl arbitrary::Arbitrary<'_> for Location {
55 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
56 let value = u.int_in_range(0..=MAX_LOCATION)?;
57 Ok(Self(value))
58 }
59}
60
61impl Location {
62 #[inline]
67 pub(crate) const fn new_unchecked(loc: u64) -> Self {
68 Self(loc)
69 }
70
71 #[inline]
91 pub const fn new(loc: u64) -> Option<Self> {
92 if loc > MAX_LOCATION {
93 None
94 } else {
95 Some(Self(loc))
96 }
97 }
98
99 #[inline]
101 pub const fn as_u64(self) -> u64 {
102 self.0
103 }
104
105 #[inline]
107 pub const fn is_valid(self) -> bool {
108 self.0 <= MAX_LOCATION
109 }
110
111 #[inline]
113 pub const fn checked_add(self, rhs: u64) -> Option<Self> {
114 match self.0.checked_add(rhs) {
115 Some(value) => {
116 if value <= MAX_LOCATION {
117 Some(Self(value))
118 } else {
119 None
120 }
121 }
122 None => None,
123 }
124 }
125
126 #[inline]
128 pub const fn checked_sub(self, rhs: u64) -> Option<Self> {
129 match self.0.checked_sub(rhs) {
130 Some(value) => Some(Self(value)),
131 None => None,
132 }
133 }
134
135 #[inline]
137 pub const fn saturating_add(self, rhs: u64) -> Self {
138 let result = self.0.saturating_add(rhs);
139 if result > MAX_LOCATION {
140 Self(MAX_LOCATION)
141 } else {
142 Self(result)
143 }
144 }
145
146 #[inline]
148 pub const fn saturating_sub(self, rhs: u64) -> Self {
149 Self(self.0.saturating_sub(rhs))
150 }
151}
152
153impl fmt::Display for Location {
154 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155 write!(f, "Location({})", self.0)
156 }
157}
158
159impl From<u64> for Location {
160 #[inline]
161 fn from(value: u64) -> Self {
162 Self::new_unchecked(value)
163 }
164}
165
166impl From<usize> for Location {
167 #[inline]
168 fn from(value: usize) -> Self {
169 Self::new_unchecked(value as u64)
170 }
171}
172
173impl Deref for Location {
174 type Target = u64;
175 fn deref(&self) -> &Self::Target {
176 &self.0
177 }
178}
179
180impl From<Location> for u64 {
181 #[inline]
182 fn from(loc: Location) -> Self {
183 *loc
184 }
185}
186
187impl Add for Location {
193 type Output = Self;
194
195 #[inline]
196 fn add(self, rhs: Self) -> Self::Output {
197 Self(self.0 + rhs.0)
198 }
199}
200
201impl Add<u64> for Location {
207 type Output = Self;
208
209 #[inline]
210 fn add(self, rhs: u64) -> Self::Output {
211 Self(self.0 + rhs)
212 }
213}
214
215impl Sub for Location {
221 type Output = Self;
222
223 #[inline]
224 fn sub(self, rhs: Self) -> Self::Output {
225 Self(self.0 - rhs.0)
226 }
227}
228
229impl Sub<u64> for Location {
235 type Output = Self;
236
237 #[inline]
238 fn sub(self, rhs: u64) -> Self::Output {
239 Self(self.0 - rhs)
240 }
241}
242
243impl PartialEq<u64> for Location {
244 #[inline]
245 fn eq(&self, other: &u64) -> bool {
246 self.0 == *other
247 }
248}
249
250impl PartialOrd<u64> for Location {
251 #[inline]
252 fn partial_cmp(&self, other: &u64) -> Option<core::cmp::Ordering> {
253 self.0.partial_cmp(other)
254 }
255}
256
257impl PartialEq<Location> for u64 {
259 #[inline]
260 fn eq(&self, other: &Location) -> bool {
261 *self == other.0
262 }
263}
264
265impl PartialOrd<Location> for u64 {
266 #[inline]
267 fn partial_cmp(&self, other: &Location) -> Option<core::cmp::Ordering> {
268 self.partial_cmp(&other.0)
269 }
270}
271
272impl AddAssign<u64> for Location {
278 #[inline]
279 fn add_assign(&mut self, rhs: u64) {
280 self.0 += rhs;
281 }
282}
283
284impl SubAssign<u64> for Location {
290 #[inline]
291 fn sub_assign(&mut self, rhs: u64) {
292 self.0 -= rhs;
293 }
294}
295
296impl TryFrom<Position> for Location {
297 type Error = LocationError;
298
299 #[inline]
306 fn try_from(pos: Position) -> Result<Self, Self::Error> {
307 if *pos > MAX_POSITION {
309 return Err(LocationError::Overflow(pos));
310 }
311 if *pos == 0 {
313 return Ok(Self(0));
314 }
315
316 let start = u64::MAX >> (pos + 1).leading_zeros();
319 let height = start.trailing_ones();
320 if height == 0 {
322 return Err(LocationError::NonLeaf(pos));
323 }
324 let mut two_h = 1 << (height - 1);
325 let mut cur_node = start - 1;
326 let mut leaf_loc_floor = 0u64;
327
328 while two_h > 1 {
329 if cur_node == *pos {
330 return Err(LocationError::NonLeaf(pos));
331 }
332 let left_pos = cur_node - two_h;
333 two_h >>= 1;
334 if *pos > left_pos {
335 leaf_loc_floor += two_h;
338 cur_node -= 1; } else {
340 cur_node = left_pos;
342 }
343 }
344
345 Ok(Self(leaf_loc_floor))
346 }
347}
348
349#[derive(Debug, Clone, Copy, Eq, PartialEq, Error)]
351pub enum LocationError {
352 #[error("{0} is not a leaf")]
353 NonLeaf(Position),
354
355 #[error("{0} > MAX_LOCATION")]
356 Overflow(Position),
357}
358
359pub trait LocationRangeExt {
361 fn to_usize_range(&self) -> Range<usize>;
363}
364
365impl LocationRangeExt for Range<Location> {
366 #[inline]
367 fn to_usize_range(&self) -> Range<usize> {
368 *self.start as usize..*self.end as usize
369 }
370}
371
372#[cfg(test)]
373mod tests {
374 use super::{Location, MAX_LOCATION};
375 use crate::mmr::{position::Position, LocationError, MAX_POSITION};
376
377 #[test]
379 fn test_try_from_position() {
380 const CASES: &[(Position, Location)] = &[
381 (Position::new(0), Location::new_unchecked(0)),
382 (Position::new(1), Location::new_unchecked(1)),
383 (Position::new(3), Location::new_unchecked(2)),
384 (Position::new(4), Location::new_unchecked(3)),
385 (Position::new(7), Location::new_unchecked(4)),
386 (Position::new(8), Location::new_unchecked(5)),
387 (Position::new(10), Location::new_unchecked(6)),
388 (Position::new(11), Location::new_unchecked(7)),
389 (Position::new(15), Location::new_unchecked(8)),
390 (Position::new(16), Location::new_unchecked(9)),
391 (Position::new(18), Location::new_unchecked(10)),
392 (Position::new(19), Location::new_unchecked(11)),
393 (Position::new(22), Location::new_unchecked(12)),
394 (Position::new(23), Location::new_unchecked(13)),
395 (Position::new(25), Location::new_unchecked(14)),
396 (Position::new(26), Location::new_unchecked(15)),
397 ];
398 for (pos, expected_loc) in CASES {
399 let loc = Location::try_from(*pos).expect("should map to a leaf location");
400 assert_eq!(loc, *expected_loc);
401 }
402 }
403
404 #[test]
406 fn test_try_from_position_error() {
407 const CASES: &[Position] = &[
408 Position::new(2),
409 Position::new(5),
410 Position::new(6),
411 Position::new(9),
412 Position::new(12),
413 Position::new(13),
414 Position::new(14),
415 Position::new(17),
416 Position::new(20),
417 Position::new(21),
418 Position::new(24),
419 Position::new(27),
420 Position::new(28),
421 Position::new(29),
422 Position::new(30),
423 ];
424 for &pos in CASES {
425 let err = Location::try_from(pos).expect_err("position is not a leaf");
426 assert_eq!(err, LocationError::NonLeaf(pos));
427 }
428 }
429
430 #[test]
431 fn test_try_from_position_error_overflow() {
432 let overflow_pos = Position::new(u64::MAX);
433 let err = Location::try_from(overflow_pos).expect_err("should overflow");
434 assert_eq!(err, LocationError::Overflow(overflow_pos));
435
436 let result = Location::try_from(MAX_POSITION);
438 assert_eq!(result, Err(LocationError::NonLeaf(MAX_POSITION)));
439
440 let overflow_pos = MAX_POSITION + 1;
441 let err = Location::try_from(overflow_pos).expect_err("should overflow");
442 assert_eq!(err, LocationError::Overflow(overflow_pos));
443 }
444
445 #[test]
446 fn test_checked_add() {
447 let loc = Location::new_unchecked(10);
448 assert_eq!(loc.checked_add(5).unwrap(), 15);
449
450 assert!(Location::new_unchecked(u64::MAX).checked_add(1).is_none());
452
453 assert!(Location::new_unchecked(MAX_LOCATION)
455 .checked_add(1)
456 .is_none());
457
458 let loc = Location::new_unchecked(MAX_LOCATION - 10);
460 assert_eq!(loc.checked_add(10).unwrap(), MAX_LOCATION);
461 }
462
463 #[test]
464 fn test_checked_sub() {
465 let loc = Location::new_unchecked(10);
466 assert_eq!(loc.checked_sub(5).unwrap(), 5);
467 assert!(loc.checked_sub(11).is_none());
468 }
469
470 #[test]
471 fn test_saturating_add() {
472 let loc = Location::new_unchecked(10);
473 assert_eq!(loc.saturating_add(5), 15);
474
475 assert_eq!(
477 Location::new_unchecked(u64::MAX).saturating_add(1),
478 MAX_LOCATION
479 );
480 assert_eq!(
481 Location::new_unchecked(MAX_LOCATION).saturating_add(1),
482 MAX_LOCATION
483 );
484 assert_eq!(
485 Location::new_unchecked(MAX_LOCATION).saturating_add(1000),
486 MAX_LOCATION
487 );
488 }
489
490 #[test]
491 fn test_saturating_sub() {
492 let loc = Location::new_unchecked(10);
493 assert_eq!(loc.saturating_sub(5), 5);
494 assert_eq!(Location::new_unchecked(0).saturating_sub(1), 0);
495 }
496
497 #[test]
498 fn test_display() {
499 let location = Location::new_unchecked(42);
500 assert_eq!(location.to_string(), "Location(42)");
501 }
502
503 #[test]
504 fn test_add() {
505 let loc1 = Location::new_unchecked(10);
506 let loc2 = Location::new_unchecked(5);
507 assert_eq!((loc1 + loc2), 15);
508 }
509
510 #[test]
511 fn test_sub() {
512 let loc1 = Location::new_unchecked(10);
513 let loc2 = Location::new_unchecked(3);
514 assert_eq!((loc1 - loc2), 7);
515 }
516
517 #[test]
518 fn test_comparison_with_u64() {
519 let loc = Location::new_unchecked(42);
520
521 assert_eq!(loc, 42u64);
523 assert_eq!(42u64, loc);
524 assert_ne!(loc, 43u64);
525 assert_ne!(43u64, loc);
526
527 assert!(loc < 43u64);
529 assert!(43u64 > loc);
530 assert!(loc > 41u64);
531 assert!(41u64 < loc);
532 assert!(loc <= 42u64);
533 assert!(42u64 >= loc);
534 }
535
536 #[test]
537 fn test_assignment_with_u64() {
538 let mut loc = Location::new_unchecked(10);
539
540 loc += 5;
542 assert_eq!(loc, 15u64);
543
544 loc -= 3;
546 assert_eq!(loc, 12u64);
547 }
548
549 #[test]
550 fn test_new() {
551 assert!(Location::new(0).is_some());
553 assert!(Location::new(1000).is_some());
554 assert!(Location::new(MAX_LOCATION).is_some());
555
556 assert!(Location::new(MAX_LOCATION + 1).is_none());
558 assert!(Location::new(u64::MAX).is_none());
559 }
560
561 #[test]
562 fn test_is_valid() {
563 assert!(Location::new_unchecked(0).is_valid());
564 assert!(Location::new_unchecked(1000).is_valid());
565 assert!(Location::new_unchecked(MAX_LOCATION).is_valid());
566 assert!(Location::new_unchecked(MAX_LOCATION).is_valid());
567 assert!(!Location::new_unchecked(u64::MAX).is_valid());
568 }
569
570 #[test]
571 fn test_max_location_boundary() {
572 let max_loc = Location::new_unchecked(MAX_LOCATION);
574 assert!(max_loc.is_valid());
575 let pos = Position::try_from(max_loc).unwrap();
576 let expected = (1u64 << 63) - 64;
580 assert_eq!(*pos, expected);
581 }
582
583 #[test]
584 fn test_overflow_location_returns_error() {
585 let over_loc = Location::new_unchecked(MAX_LOCATION + 1);
587 assert!(Position::try_from(over_loc).is_err());
588
589 match Position::try_from(over_loc) {
591 Err(crate::mmr::Error::LocationOverflow(loc)) => {
592 assert_eq!(loc, over_loc);
593 }
594 _ => panic!("expected LocationOverflow error"),
595 }
596 }
597}