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
53impl Location {
54 #[inline]
59 pub(crate) const fn new_unchecked(loc: u64) -> Self {
60 Self(loc)
61 }
62
63 #[inline]
83 pub const fn new(loc: u64) -> Option<Self> {
84 if loc > MAX_LOCATION {
85 None
86 } else {
87 Some(Self(loc))
88 }
89 }
90
91 #[inline]
93 pub const fn as_u64(self) -> u64 {
94 self.0
95 }
96
97 #[inline]
99 pub const fn is_valid(self) -> bool {
100 self.0 <= MAX_LOCATION
101 }
102
103 #[inline]
105 pub const fn checked_add(self, rhs: u64) -> Option<Self> {
106 match self.0.checked_add(rhs) {
107 Some(value) => {
108 if value <= MAX_LOCATION {
109 Some(Self(value))
110 } else {
111 None
112 }
113 }
114 None => None,
115 }
116 }
117
118 #[inline]
120 pub const fn checked_sub(self, rhs: u64) -> Option<Self> {
121 match self.0.checked_sub(rhs) {
122 Some(value) => Some(Self(value)),
123 None => None,
124 }
125 }
126
127 #[inline]
129 pub const fn saturating_add(self, rhs: u64) -> Self {
130 let result = self.0.saturating_add(rhs);
131 if result > MAX_LOCATION {
132 Self(MAX_LOCATION)
133 } else {
134 Self(result)
135 }
136 }
137
138 #[inline]
140 pub const fn saturating_sub(self, rhs: u64) -> Self {
141 Self(self.0.saturating_sub(rhs))
142 }
143}
144
145impl fmt::Display for Location {
146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147 write!(f, "Location({})", self.0)
148 }
149}
150
151impl From<u64> for Location {
152 #[inline]
153 fn from(value: u64) -> Self {
154 Self::new_unchecked(value)
155 }
156}
157
158impl From<usize> for Location {
159 #[inline]
160 fn from(value: usize) -> Self {
161 Self::new_unchecked(value as u64)
162 }
163}
164
165impl Deref for Location {
166 type Target = u64;
167 fn deref(&self) -> &Self::Target {
168 &self.0
169 }
170}
171
172impl From<Location> for u64 {
173 #[inline]
174 fn from(loc: Location) -> Self {
175 *loc
176 }
177}
178
179impl Add for Location {
185 type Output = Self;
186
187 #[inline]
188 fn add(self, rhs: Self) -> Self::Output {
189 Self(self.0 + rhs.0)
190 }
191}
192
193impl Add<u64> for Location {
199 type Output = Self;
200
201 #[inline]
202 fn add(self, rhs: u64) -> Self::Output {
203 Self(self.0 + rhs)
204 }
205}
206
207impl Sub for Location {
213 type Output = Self;
214
215 #[inline]
216 fn sub(self, rhs: Self) -> Self::Output {
217 Self(self.0 - rhs.0)
218 }
219}
220
221impl Sub<u64> for Location {
227 type Output = Self;
228
229 #[inline]
230 fn sub(self, rhs: u64) -> Self::Output {
231 Self(self.0 - rhs)
232 }
233}
234
235impl PartialEq<u64> for Location {
236 #[inline]
237 fn eq(&self, other: &u64) -> bool {
238 self.0 == *other
239 }
240}
241
242impl PartialOrd<u64> for Location {
243 #[inline]
244 fn partial_cmp(&self, other: &u64) -> Option<core::cmp::Ordering> {
245 self.0.partial_cmp(other)
246 }
247}
248
249impl PartialEq<Location> for u64 {
251 #[inline]
252 fn eq(&self, other: &Location) -> bool {
253 *self == other.0
254 }
255}
256
257impl PartialOrd<Location> for u64 {
258 #[inline]
259 fn partial_cmp(&self, other: &Location) -> Option<core::cmp::Ordering> {
260 self.partial_cmp(&other.0)
261 }
262}
263
264impl AddAssign<u64> for Location {
270 #[inline]
271 fn add_assign(&mut self, rhs: u64) {
272 self.0 += rhs;
273 }
274}
275
276impl SubAssign<u64> for Location {
282 #[inline]
283 fn sub_assign(&mut self, rhs: u64) {
284 self.0 -= rhs;
285 }
286}
287
288impl TryFrom<Position> for Location {
289 type Error = LocationError;
290
291 #[inline]
298 fn try_from(pos: Position) -> Result<Self, Self::Error> {
299 if *pos > MAX_POSITION {
301 return Err(LocationError::Overflow(pos));
302 }
303 if *pos == 0 {
305 return Ok(Self(0));
306 }
307
308 let start = u64::MAX >> (pos + 1).leading_zeros();
311 let height = start.trailing_ones();
312 if height == 0 {
314 return Err(LocationError::NonLeaf(pos));
315 }
316 let mut two_h = 1 << (height - 1);
317 let mut cur_node = start - 1;
318 let mut leaf_loc_floor = 0u64;
319
320 while two_h > 1 {
321 if cur_node == *pos {
322 return Err(LocationError::NonLeaf(pos));
323 }
324 let left_pos = cur_node - two_h;
325 two_h >>= 1;
326 if *pos > left_pos {
327 leaf_loc_floor += two_h;
330 cur_node -= 1; } else {
332 cur_node = left_pos;
334 }
335 }
336
337 Ok(Self(leaf_loc_floor))
338 }
339}
340
341#[derive(Debug, Clone, Copy, Eq, PartialEq, Error)]
343pub enum LocationError {
344 #[error("{0} is not a leaf")]
345 NonLeaf(Position),
346
347 #[error("{0} > MAX_LOCATION")]
348 Overflow(Position),
349}
350
351pub trait LocationRangeExt {
353 fn to_usize_range(&self) -> Range<usize>;
355}
356
357impl LocationRangeExt for Range<Location> {
358 #[inline]
359 fn to_usize_range(&self) -> Range<usize> {
360 *self.start as usize..*self.end as usize
361 }
362}
363
364#[cfg(test)]
365mod tests {
366 use super::{Location, MAX_LOCATION};
367 use crate::mmr::{position::Position, LocationError, MAX_POSITION};
368
369 #[test]
371 fn test_try_from_position() {
372 const CASES: &[(Position, Location)] = &[
373 (Position::new(0), Location::new_unchecked(0)),
374 (Position::new(1), Location::new_unchecked(1)),
375 (Position::new(3), Location::new_unchecked(2)),
376 (Position::new(4), Location::new_unchecked(3)),
377 (Position::new(7), Location::new_unchecked(4)),
378 (Position::new(8), Location::new_unchecked(5)),
379 (Position::new(10), Location::new_unchecked(6)),
380 (Position::new(11), Location::new_unchecked(7)),
381 (Position::new(15), Location::new_unchecked(8)),
382 (Position::new(16), Location::new_unchecked(9)),
383 (Position::new(18), Location::new_unchecked(10)),
384 (Position::new(19), Location::new_unchecked(11)),
385 (Position::new(22), Location::new_unchecked(12)),
386 (Position::new(23), Location::new_unchecked(13)),
387 (Position::new(25), Location::new_unchecked(14)),
388 (Position::new(26), Location::new_unchecked(15)),
389 ];
390 for (pos, expected_loc) in CASES {
391 let loc = Location::try_from(*pos).expect("should map to a leaf location");
392 assert_eq!(loc, *expected_loc);
393 }
394 }
395
396 #[test]
398 fn test_try_from_position_error() {
399 const CASES: &[Position] = &[
400 Position::new(2),
401 Position::new(5),
402 Position::new(6),
403 Position::new(9),
404 Position::new(12),
405 Position::new(13),
406 Position::new(14),
407 Position::new(17),
408 Position::new(20),
409 Position::new(21),
410 Position::new(24),
411 Position::new(27),
412 Position::new(28),
413 Position::new(29),
414 Position::new(30),
415 ];
416 for &pos in CASES {
417 let err = Location::try_from(pos).expect_err("position is not a leaf");
418 assert_eq!(err, LocationError::NonLeaf(pos));
419 }
420 }
421
422 #[test]
423 fn test_try_from_position_error_overflow() {
424 let overflow_pos = Position::new(u64::MAX);
425 let err = Location::try_from(overflow_pos).expect_err("should overflow");
426 assert_eq!(err, LocationError::Overflow(overflow_pos));
427
428 let result = Location::try_from(MAX_POSITION);
430 assert_eq!(result, Err(LocationError::NonLeaf(MAX_POSITION)));
431
432 let overflow_pos = MAX_POSITION + 1;
433 let err = Location::try_from(overflow_pos).expect_err("should overflow");
434 assert_eq!(err, LocationError::Overflow(overflow_pos));
435 }
436
437 #[test]
438 fn test_checked_add() {
439 let loc = Location::new_unchecked(10);
440 assert_eq!(loc.checked_add(5).unwrap(), 15);
441
442 assert!(Location::new_unchecked(u64::MAX).checked_add(1).is_none());
444
445 assert!(Location::new_unchecked(MAX_LOCATION)
447 .checked_add(1)
448 .is_none());
449
450 let loc = Location::new_unchecked(MAX_LOCATION - 10);
452 assert_eq!(loc.checked_add(10).unwrap(), MAX_LOCATION);
453 }
454
455 #[test]
456 fn test_checked_sub() {
457 let loc = Location::new_unchecked(10);
458 assert_eq!(loc.checked_sub(5).unwrap(), 5);
459 assert!(loc.checked_sub(11).is_none());
460 }
461
462 #[test]
463 fn test_saturating_add() {
464 let loc = Location::new_unchecked(10);
465 assert_eq!(loc.saturating_add(5), 15);
466
467 assert_eq!(
469 Location::new_unchecked(u64::MAX).saturating_add(1),
470 MAX_LOCATION
471 );
472 assert_eq!(
473 Location::new_unchecked(MAX_LOCATION).saturating_add(1),
474 MAX_LOCATION
475 );
476 assert_eq!(
477 Location::new_unchecked(MAX_LOCATION).saturating_add(1000),
478 MAX_LOCATION
479 );
480 }
481
482 #[test]
483 fn test_saturating_sub() {
484 let loc = Location::new_unchecked(10);
485 assert_eq!(loc.saturating_sub(5), 5);
486 assert_eq!(Location::new_unchecked(0).saturating_sub(1), 0);
487 }
488
489 #[test]
490 fn test_display() {
491 let location = Location::new_unchecked(42);
492 assert_eq!(location.to_string(), "Location(42)");
493 }
494
495 #[test]
496 fn test_add() {
497 let loc1 = Location::new_unchecked(10);
498 let loc2 = Location::new_unchecked(5);
499 assert_eq!((loc1 + loc2), 15);
500 }
501
502 #[test]
503 fn test_sub() {
504 let loc1 = Location::new_unchecked(10);
505 let loc2 = Location::new_unchecked(3);
506 assert_eq!((loc1 - loc2), 7);
507 }
508
509 #[test]
510 fn test_comparison_with_u64() {
511 let loc = Location::new_unchecked(42);
512
513 assert_eq!(loc, 42u64);
515 assert_eq!(42u64, loc);
516 assert_ne!(loc, 43u64);
517 assert_ne!(43u64, loc);
518
519 assert!(loc < 43u64);
521 assert!(43u64 > loc);
522 assert!(loc > 41u64);
523 assert!(41u64 < loc);
524 assert!(loc <= 42u64);
525 assert!(42u64 >= loc);
526 }
527
528 #[test]
529 fn test_assignment_with_u64() {
530 let mut loc = Location::new_unchecked(10);
531
532 loc += 5;
534 assert_eq!(loc, 15u64);
535
536 loc -= 3;
538 assert_eq!(loc, 12u64);
539 }
540
541 #[test]
542 fn test_new() {
543 assert!(Location::new(0).is_some());
545 assert!(Location::new(1000).is_some());
546 assert!(Location::new(MAX_LOCATION).is_some());
547
548 assert!(Location::new(MAX_LOCATION + 1).is_none());
550 assert!(Location::new(u64::MAX).is_none());
551 }
552
553 #[test]
554 fn test_is_valid() {
555 assert!(Location::new_unchecked(0).is_valid());
556 assert!(Location::new_unchecked(1000).is_valid());
557 assert!(Location::new_unchecked(MAX_LOCATION).is_valid());
558 assert!(Location::new_unchecked(MAX_LOCATION).is_valid());
559 assert!(!Location::new_unchecked(u64::MAX).is_valid());
560 }
561
562 #[test]
563 fn test_max_location_boundary() {
564 let max_loc = Location::new_unchecked(MAX_LOCATION);
566 assert!(max_loc.is_valid());
567 let pos = Position::try_from(max_loc).unwrap();
568 let expected = (1u64 << 63) - 64;
572 assert_eq!(*pos, expected);
573 }
574
575 #[test]
576 fn test_overflow_location_returns_error() {
577 let over_loc = Location::new_unchecked(MAX_LOCATION + 1);
579 assert!(Position::try_from(over_loc).is_err());
580
581 match Position::try_from(over_loc) {
583 Err(crate::mmr::Error::LocationOverflow(loc)) => {
584 assert_eq!(loc, over_loc);
585 }
586 _ => panic!("expected LocationOverflow error"),
587 }
588 }
589}