1use super::Weight;
21
22use sp_arithmetic::Perbill;
23
24#[derive(Debug, Clone)]
44pub struct WeightMeter {
45 consumed: Weight,
47
48 limit: Weight,
50}
51
52impl WeightMeter {
53 pub fn with_consumed_and_limit(consumed: Weight, limit: Weight) -> Self {
55 Self { consumed, limit }
56 }
57
58 pub fn with_limit(limit: Weight) -> Self {
60 Self { consumed: Weight::zero(), limit }
61 }
62
63 pub fn new() -> Self {
65 Self::with_limit(Weight::MAX)
66 }
67
68 pub fn limit_to(self, weight: Weight) -> Self {
72 Self::with_limit(self.remaining().min(weight))
73 }
74
75 pub fn consumed(&self) -> Weight {
77 self.consumed
78 }
79
80 pub fn limit(&self) -> Weight {
82 self.limit
83 }
84
85 pub fn remaining(&self) -> Weight {
87 self.limit.saturating_sub(self.consumed)
88 }
89
90 pub fn consumed_ratio(&self) -> Perbill {
116 let time = Perbill::from_rational(self.consumed.ref_time(), self.limit.ref_time());
117 let pov = Perbill::from_rational(self.consumed.proof_size(), self.limit.proof_size());
118 time.max(pov)
119 }
120
121 pub fn consume(&mut self, w: Weight) {
123 self.consumed.saturating_accrue(w);
124 debug_assert!(self.consumed.all_lte(self.limit), "Weight counter overflow");
125 }
126
127 pub fn try_consume(&mut self, w: Weight) -> Result<(), ()> {
131 self.consumed.checked_add(&w).map_or(Err(()), |test| {
132 if test.any_gt(self.limit) {
133 Err(())
134 } else {
135 self.consumed = test;
136 Ok(())
137 }
138 })
139 }
140
141 pub fn can_consume(&self, w: Weight) -> bool {
143 self.consumed.checked_add(&w).map_or(false, |t| t.all_lte(self.limit))
144 }
145
146 pub fn reclaim_proof_size(&mut self, s: u64) {
148 self.consumed.saturating_reduce(Weight::from_parts(0, s));
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use crate::*;
155 use sp_arithmetic::traits::Zero;
156
157 #[test]
158 fn weight_meter_remaining_works() {
159 let mut meter = WeightMeter::with_limit(Weight::from_parts(10, 20));
160
161 assert_eq!(meter.try_consume(Weight::from_parts(5, 0)), Ok(()));
162 assert_eq!(meter.consumed, Weight::from_parts(5, 0));
163 assert_eq!(meter.remaining(), Weight::from_parts(5, 20));
164
165 assert_eq!(meter.try_consume(Weight::from_parts(2, 10)), Ok(()));
166 assert_eq!(meter.consumed, Weight::from_parts(7, 10));
167 assert_eq!(meter.remaining(), Weight::from_parts(3, 10));
168
169 assert_eq!(meter.try_consume(Weight::from_parts(3, 10)), Ok(()));
170 assert_eq!(meter.consumed, Weight::from_parts(10, 20));
171 assert_eq!(meter.remaining(), Weight::from_parts(0, 0));
172 }
173
174 #[test]
175 fn weight_meter_can_consume_works() {
176 let meter = WeightMeter::with_limit(Weight::from_parts(1, 1));
177
178 assert!(meter.can_consume(Weight::from_parts(0, 0)));
179 assert!(meter.can_consume(Weight::from_parts(1, 1)));
180 assert!(!meter.can_consume(Weight::from_parts(0, 2)));
181 assert!(!meter.can_consume(Weight::from_parts(2, 0)));
182 assert!(!meter.can_consume(Weight::from_parts(2, 2)));
183 }
184
185 #[test]
186 fn weight_meter_try_consume_works() {
187 let mut meter = WeightMeter::with_limit(Weight::from_parts(2, 2));
188
189 assert_eq!(meter.try_consume(Weight::from_parts(0, 0)), Ok(()));
190 assert_eq!(meter.try_consume(Weight::from_parts(1, 1)), Ok(()));
191 assert_eq!(meter.try_consume(Weight::from_parts(0, 2)), Err(()));
192 assert_eq!(meter.try_consume(Weight::from_parts(2, 0)), Err(()));
193 assert_eq!(meter.try_consume(Weight::from_parts(2, 2)), Err(()));
194 assert_eq!(meter.try_consume(Weight::from_parts(0, 1)), Ok(()));
195 assert_eq!(meter.try_consume(Weight::from_parts(1, 0)), Ok(()));
196 }
197
198 #[test]
199 fn weight_meter_check_and_can_consume_works() {
200 let mut meter = WeightMeter::new();
201
202 assert!(meter.can_consume(Weight::from_parts(u64::MAX, 0)));
203 assert_eq!(meter.try_consume(Weight::from_parts(u64::MAX, 0)), Ok(()));
204
205 assert!(meter.can_consume(Weight::from_parts(0, u64::MAX)));
206 assert_eq!(meter.try_consume(Weight::from_parts(0, u64::MAX)), Ok(()));
207
208 assert!(!meter.can_consume(Weight::from_parts(0, 1)));
209 assert_eq!(meter.try_consume(Weight::from_parts(0, 1)), Err(()));
210
211 assert!(!meter.can_consume(Weight::from_parts(1, 0)));
212 assert_eq!(meter.try_consume(Weight::from_parts(1, 0)), Err(()));
213
214 assert!(meter.can_consume(Weight::zero()));
215 assert_eq!(meter.try_consume(Weight::zero()), Ok(()));
216 }
217
218 #[test]
219 fn consumed_ratio_works() {
220 let mut meter = WeightMeter::with_limit(Weight::from_parts(10, 20));
221
222 assert_eq!(meter.try_consume(Weight::from_parts(5, 0)), Ok(()));
223 assert_eq!(meter.consumed_ratio(), Perbill::from_percent(50));
224 assert_eq!(meter.try_consume(Weight::from_parts(0, 12)), Ok(()));
225 assert_eq!(meter.consumed_ratio(), Perbill::from_percent(60));
226
227 assert_eq!(meter.try_consume(Weight::from_parts(2, 0)), Ok(()));
228 assert_eq!(meter.consumed_ratio(), Perbill::from_percent(70));
229 assert_eq!(meter.try_consume(Weight::from_parts(0, 4)), Ok(()));
230 assert_eq!(meter.consumed_ratio(), Perbill::from_percent(80));
231
232 assert_eq!(meter.try_consume(Weight::from_parts(3, 0)), Ok(()));
233 assert_eq!(meter.consumed_ratio(), Perbill::from_percent(100));
234 assert_eq!(meter.try_consume(Weight::from_parts(0, 4)), Ok(()));
235 assert_eq!(meter.consumed_ratio(), Perbill::from_percent(100));
236 }
237
238 #[test]
239 fn try_consume_works() {
240 let mut meter = WeightMeter::with_limit(Weight::from_parts(10, 0));
241
242 assert!(meter.try_consume(Weight::from_parts(11, 0)).is_err());
243 assert!(meter.consumed().is_zero(), "No modification");
244
245 assert!(meter.try_consume(Weight::from_parts(9, 0)).is_ok());
246 assert!(meter.try_consume(Weight::from_parts(2, 0)).is_err());
247 assert!(meter.try_consume(Weight::from_parts(1, 0)).is_ok());
248 assert!(meter.remaining().is_zero());
249 assert_eq!(meter.consumed(), Weight::from_parts(10, 0));
250 }
251
252 #[test]
253 fn can_consume_works() {
254 let mut meter = WeightMeter::with_limit(Weight::from_parts(10, 0));
255
256 assert!(!meter.can_consume(Weight::from_parts(11, 0)));
257 assert!(meter.consumed().is_zero(), "No modification");
258
259 assert!(meter.can_consume(Weight::from_parts(9, 0)));
260 meter.consume(Weight::from_parts(9, 0));
261 assert!(!meter.can_consume(Weight::from_parts(2, 0)));
262 assert!(meter.can_consume(Weight::from_parts(1, 0)));
263 }
264
265 #[test]
266 #[cfg(debug_assertions)]
267 fn consume_works() {
268 let mut meter = WeightMeter::with_limit(Weight::from_parts(5, 10));
269
270 meter.consume(Weight::from_parts(4, 0));
271 assert_eq!(meter.remaining(), Weight::from_parts(1, 10));
272 meter.consume(Weight::from_parts(1, 0));
273 assert_eq!(meter.remaining(), Weight::from_parts(0, 10));
274 meter.consume(Weight::from_parts(0, 10));
275 assert_eq!(meter.consumed(), Weight::from_parts(5, 10));
276 }
277
278 #[test]
279 #[cfg(debug_assertions)]
280 fn reclaim_works() {
281 let mut meter = WeightMeter::with_limit(Weight::from_parts(5, 10));
282
283 meter.consume(Weight::from_parts(5, 10));
284 assert_eq!(meter.consumed(), Weight::from_parts(5, 10));
285
286 meter.reclaim_proof_size(3);
287 assert_eq!(meter.consumed(), Weight::from_parts(5, 7));
288
289 meter.reclaim_proof_size(10);
290 assert_eq!(meter.consumed(), Weight::from_parts(5, 0));
291 }
292
293 #[test]
294 #[cfg(debug_assertions)]
295 #[should_panic(expected = "Weight counter overflow")]
296 fn consume_defensive_fail() {
297 let mut meter = WeightMeter::with_limit(Weight::from_parts(10, 0));
298 let _ = meter.consume(Weight::from_parts(11, 0));
299 }
300}