1use std::fmt;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8pub enum ResourceKind {
9 ScratchBytes,
10 Lines,
11 Work,
12 States,
13 DecodedBytes,
14}
15
16impl ResourceKind {
17 pub fn as_str(self) -> &'static str {
18 match self {
19 Self::ScratchBytes => "scratch_bytes",
20 Self::Lines => "lines",
21 Self::Work => "work",
22 Self::States => "states",
23 Self::DecodedBytes => "decoded_bytes",
24 }
25 }
26}
27
28impl fmt::Display for ResourceKind {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 f.write_str(self.as_str())
31 }
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub struct BudgetExceeded {
37 pub kind: ResourceKind,
38 pub limit: u64,
39 pub needed: u64,
40}
41
42impl fmt::Display for BudgetExceeded {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 write!(
45 f,
46 "budget exceeded for {}: needed {}, limit {}",
47 self.kind, self.needed, self.limit
48 )
49 }
50}
51
52impl std::error::Error for BudgetExceeded {}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
56pub struct ResourceUsage {
57 pub scratch_bytes: u64,
58 pub lines: u64,
59 pub work: u64,
60 pub states: u64,
61 pub decoded_bytes: u64,
62}
63
64impl ResourceUsage {
65 pub fn get(self, kind: ResourceKind) -> u64 {
66 match kind {
67 ResourceKind::ScratchBytes => self.scratch_bytes,
68 ResourceKind::Lines => self.lines,
69 ResourceKind::Work => self.work,
70 ResourceKind::States => self.states,
71 ResourceKind::DecodedBytes => self.decoded_bytes,
72 }
73 }
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub struct ResourceBudget {
80 limits: ResourceUsage,
81 used: ResourceUsage,
82}
83
84impl ResourceBudget {
85 pub fn new(limits: ResourceUsage) -> Self {
86 Self {
87 limits,
88 used: ResourceUsage::default(),
89 }
90 }
91
92 pub fn unlimited() -> Self {
93 Self::new(ResourceUsage {
94 scratch_bytes: u64::MAX,
95 lines: u64::MAX,
96 work: u64::MAX,
97 states: u64::MAX,
98 decoded_bytes: u64::MAX,
99 })
100 }
101
102 pub fn limit(&self, kind: ResourceKind) -> u64 {
103 self.limits.get(kind)
104 }
105
106 pub fn used(&self) -> ResourceUsage {
107 self.used
108 }
109
110 pub fn require(&mut self, kind: ResourceKind, needed: u64) -> Result<(), BudgetExceeded> {
111 let limit = self.limit(kind);
112 if needed > limit {
113 return Err(BudgetExceeded {
114 kind,
115 limit,
116 needed,
117 });
118 }
119 if needed > self.used.get(kind) {
120 self.set_used(kind, needed);
121 }
122 Ok(())
123 }
124
125 pub fn consume(&mut self, kind: ResourceKind, amount: u64) -> Result<(), BudgetExceeded> {
126 let used = self.used.get(kind).saturating_add(amount);
127 self.require(kind, used)?;
128 Ok(())
129 }
130
131 pub fn record(&mut self, kind: ResourceKind, used: u64) {
132 self.set_used(kind, used);
133 }
134
135 fn set_used(&mut self, kind: ResourceKind, used: u64) {
136 match kind {
137 ResourceKind::ScratchBytes => self.used.scratch_bytes = used,
138 ResourceKind::Lines => self.used.lines = used,
139 ResourceKind::Work => self.used.work = used,
140 ResourceKind::States => self.used.states = used,
141 ResourceKind::DecodedBytes => self.used.decoded_bytes = used,
142 }
143 }
144}