tp_runtime/generic/
era.rs1#[cfg(feature = "std")]
21use serde::{Serialize, Deserialize};
22
23use crate::codec::{Decode, Encode, Input, Output, Error};
24
25pub type Period = u64;
27
28pub type Phase = u64;
30
31#[derive(PartialEq, Eq, Clone, Copy, tet_core::RuntimeDebug)]
33#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
34pub enum Era {
35 Immortal,
37
38 Mortal(Period, Phase),
48}
49
50impl Era {
60 pub fn mortal(period: u64, current: u64) -> Self {
67 let period = period.checked_next_power_of_two()
68 .unwrap_or(1 << 16)
69 .max(4)
70 .min(1 << 16);
71 let phase = current % period;
72 let quantize_factor = (period >> 12).max(1);
73 let quantized_phase = phase / quantize_factor * quantize_factor;
74
75 Era::Mortal(period, quantized_phase)
76 }
77
78 pub fn immortal() -> Self {
80 Era::Immortal
81 }
82
83 pub fn is_immortal(&self) -> bool {
85 match self {
86 Era::Immortal => true,
87 _ => false,
88 }
89 }
90
91 pub fn birth(self, current: u64) -> u64 {
94 match self {
95 Era::Immortal => 0,
96 Era::Mortal(period, phase) => (current.max(phase) - phase) / period * period + phase,
97 }
98 }
99
100 pub fn death(self, current: u64) -> u64 {
102 match self {
103 Era::Immortal => u64::max_value(),
104 Era::Mortal(period, _) => self.birth(current) + period,
105 }
106 }
107}
108
109impl Encode for Era {
110 fn encode_to<T: Output + ?Sized>(&self, output: &mut T) {
111 match self {
112 Era::Immortal => output.push_byte(0),
113 Era::Mortal(period, phase) => {
114 let quantize_factor = (*period as u64 >> 12).max(1);
115 let encoded = (period.trailing_zeros() - 1).max(1).min(15) as u16 | ((phase / quantize_factor) << 4) as u16;
116 encoded.encode_to(output);
117 }
118 }
119 }
120}
121
122impl codec::EncodeLike for Era {}
123
124impl Decode for Era {
125 fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
126 let first = input.read_byte()?;
127 if first == 0 {
128 Ok(Era::Immortal)
129 } else {
130 let encoded = first as u64 + ((input.read_byte()? as u64) << 8);
131 let period = 2 << (encoded % (1 << 4));
132 let quantize_factor = (period >> 12).max(1);
133 let phase = (encoded >> 4) * quantize_factor;
134 if period >= 4 && phase < period {
135 Ok(Era::Mortal(period, phase))
136 } else {
137 Err("Invalid period and phase".into())
138 }
139 }
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn immortal_works() {
149 let e = Era::immortal();
150 assert_eq!(e.birth(0), 0);
151 assert_eq!(e.death(0), u64::max_value());
152 assert_eq!(e.birth(1), 0);
153 assert_eq!(e.death(1), u64::max_value());
154 assert_eq!(e.birth(u64::max_value()), 0);
155 assert_eq!(e.death(u64::max_value()), u64::max_value());
156 assert!(e.is_immortal());
157
158 assert_eq!(e.encode(), vec![0u8]);
159 assert_eq!(e, Era::decode(&mut&[0u8][..]).unwrap());
160 }
161
162 #[test]
163 fn mortal_codec_works() {
164 let e = Era::mortal(64, 42);
165 assert!(!e.is_immortal());
166
167 let expected = vec![5 + 42 % 16 * 16, 42 / 16];
168 assert_eq!(e.encode(), expected);
169 assert_eq!(e, Era::decode(&mut&expected[..]).unwrap());
170 }
171
172 #[test]
173 fn long_period_mortal_codec_works() {
174 let e = Era::mortal(32768, 20000);
175
176 let expected = vec![(14 + 2500 % 16 * 16) as u8, (2500 / 16) as u8];
177 assert_eq!(e.encode(), expected);
178 assert_eq!(e, Era::decode(&mut&expected[..]).unwrap());
179 }
180
181 #[test]
182 fn era_initialization_works() {
183 assert_eq!(Era::mortal(64, 42), Era::Mortal(64, 42));
184 assert_eq!(Era::mortal(32768, 20000), Era::Mortal(32768, 20000));
185 assert_eq!(Era::mortal(200, 513), Era::Mortal(256, 1));
186 assert_eq!(Era::mortal(2, 1), Era::Mortal(4, 1));
187 assert_eq!(Era::mortal(4, 5), Era::Mortal(4, 1));
188 }
189
190 #[test]
191 fn quantized_clamped_era_initialization_works() {
192 assert_eq!(Era::mortal(1000000, 1000001), Era::Mortal(65536, 1000001 % 65536 / 4 * 4));
194 }
195
196 #[test]
197 fn mortal_birth_death_works() {
198 let e = Era::mortal(4, 6);
199 for i in 6..10 {
200 assert_eq!(e.birth(i), 6);
201 assert_eq!(e.death(i), 10);
202 }
203
204 assert_ne!(e.birth(10), 6);
206 assert_ne!(e.birth(5), 6);
207 }
208
209 #[test]
210 fn current_less_than_phase() {
211 Era::mortal(4, 3).birth(1);
213 }
214}