1use core::cmp::Ordering;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub struct Since(u64);
6
7impl Since {
8 const LOCK_TYPE_FLAG: u64 = 1 << 63;
9 const METRIC_TYPE_FLAG_MASK: u64 = 0x6000_0000_0000_0000;
10 const FLAGS_MASK: u64 = 0xff00_0000_0000_0000;
11 const VALUE_MASK: u64 = 0x00ff_ffff_ffff_ffff;
12 const REMAIN_FLAGS_BITS: u64 = 0x1f00_0000_0000_0000;
13 const LOCK_BY_BLOCK_NUMBER_MASK: u64 = 0x0000_0000_0000_0000;
14 const LOCK_BY_EPOCH_MASK: u64 = 0x2000_0000_0000_0000;
15 const LOCK_BY_TIMESTAMP_MASK: u64 = 0x4000_0000_0000_0000;
16
17 pub fn from_block_number(number: u64, absolute: bool) -> Option<Self> {
18 if number & Self::FLAGS_MASK != 0 {
19 return None;
20 }
21 Some(Self::new(
22 number
23 | Self::LOCK_BY_BLOCK_NUMBER_MASK
24 | (if absolute { 0 } else { Self::LOCK_TYPE_FLAG }),
25 ))
26 }
27
28 pub fn from_timestamp(timestamp: u64, absolute: bool) -> Option<Self> {
29 if timestamp & Self::FLAGS_MASK != 0 {
30 return None;
31 }
32 Some(Self::new(
33 timestamp
34 | Self::LOCK_BY_TIMESTAMP_MASK
35 | (if absolute { 0 } else { Self::LOCK_TYPE_FLAG }),
36 ))
37 }
38
39 pub fn from_epoch(epoch: EpochNumberWithFraction, absolute: bool) -> Self {
40 debug_assert!(epoch.full_value() & Self::FLAGS_MASK == 0);
41 Self::new(
42 epoch.full_value()
43 | Self::LOCK_BY_EPOCH_MASK
44 | (if absolute { 0 } else { Self::LOCK_TYPE_FLAG }),
45 )
46 }
47
48 pub fn new(v: u64) -> Self {
49 Since(v)
50 }
51
52 pub fn as_u64(self) -> u64 {
53 self.0
54 }
55
56 pub fn is_absolute(self) -> bool {
57 self.0 & Self::LOCK_TYPE_FLAG == 0
58 }
59
60 #[inline]
61 pub fn is_relative(self) -> bool {
62 !self.is_absolute()
63 }
64
65 pub fn flags_is_valid(self) -> bool {
66 (self.0 & Self::REMAIN_FLAGS_BITS == 0)
67 && ((self.0 & Self::METRIC_TYPE_FLAG_MASK) != Self::METRIC_TYPE_FLAG_MASK)
68 }
69
70 pub fn flags(self) -> u64 {
71 self.0 & Self::FLAGS_MASK
72 }
73
74 pub fn extract_lock_value(self) -> Option<LockValue> {
75 let value = self.0 & Self::VALUE_MASK;
76 match self.0 & Self::METRIC_TYPE_FLAG_MASK {
77 Self::LOCK_BY_BLOCK_NUMBER_MASK => Some(LockValue::BlockNumber(value)),
79 Self::LOCK_BY_EPOCH_MASK => Some(LockValue::EpochNumberWithFraction(
81 EpochNumberWithFraction::from_full_value(value),
82 )),
83 Self::LOCK_BY_TIMESTAMP_MASK => Some(LockValue::Timestamp(value * 1000)),
85 _ => None,
86 }
87 }
88
89 #[cfg(feature = "ckb-types")]
92 pub fn to_absolute_value(self, base_block: ckb_types::packed::Header) -> Option<Self> {
93 debug_assert!(self.is_relative());
94
95 let to_le_u64 = |v: &ckb_types::packed::Uint64| {
96 let mut tmp = [0u8; 8];
97 tmp.copy_from_slice(&v.raw_data());
98 u64::from_le_bytes(tmp)
99 };
100
101 match self.extract_lock_value() {
102 Some(LockValue::BlockNumber(number)) => to_le_u64(&base_block.raw().number())
103 .checked_add(number)
104 .and_then(|block_number| Self::from_block_number(block_number, true)),
105 Some(LockValue::Timestamp(timestamp)) => to_le_u64(&base_block.raw().timestamp())
106 .checked_add(timestamp)
107 .and_then(|timestamp| Self::from_timestamp(timestamp, true)),
108 Some(LockValue::EpochNumberWithFraction(epoch)) => {
109 let base_epoch = EpochNumberWithFraction::from_full_value(
110 to_le_u64(&base_block.raw().epoch()) & Self::VALUE_MASK,
111 );
112 Some(Self::from_epoch((epoch + base_epoch)?, true))
113 }
114 _ => None,
115 }
116 }
117}
118
119impl PartialOrd for Since {
120 fn partial_cmp(&self, other: &Since) -> Option<Ordering> {
121 if self.is_absolute() != other.is_absolute() {
125 return None;
126 }
127
128 match (self.extract_lock_value(), other.extract_lock_value()) {
129 (Some(LockValue::BlockNumber(a)), Some(LockValue::BlockNumber(b))) => a.partial_cmp(&b),
130 (
131 Some(LockValue::EpochNumberWithFraction(a)),
132 Some(LockValue::EpochNumberWithFraction(b)),
133 ) => a.partial_cmp(&b),
134 (Some(LockValue::Timestamp(a)), Some(LockValue::Timestamp(b))) => a.partial_cmp(&b),
135 _ => None,
136 }
137 }
138}
139
140pub enum LockValue {
141 BlockNumber(u64),
142 EpochNumberWithFraction(EpochNumberWithFraction),
143 Timestamp(u64),
144}
145
146impl LockValue {
147 pub fn block_number(&self) -> Option<u64> {
148 if let Self::BlockNumber(v) = self {
149 Some(*v)
150 } else {
151 None
152 }
153 }
154
155 pub fn epoch(&self) -> Option<EpochNumberWithFraction> {
156 if let Self::EpochNumberWithFraction(v) = self {
157 Some(*v)
158 } else {
159 None
160 }
161 }
162
163 pub fn timestamp(&self) -> Option<u64> {
164 if let Self::Timestamp(v) = self {
165 Some(*v)
166 } else {
167 None
168 }
169 }
170}
171
172#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
173pub struct EpochNumberWithFraction(u64);
174
175impl EpochNumberWithFraction {
176 pub const NUMBER_OFFSET: usize = 0;
177 pub const NUMBER_BITS: usize = 24;
178 pub const NUMBER_MAXIMUM_VALUE: u64 = (1u64 << Self::NUMBER_BITS);
179 pub const NUMBER_MASK: u64 = (Self::NUMBER_MAXIMUM_VALUE - 1);
180 pub const INDEX_OFFSET: usize = Self::NUMBER_BITS;
181 pub const INDEX_BITS: usize = 16;
182 pub const INDEX_MAXIMUM_VALUE: u64 = (1u64 << Self::INDEX_BITS);
183 pub const INDEX_MASK: u64 = (Self::INDEX_MAXIMUM_VALUE - 1);
184 pub const LENGTH_OFFSET: usize = Self::NUMBER_BITS + Self::INDEX_BITS;
185 pub const LENGTH_BITS: usize = 16;
186 pub const LENGTH_MAXIMUM_VALUE: u64 = (1u64 << Self::LENGTH_BITS);
187 pub const LENGTH_MASK: u64 = (Self::LENGTH_MAXIMUM_VALUE - 1);
188
189 pub fn create(number: u64, index: u64, length: u64) -> Option<EpochNumberWithFraction> {
190 if number < Self::NUMBER_MAXIMUM_VALUE
191 && index < Self::INDEX_MAXIMUM_VALUE
192 && length < Self::LENGTH_MAXIMUM_VALUE
193 && length > 0
194 && index < length
195 {
196 Some(Self::new_unchecked(number, index, length))
197 } else {
198 None
199 }
200 }
201
202 pub fn new(number: u64, index: u64, length: u64) -> EpochNumberWithFraction {
203 debug_assert!(number < Self::NUMBER_MAXIMUM_VALUE);
204 debug_assert!(index < Self::INDEX_MAXIMUM_VALUE);
205 debug_assert!(length < Self::LENGTH_MAXIMUM_VALUE);
206 debug_assert!(length > 0);
207 debug_assert!(index < length);
208 Self::new_unchecked(number, index, length)
209 }
210
211 pub const fn new_unchecked(number: u64, index: u64, length: u64) -> Self {
212 EpochNumberWithFraction(
213 (length << Self::LENGTH_OFFSET)
214 | (index << Self::INDEX_OFFSET)
215 | (number << Self::NUMBER_OFFSET),
216 )
217 }
218
219 pub fn number(self) -> u64 {
220 (self.0 >> Self::NUMBER_OFFSET) & Self::NUMBER_MASK
221 }
222
223 pub fn index(self) -> u64 {
224 (self.0 >> Self::INDEX_OFFSET) & Self::INDEX_MASK
225 }
226
227 pub fn length(self) -> u64 {
228 (self.0 >> Self::LENGTH_OFFSET) & Self::LENGTH_MASK
229 }
230
231 pub fn full_value(self) -> u64 {
232 self.0
233 }
234
235 pub fn from_full_value(value: u64) -> Self {
241 let epoch = Self(value);
242 if epoch.length() == 0 {
243 Self::new(epoch.number(), 0, 1)
244 } else {
245 epoch
246 }
247 }
248}
249
250impl PartialOrd for EpochNumberWithFraction {
251 fn partial_cmp(&self, other: &EpochNumberWithFraction) -> Option<Ordering> {
252 if self.number() < other.number() {
253 Some(Ordering::Less)
254 } else if self.number() > other.number() {
255 Some(Ordering::Greater)
256 } else {
257 let block_a = (self.index() as u128) * (other.length() as u128);
258 let block_b = (other.index() as u128) * (self.length() as u128);
259 block_a.partial_cmp(&block_b)
260 }
261 }
262}
263
264impl core::ops::Add for EpochNumberWithFraction {
265 type Output = Option<EpochNumberWithFraction>;
266
267 fn add(self, rhs: EpochNumberWithFraction) -> Self::Output {
268 let mut number = self.number().checked_add(rhs.number())?;
269
270 let mut numerator = ((self.index() as u128) * (rhs.length() as u128))
271 .checked_add((rhs.index() as u128) * (self.length() as u128))?;
272 let mut denominator = (self.length() as u128) * (rhs.length() as u128);
273 let divisor = gcd::binary_u128(numerator, denominator);
274 debug_assert!(numerator % divisor == 0);
275 debug_assert!(denominator % divisor == 0);
276 numerator /= divisor;
277 denominator /= divisor;
278
279 let full_epoches = u64::try_from(numerator / denominator).ok()?;
280 number = number.checked_add(full_epoches)?;
281 numerator %= denominator;
282
283 let numerator = u64::try_from(numerator).ok()?;
284 let denominator = u64::try_from(denominator).ok()?;
285
286 EpochNumberWithFraction::create(number, numerator, denominator)
287 }
288}