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