cuprate_helper/
map.rs

1//! Mapping of data types.
2//!
3//! This module provides functions solely for mapping data types into others, mostly similar ones.
4//!
5//! `#[no_std]` compatible.
6
7//---------------------------------------------------------------------------------------------------- Use
8use monero_serai::transaction::Timelock;
9
10use cuprate_constants::block::MAX_BLOCK_HEIGHT;
11
12use crate::cast::{u64_to_usize, usize_to_u64};
13
14//---------------------------------------------------------------------------------------------------- `(u64, u64) <-> u128`
15/// Split a [`u128`] value into 2 64-bit values.
16///
17/// The tuple returned is `(low, high)` where `low` is the least significant
18/// 64-bits of `number`, and `high` is the most significant.
19///
20/// Note that the output of this function are `u64` representations of _bits_, not numerical values.
21///
22/// See [`combine_low_high_bits_to_u128`] for the inverse function.
23///
24/// ```rust
25/// # use cuprate_helper::map::*;
26/// let value = u128::MAX - 1;
27/// let low = u64::MAX - 1;
28/// let high = u64::MAX;
29///
30/// assert_eq!(split_u128_into_low_high_bits(value), (low, high));
31/// ```
32#[inline]
33pub const fn split_u128_into_low_high_bits(value: u128) -> (u64, u64) {
34    #[expect(clippy::cast_possible_truncation)]
35    (value as u64, (value >> 64) as u64)
36}
37
38/// Combine 2 64-bit values into a single [`u128`] value.
39///
40/// The inputs:
41/// - `low_bits` are the _least_ significant 64-bits of `cumulative_difficulty`
42/// - `high_bits` are the _most_ significant 64-bits of `cumulative_difficulty`
43///
44/// Note that `low_bits` & `high_bits` should be `u64` representation of _bits_, not numerical values.
45///
46/// See [`split_u128_into_low_high_bits`] for the inverse function.
47///
48/// ```rust
49/// # use cuprate_helper::map::*;
50/// let value = u128::MAX - 1;
51/// let low = u64::MAX - 1;
52/// let high = u64::MAX;
53///
54/// assert_eq!(combine_low_high_bits_to_u128(low, high), value);
55/// ```
56#[inline]
57pub const fn combine_low_high_bits_to_u128(low_bits: u64, high_bits: u64) -> u128 {
58    let res = (high_bits as u128) << 64;
59    res | (low_bits as u128)
60}
61
62//---------------------------------------------------------------------------------------------------- Timelock
63/// Map a [`u64`] to a [`Timelock`].
64///
65/// Height/time is not differentiated via type, but rather:
66/// "height is any value less than [`MAX_BLOCK_HEIGHT`] and timestamp is any value above"
67/// so the `u64/usize` is stored without any tag.
68///
69/// See [`timelock_to_u64`] for the inverse function.
70///
71/// - <https://github.com/Cuprate/cuprate/pull/102#discussion_r1558504285>
72/// - <https://github.com/serai-dex/serai/blob/bc1dec79917d37d326ac3d9bc571a64131b0424a/coins/monero/src/transaction.rs#L139>
73///
74/// ```rust
75/// # use cuprate_helper::map::*;
76/// # use monero_serai::transaction::*;
77/// use cuprate_constants::block::{MAX_BLOCK_HEIGHT, MAX_BLOCK_HEIGHT_USIZE};
78/// assert_eq!(u64_to_timelock(0), Timelock::None);
79/// assert_eq!(u64_to_timelock(MAX_BLOCK_HEIGHT-1), Timelock::Block(MAX_BLOCK_HEIGHT_USIZE-1));
80/// assert_eq!(u64_to_timelock(MAX_BLOCK_HEIGHT), Timelock::Time(MAX_BLOCK_HEIGHT));
81/// ```
82pub const fn u64_to_timelock(u: u64) -> Timelock {
83    if u == 0 {
84        Timelock::None
85    } else if u < MAX_BLOCK_HEIGHT {
86        Timelock::Block(u64_to_usize(u))
87    } else {
88        Timelock::Time(u)
89    }
90}
91
92/// Map [`Timelock`] to a [`u64`].
93///
94/// See [`u64_to_timelock`] for the inverse function and more documentation.
95///
96/// ```rust
97/// # use cuprate_helper::map::*;
98/// # use monero_serai::transaction::*;
99/// use cuprate_constants::block::{MAX_BLOCK_HEIGHT, MAX_BLOCK_HEIGHT_USIZE};
100/// assert_eq!(timelock_to_u64(Timelock::None), 0);
101/// assert_eq!(timelock_to_u64(Timelock::Block(MAX_BLOCK_HEIGHT_USIZE-1)), MAX_BLOCK_HEIGHT-1);
102/// assert_eq!(timelock_to_u64(Timelock::Time(MAX_BLOCK_HEIGHT)), MAX_BLOCK_HEIGHT);
103/// ```
104pub const fn timelock_to_u64(timelock: Timelock) -> u64 {
105    match timelock {
106        Timelock::None => 0,
107        Timelock::Block(u) => usize_to_u64(u),
108        Timelock::Time(u) => u,
109    }
110}
111
112//---------------------------------------------------------------------------------------------------- Tests
113#[cfg(test)]
114mod test {}