sim_expr_tree_core/
policy.rs1use sim_codec::{DecodeLimits, DecodePosition};
2use sim_kernel::EncodePosition;
3
4pub const HARD_MAX_FACE_BYTES: usize = 8 * 1024 * 1024;
6pub const HARD_MAX_FACE_DEPTH: usize = 512;
8pub const HARD_MAX_FACE_ITEMS: usize = 200_000;
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq)]
17pub struct FaceBudget {
18 max_bytes: usize,
19 max_depth: usize,
20 max_items: usize,
21}
22
23impl FaceBudget {
24 #[must_use]
26 pub const fn new(max_bytes: usize, max_depth: usize, max_items: usize) -> Self {
27 Self {
28 max_bytes: if max_bytes > HARD_MAX_FACE_BYTES {
29 HARD_MAX_FACE_BYTES
30 } else {
31 max_bytes
32 },
33 max_depth: if max_depth > HARD_MAX_FACE_DEPTH {
34 HARD_MAX_FACE_DEPTH
35 } else {
36 max_depth
37 },
38 max_items: if max_items > HARD_MAX_FACE_ITEMS {
39 HARD_MAX_FACE_ITEMS
40 } else {
41 max_items
42 },
43 }
44 }
45
46 #[must_use]
48 pub const fn max_bytes(self) -> usize {
49 self.max_bytes
50 }
51
52 #[must_use]
54 pub const fn max_depth(self) -> usize {
55 self.max_depth
56 }
57
58 #[must_use]
60 pub const fn max_items(self) -> usize {
61 self.max_items
62 }
63}
64
65impl Default for FaceBudget {
66 fn default() -> Self {
67 Self::new(256 * 1024, 128, 16_384)
68 }
69}
70
71#[derive(Clone, Debug, Default, PartialEq, Eq)]
77pub struct CodecPolicyPatch {
78 pub source_codec: Option<Option<String>>,
80 pub source_position: Option<DecodePosition>,
82 pub decode_limits: Option<DecodeLimits>,
84 pub source_budget: Option<FaceBudget>,
86 pub result_codec: Option<Option<String>>,
88 pub result_position: Option<EncodePosition>,
90 pub result_budget: Option<FaceBudget>,
92}
93
94impl CodecPolicyPatch {
95 #[must_use]
97 pub fn empty() -> Self {
98 Self::default()
99 }
100
101 #[must_use]
103 pub fn set_codec(codec: impl Into<String>) -> Self {
104 let codec = codec.into();
105 Self {
106 source_codec: Some(Some(codec.clone())),
107 result_codec: Some(Some(codec)),
108 ..Self::default()
109 }
110 }
111
112 #[must_use]
114 pub fn clear_codec() -> Self {
115 Self {
116 source_codec: Some(None),
117 result_codec: Some(None),
118 ..Self::default()
119 }
120 }
121
122 #[must_use]
124 pub fn source_codec(codec: impl Into<String>) -> Self {
125 Self {
126 source_codec: Some(Some(codec.into())),
127 ..Self::default()
128 }
129 }
130
131 #[must_use]
133 pub fn result_codec(codec: impl Into<String>) -> Self {
134 Self {
135 result_codec: Some(Some(codec.into())),
136 ..Self::default()
137 }
138 }
139
140 pub(crate) fn apply_to(&self, effective: &mut EffectiveCodecPolicy) {
141 if let Some(codec) = &self.source_codec {
142 effective.source_codec = codec.clone();
143 }
144 if let Some(position) = self.source_position {
145 effective.source_position = position;
146 }
147 if let Some(limits) = self.decode_limits {
148 effective.decode_limits = bound_decode_limits(limits);
149 }
150 if let Some(budget) = self.source_budget {
151 effective.source_budget =
152 FaceBudget::new(budget.max_bytes(), budget.max_depth(), budget.max_items());
153 }
154 if let Some(codec) = &self.result_codec {
155 effective.result_codec = codec.clone();
156 }
157 if let Some(position) = self.result_position {
158 effective.result_position = position;
159 }
160 if let Some(budget) = self.result_budget {
161 effective.result_budget =
162 FaceBudget::new(budget.max_bytes(), budget.max_depth(), budget.max_items());
163 }
164 }
165}
166
167#[derive(Clone, Debug, PartialEq, Eq)]
169pub struct EffectiveCodecPolicy {
170 source_codec: Option<String>,
171 source_position: DecodePosition,
172 decode_limits: DecodeLimits,
173 source_budget: FaceBudget,
174 result_codec: Option<String>,
175 result_position: EncodePosition,
176 result_budget: FaceBudget,
177}
178
179impl EffectiveCodecPolicy {
180 #[must_use]
182 pub fn empty() -> Self {
183 Self::default()
184 }
185
186 #[must_use]
189 pub fn derive(patches: impl IntoIterator<Item = CodecPolicyPatch>) -> Self {
190 let mut effective = Self::default();
191 for patch in patches {
192 patch.apply_to(&mut effective);
193 }
194 effective
195 }
196
197 #[must_use]
199 pub fn source_codec(&self) -> Option<&str> {
200 self.source_codec.as_deref()
201 }
202
203 #[must_use]
205 pub const fn source_position(&self) -> DecodePosition {
206 self.source_position
207 }
208
209 #[must_use]
211 pub const fn decode_limits(&self) -> DecodeLimits {
212 self.decode_limits
213 }
214
215 #[must_use]
217 pub const fn source_budget(&self) -> FaceBudget {
218 self.source_budget
219 }
220
221 #[must_use]
223 pub fn result_codec(&self) -> Option<&str> {
224 self.result_codec.as_deref()
225 }
226
227 #[must_use]
229 pub const fn result_position(&self) -> EncodePosition {
230 self.result_position
231 }
232
233 #[must_use]
235 pub const fn result_budget(&self) -> FaceBudget {
236 self.result_budget
237 }
238}
239
240impl Default for EffectiveCodecPolicy {
241 fn default() -> Self {
242 Self {
243 source_codec: None,
244 source_position: DecodePosition::Data,
245 decode_limits: DecodeLimits::default(),
246 source_budget: FaceBudget::default(),
247 result_codec: None,
248 result_position: EncodePosition::Data,
249 result_budget: FaceBudget::default(),
250 }
251 }
252}
253
254fn bound_decode_limits(requested: DecodeLimits) -> DecodeLimits {
255 let hard = DecodeLimits::default();
256 DecodeLimits {
257 max_input_bytes: requested.max_input_bytes.min(hard.max_input_bytes),
258 max_tokens: requested.max_tokens.min(hard.max_tokens),
259 max_expr_nodes: requested.max_expr_nodes.min(hard.max_expr_nodes),
260 max_depth: requested.max_depth.min(hard.max_depth),
261 max_string_bytes: requested.max_string_bytes.min(hard.max_string_bytes),
262 max_blob_bytes: requested.max_blob_bytes.min(hard.max_blob_bytes),
263 max_collection_len: requested.max_collection_len.min(hard.max_collection_len),
264 max_trivia_items: requested.max_trivia_items.min(hard.max_trivia_items),
265 }
266}