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