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 #[deprecated(note = "Use `consume` instead. Will be removed after December 2023.")]
123 pub fn defensive_saturating_accrue(&mut self, w: Weight) {
124 self.consume(w);
125 }
126
127 pub fn consume(&mut self, w: Weight) {
129 self.consumed.saturating_accrue(w);
130 debug_assert!(self.consumed.all_lte(self.limit), "Weight counter overflow");
131 }
132
133 pub fn try_consume(&mut self, w: Weight) -> Result<(), ()> {
137 self.consumed.checked_add(&w).map_or(Err(()), |test| {
138 if test.any_gt(self.limit) {
139 Err(())
140 } else {
141 self.consumed = test;
142 Ok(())
143 }
144 })
145 }
146
147 pub fn can_consume(&self, w: Weight) -> bool {
149 self.consumed.checked_add(&w).map_or(false, |t| t.all_lte(self.limit))
150 }
151
152 pub fn reclaim_proof_size(&mut self, s: u64) {
154 self.consumed.saturating_reduce(Weight::from_parts(0, s));
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use crate::*;
161 use sp_arithmetic::traits::Zero;
162
163 #[test]
164 fn weight_meter_remaining_works() {
165 let mut meter = WeightMeter::with_limit(Weight::from_parts(10, 20));
166
167 assert_eq!(meter.try_consume(Weight::from_parts(5, 0)), Ok(()));
168 assert_eq!(meter.consumed, Weight::from_parts(5, 0));
169 assert_eq!(meter.remaining(), Weight::from_parts(5, 20));
170
171 assert_eq!(meter.try_consume(Weight::from_parts(2, 10)), Ok(()));
172 assert_eq!(meter.consumed, Weight::from_parts(7, 10));
173 assert_eq!(meter.remaining(), Weight::from_parts(3, 10));
174
175 assert_eq!(meter.try_consume(Weight::from_parts(3, 10)), Ok(()));
176 assert_eq!(meter.consumed, Weight::from_parts(10, 20));
177 assert_eq!(meter.remaining(), Weight::from_parts(0, 0));
178 }
179
180 #[test]
181 fn weight_meter_can_consume_works() {
182 let meter = WeightMeter::with_limit(Weight::from_parts(1, 1));
183
184 assert!(meter.can_consume(Weight::from_parts(0, 0)));
185 assert!(meter.can_consume(Weight::from_parts(1, 1)));
186 assert!(!meter.can_consume(Weight::from_parts(0, 2)));
187 assert!(!meter.can_consume(Weight::from_parts(2, 0)));
188 assert!(!meter.can_consume(Weight::from_parts(2, 2)));
189 }
190
191 #[test]
192 fn weight_meter_try_consume_works() {
193 let mut meter = WeightMeter::with_limit(Weight::from_parts(2, 2));
194
195 assert_eq!(meter.try_consume(Weight::from_parts(0, 0)), Ok(()));
196 assert_eq!(meter.try_consume(Weight::from_parts(1, 1)), Ok(()));
197 assert_eq!(meter.try_consume(Weight::from_parts(0, 2)), Err(()));
198 assert_eq!(meter.try_consume(Weight::from_parts(2, 0)), Err(()));
199 assert_eq!(meter.try_consume(Weight::from_parts(2, 2)), Err(()));
200 assert_eq!(meter.try_consume(Weight::from_parts(0, 1)), Ok(()));
201 assert_eq!(meter.try_consume(Weight::from_parts(1, 0)), Ok(()));
202 }
203
204 #[test]
205 fn weight_meter_check_and_can_consume_works() {
206 let mut meter = WeightMeter::new();
207
208 assert!(meter.can_consume(Weight::from_parts(u64::MAX, 0)));
209 assert_eq!(meter.try_consume(Weight::from_parts(u64::MAX, 0)), Ok(()));
210
211 assert!(meter.can_consume(Weight::from_parts(0, u64::MAX)));
212 assert_eq!(meter.try_consume(Weight::from_parts(0, u64::MAX)), Ok(()));
213
214 assert!(!meter.can_consume(Weight::from_parts(0, 1)));
215 assert_eq!(meter.try_consume(Weight::from_parts(0, 1)), Err(()));
216
217 assert!(!meter.can_consume(Weight::from_parts(1, 0)));
218 assert_eq!(meter.try_consume(Weight::from_parts(1, 0)), Err(()));
219
220 assert!(meter.can_consume(Weight::zero()));
221 assert_eq!(meter.try_consume(Weight::zero()), Ok(()));
222 }
223
224 #[test]
225 fn consumed_ratio_works() {
226 let mut meter = WeightMeter::with_limit(Weight::from_parts(10, 20));
227
228 assert_eq!(meter.try_consume(Weight::from_parts(5, 0)), Ok(()));
229 assert_eq!(meter.consumed_ratio(), Perbill::from_percent(50));
230 assert_eq!(meter.try_consume(Weight::from_parts(0, 12)), Ok(()));
231 assert_eq!(meter.consumed_ratio(), Perbill::from_percent(60));
232
233 assert_eq!(meter.try_consume(Weight::from_parts(2, 0)), Ok(()));
234 assert_eq!(meter.consumed_ratio(), Perbill::from_percent(70));
235 assert_eq!(meter.try_consume(Weight::from_parts(0, 4)), Ok(()));
236 assert_eq!(meter.consumed_ratio(), Perbill::from_percent(80));
237
238 assert_eq!(meter.try_consume(Weight::from_parts(3, 0)), Ok(()));
239 assert_eq!(meter.consumed_ratio(), Perbill::from_percent(100));
240 assert_eq!(meter.try_consume(Weight::from_parts(0, 4)), Ok(()));
241 assert_eq!(meter.consumed_ratio(), Perbill::from_percent(100));
242 }
243
244 #[test]
245 fn try_consume_works() {
246 let mut meter = WeightMeter::with_limit(Weight::from_parts(10, 0));
247
248 assert!(meter.try_consume(Weight::from_parts(11, 0)).is_err());
249 assert!(meter.consumed().is_zero(), "No modification");
250
251 assert!(meter.try_consume(Weight::from_parts(9, 0)).is_ok());
252 assert!(meter.try_consume(Weight::from_parts(2, 0)).is_err());
253 assert!(meter.try_consume(Weight::from_parts(1, 0)).is_ok());
254 assert!(meter.remaining().is_zero());
255 assert_eq!(meter.consumed(), Weight::from_parts(10, 0));
256 }
257
258 #[test]
259 fn can_consume_works() {
260 let mut meter = WeightMeter::with_limit(Weight::from_parts(10, 0));
261
262 assert!(!meter.can_consume(Weight::from_parts(11, 0)));
263 assert!(meter.consumed().is_zero(), "No modification");
264
265 assert!(meter.can_consume(Weight::from_parts(9, 0)));
266 meter.consume(Weight::from_parts(9, 0));
267 assert!(!meter.can_consume(Weight::from_parts(2, 0)));
268 assert!(meter.can_consume(Weight::from_parts(1, 0)));
269 }
270
271 #[test]
272 #[cfg(debug_assertions)]
273 fn consume_works() {
274 let mut meter = WeightMeter::with_limit(Weight::from_parts(5, 10));
275
276 meter.consume(Weight::from_parts(4, 0));
277 assert_eq!(meter.remaining(), Weight::from_parts(1, 10));
278 meter.consume(Weight::from_parts(1, 0));
279 assert_eq!(meter.remaining(), Weight::from_parts(0, 10));
280 meter.consume(Weight::from_parts(0, 10));
281 assert_eq!(meter.consumed(), Weight::from_parts(5, 10));
282 }
283
284 #[test]
285 #[cfg(debug_assertions)]
286 fn reclaim_works() {
287 let mut meter = WeightMeter::with_limit(Weight::from_parts(5, 10));
288
289 meter.consume(Weight::from_parts(5, 10));
290 assert_eq!(meter.consumed(), Weight::from_parts(5, 10));
291
292 meter.reclaim_proof_size(3);
293 assert_eq!(meter.consumed(), Weight::from_parts(5, 7));
294
295 meter.reclaim_proof_size(10);
296 assert_eq!(meter.consumed(), Weight::from_parts(5, 0));
297 }
298
299 #[test]
300 #[cfg(debug_assertions)]
301 #[should_panic(expected = "Weight counter overflow")]
302 fn consume_defensive_fail() {
303 let mut meter = WeightMeter::with_limit(Weight::from_parts(10, 0));
304 let _ = meter.consume(Weight::from_parts(11, 0));
305 }
306}