Skip to main content

merman_render/svg/icon_registry/
limits.rs

1const KIB: u64 = 1024;
2const MIB: u64 = 1024 * KIB;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5#[non_exhaustive]
6pub enum IconRegistryResourceLimitId {
7    MaxPacks,
8    MaxPackBytes,
9    MaxInputBytes,
10    MaxJsonDepth,
11    MaxJsonMembers,
12    MaxJsonKeyBytes,
13    MaxPrefixBytes,
14    MaxNameBytes,
15    MaxBodyBytes,
16    MaxRetainedBodyBytes,
17    MaxIconEntries,
18    MaxAliasEntries,
19    MaxTotalEntries,
20    MaxAliasEdges,
21    MaxAliasDepth,
22    MaxAliasFanout,
23    MaxBuildWorkUnits,
24    MaxXmlElementsPerBody,
25    MaxXmlDepthPerBody,
26    MaxIdRewriteEditsPerBody,
27    MaxRetainedXmlPlanBytes,
28    MaxCoordinateMagnitude,
29}
30
31impl IconRegistryResourceLimitId {
32    pub const ALL: &'static [Self] = &[
33        Self::MaxPacks,
34        Self::MaxPackBytes,
35        Self::MaxInputBytes,
36        Self::MaxJsonDepth,
37        Self::MaxJsonMembers,
38        Self::MaxJsonKeyBytes,
39        Self::MaxPrefixBytes,
40        Self::MaxNameBytes,
41        Self::MaxBodyBytes,
42        Self::MaxRetainedBodyBytes,
43        Self::MaxIconEntries,
44        Self::MaxAliasEntries,
45        Self::MaxTotalEntries,
46        Self::MaxAliasEdges,
47        Self::MaxAliasDepth,
48        Self::MaxAliasFanout,
49        Self::MaxBuildWorkUnits,
50        Self::MaxXmlElementsPerBody,
51        Self::MaxXmlDepthPerBody,
52        Self::MaxIdRewriteEditsPerBody,
53        Self::MaxRetainedXmlPlanBytes,
54        Self::MaxCoordinateMagnitude,
55    ];
56
57    pub const fn stable_id(self) -> &'static str {
58        match self {
59            Self::MaxPacks => "max_icon_registry_packs",
60            Self::MaxPackBytes => "max_icon_pack_bytes",
61            Self::MaxInputBytes => "max_icon_registry_input_bytes",
62            Self::MaxJsonDepth => "max_icon_registry_json_depth",
63            Self::MaxJsonMembers => "max_icon_registry_json_members",
64            Self::MaxJsonKeyBytes => "max_icon_registry_json_key_bytes",
65            Self::MaxPrefixBytes => "max_icon_registry_prefix_bytes",
66            Self::MaxNameBytes => "max_icon_registry_name_bytes",
67            Self::MaxBodyBytes => "max_icon_body_bytes",
68            Self::MaxRetainedBodyBytes => "max_icon_registry_retained_body_bytes",
69            Self::MaxIconEntries => "max_icon_registry_icon_entries",
70            Self::MaxAliasEntries => "max_icon_registry_alias_entries",
71            Self::MaxTotalEntries => "max_icon_registry_entries",
72            Self::MaxAliasEdges => "max_icon_registry_alias_edges",
73            Self::MaxAliasDepth => "max_icon_registry_alias_depth",
74            Self::MaxAliasFanout => "max_icon_registry_alias_fanout",
75            Self::MaxBuildWorkUnits => "max_icon_registry_build_work_units",
76            Self::MaxXmlElementsPerBody => "max_icon_xml_elements_per_body",
77            Self::MaxXmlDepthPerBody => "max_icon_xml_depth_per_body",
78            Self::MaxIdRewriteEditsPerBody => "max_icon_id_rewrite_edits_per_body",
79            Self::MaxRetainedXmlPlanBytes => "max_icon_registry_retained_xml_plan_bytes",
80            Self::MaxCoordinateMagnitude => "max_icon_coordinate_magnitude",
81        }
82    }
83
84    pub const fn fixed_value(self) -> u64 {
85        match self {
86            Self::MaxPacks => 16,
87            Self::MaxPackBytes => 16 * MIB,
88            Self::MaxInputBytes => 32 * MIB,
89            Self::MaxJsonDepth => 32,
90            Self::MaxJsonMembers => 1_000_000,
91            Self::MaxJsonKeyBytes => 1_024,
92            Self::MaxPrefixBytes => 64,
93            Self::MaxNameBytes => 128,
94            Self::MaxBodyBytes => 256 * KIB,
95            Self::MaxRetainedBodyBytes => 32 * MIB,
96            Self::MaxIconEntries => 32_768,
97            Self::MaxAliasEntries => 32_768,
98            Self::MaxTotalEntries => 65_536,
99            Self::MaxAliasEdges => 32_768,
100            Self::MaxAliasDepth => 64,
101            Self::MaxAliasFanout => 1_024,
102            Self::MaxBuildWorkUnits => 4_000_000,
103            Self::MaxXmlElementsPerBody => 4_096,
104            // Leave structural headroom beneath the WASM whole-SVG backend cap for wrappers and
105            // diagram embedding. The final document validator remains authoritative.
106            Self::MaxXmlDepthPerBody => crate::resources::MAX_PORTABLE_ICON_BODY_XML_DEPTH as u64,
107            Self::MaxIdRewriteEditsPerBody => 16_384,
108            Self::MaxRetainedXmlPlanBytes => 16 * MIB,
109            Self::MaxCoordinateMagnitude => 1_000_000,
110        }
111    }
112
113    #[must_use]
114    pub const fn descriptor(self) -> &'static IconRegistryResourceLimitDescriptor {
115        &ICON_REGISTRY_RESOURCE_LIMIT_DESCRIPTORS[self as usize]
116    }
117}
118
119#[derive(Debug, Clone, Copy, PartialEq, Eq)]
120#[non_exhaustive]
121pub struct IconRegistryResourceLimitDescriptor {
122    pub id: IconRegistryResourceLimitId,
123    pub stable_id: &'static str,
124    pub phase: &'static str,
125    pub unit: &'static str,
126    pub description: &'static str,
127    pub default_value: u64,
128    pub hard_maximum: u64,
129    pub caller_configurable: bool,
130}
131
132const fn descriptor(
133    id: IconRegistryResourceLimitId,
134    phase: &'static str,
135    unit: &'static str,
136    description: &'static str,
137) -> IconRegistryResourceLimitDescriptor {
138    let value = id.fixed_value();
139    IconRegistryResourceLimitDescriptor {
140        id,
141        stable_id: id.stable_id(),
142        phase,
143        unit,
144        description,
145        default_value: value,
146        hard_maximum: value,
147        caller_configurable: false,
148    }
149}
150
151pub const ICON_REGISTRY_RESOURCE_LIMIT_DESCRIPTORS: &[IconRegistryResourceLimitDescriptor] = &[
152    descriptor(
153        IconRegistryResourceLimitId::MaxPacks,
154        "icon_registry_input",
155        "packs",
156        "Maximum IconifyJSON packs admitted by one immutable registry",
157    ),
158    descriptor(
159        IconRegistryResourceLimitId::MaxPackBytes,
160        "icon_registry_input",
161        "bytes",
162        "Maximum encoded bytes admitted from one IconifyJSON pack",
163    ),
164    descriptor(
165        IconRegistryResourceLimitId::MaxInputBytes,
166        "icon_registry_input",
167        "bytes",
168        "Maximum aggregate encoded bytes admitted by one registry",
169    ),
170    descriptor(
171        IconRegistryResourceLimitId::MaxJsonDepth,
172        "icon_registry_parse",
173        "levels",
174        "Maximum JSON nesting depth inspected while ingesting packs",
175    ),
176    descriptor(
177        IconRegistryResourceLimitId::MaxJsonMembers,
178        "icon_registry_parse",
179        "members",
180        "Maximum aggregate JSON map members and sequence items inspected",
181    ),
182    descriptor(
183        IconRegistryResourceLimitId::MaxJsonKeyBytes,
184        "icon_registry_parse",
185        "bytes",
186        "Maximum decoded bytes in any IconifyJSON object key, including unknown extensions",
187    ),
188    descriptor(
189        IconRegistryResourceLimitId::MaxPrefixBytes,
190        "icon_registry_parse",
191        "bytes",
192        "Maximum admitted Iconify prefix or registration-name bytes",
193    ),
194    descriptor(
195        IconRegistryResourceLimitId::MaxNameBytes,
196        "icon_registry_parse",
197        "bytes",
198        "Maximum admitted Iconify icon or alias name bytes",
199    ),
200    descriptor(
201        IconRegistryResourceLimitId::MaxBodyBytes,
202        "icon_registry_xml",
203        "bytes",
204        "Maximum decoded SVG fragment bytes retained for one direct icon",
205    ),
206    descriptor(
207        IconRegistryResourceLimitId::MaxRetainedBodyBytes,
208        "icon_registry_retain",
209        "bytes",
210        "Maximum aggregate decoded direct-icon SVG body bytes retained",
211    ),
212    descriptor(
213        IconRegistryResourceLimitId::MaxIconEntries,
214        "icon_registry_parse",
215        "entries",
216        "Maximum direct icon entries admitted by one registry",
217    ),
218    descriptor(
219        IconRegistryResourceLimitId::MaxAliasEntries,
220        "icon_registry_alias",
221        "entries",
222        "Maximum alias entries admitted by one registry",
223    ),
224    descriptor(
225        IconRegistryResourceLimitId::MaxTotalEntries,
226        "icon_registry_alias",
227        "entries",
228        "Maximum resolved direct icon and alias entries published",
229    ),
230    descriptor(
231        IconRegistryResourceLimitId::MaxAliasEdges,
232        "icon_registry_alias",
233        "edges",
234        "Maximum parent edges inspected by alias resolution",
235    ),
236    descriptor(
237        IconRegistryResourceLimitId::MaxAliasDepth,
238        "icon_registry_alias",
239        "levels",
240        "Maximum resolved alias parent-chain depth",
241    ),
242    descriptor(
243        IconRegistryResourceLimitId::MaxAliasFanout,
244        "icon_registry_alias",
245        "aliases",
246        "Maximum aliases that may directly reference one parent",
247    ),
248    descriptor(
249        IconRegistryResourceLimitId::MaxBuildWorkUnits,
250        "icon_registry_build",
251        "work_units",
252        "Maximum deterministic parse, XML, and alias-resolution work",
253    ),
254    descriptor(
255        IconRegistryResourceLimitId::MaxXmlElementsPerBody,
256        "icon_registry_xml",
257        "elements",
258        "Maximum XML elements in one direct icon SVG fragment",
259    ),
260    descriptor(
261        IconRegistryResourceLimitId::MaxXmlDepthPerBody,
262        "icon_registry_xml",
263        "levels",
264        "Maximum XML element nesting depth in one direct icon fragment",
265    ),
266    descriptor(
267        IconRegistryResourceLimitId::MaxIdRewriteEditsPerBody,
268        "icon_registry_xml",
269        "edits",
270        "Maximum XML ID declaration and reference rewrites planned for one icon body",
271    ),
272    descriptor(
273        IconRegistryResourceLimitId::MaxRetainedXmlPlanBytes,
274        "icon_registry_retain",
275        "bytes",
276        "Maximum aggregate retained XML rewrite and defs-plan bytes",
277    ),
278    descriptor(
279        IconRegistryResourceLimitId::MaxCoordinateMagnitude,
280        "icon_registry_geometry",
281        "coordinate_units",
282        "Maximum absolute view-box coordinate or positive dimension",
283    ),
284];
285
286pub const fn icon_registry_resource_limit_descriptors()
287-> &'static [IconRegistryResourceLimitDescriptor] {
288    ICON_REGISTRY_RESOURCE_LIMIT_DESCRIPTORS
289}
290
291#[derive(Debug, Clone, Copy)]
292pub(super) struct IconRegistryBuildLimits {
293    pub max_packs: usize,
294    pub max_pack_bytes: usize,
295    pub max_input_bytes: usize,
296    pub max_json_depth: usize,
297    pub max_json_members: usize,
298    pub max_json_key_bytes: usize,
299    pub max_prefix_bytes: usize,
300    pub max_name_bytes: usize,
301    pub max_body_bytes: usize,
302    pub max_retained_body_bytes: usize,
303    pub max_icon_entries: usize,
304    pub max_alias_entries: usize,
305    pub max_total_entries: usize,
306    pub max_alias_edges: usize,
307    pub max_alias_depth: usize,
308    pub max_alias_fanout: usize,
309    pub max_build_work_units: usize,
310    pub max_xml_elements_per_body: usize,
311    pub max_xml_depth_per_body: usize,
312    pub max_id_rewrite_edits_per_body: usize,
313    pub max_retained_xml_plan_bytes: usize,
314    pub max_coordinate_magnitude: f64,
315}
316
317impl IconRegistryBuildLimits {
318    pub const fn fixed() -> Self {
319        Self {
320            max_packs: IconRegistryResourceLimitId::MaxPacks.fixed_value() as usize,
321            max_pack_bytes: IconRegistryResourceLimitId::MaxPackBytes.fixed_value() as usize,
322            max_input_bytes: IconRegistryResourceLimitId::MaxInputBytes.fixed_value() as usize,
323            max_json_depth: IconRegistryResourceLimitId::MaxJsonDepth.fixed_value() as usize,
324            max_json_members: IconRegistryResourceLimitId::MaxJsonMembers.fixed_value() as usize,
325            max_json_key_bytes: IconRegistryResourceLimitId::MaxJsonKeyBytes.fixed_value() as usize,
326            max_prefix_bytes: IconRegistryResourceLimitId::MaxPrefixBytes.fixed_value() as usize,
327            max_name_bytes: IconRegistryResourceLimitId::MaxNameBytes.fixed_value() as usize,
328            max_body_bytes: IconRegistryResourceLimitId::MaxBodyBytes.fixed_value() as usize,
329            max_retained_body_bytes: IconRegistryResourceLimitId::MaxRetainedBodyBytes.fixed_value()
330                as usize,
331            max_icon_entries: IconRegistryResourceLimitId::MaxIconEntries.fixed_value() as usize,
332            max_alias_entries: IconRegistryResourceLimitId::MaxAliasEntries.fixed_value() as usize,
333            max_total_entries: IconRegistryResourceLimitId::MaxTotalEntries.fixed_value() as usize,
334            max_alias_edges: IconRegistryResourceLimitId::MaxAliasEdges.fixed_value() as usize,
335            max_alias_depth: IconRegistryResourceLimitId::MaxAliasDepth.fixed_value() as usize,
336            max_alias_fanout: IconRegistryResourceLimitId::MaxAliasFanout.fixed_value() as usize,
337            max_build_work_units: IconRegistryResourceLimitId::MaxBuildWorkUnits.fixed_value()
338                as usize,
339            max_xml_elements_per_body: IconRegistryResourceLimitId::MaxXmlElementsPerBody
340                .fixed_value() as usize,
341            max_xml_depth_per_body: IconRegistryResourceLimitId::MaxXmlDepthPerBody.fixed_value()
342                as usize,
343            max_id_rewrite_edits_per_body: IconRegistryResourceLimitId::MaxIdRewriteEditsPerBody
344                .fixed_value() as usize,
345            max_retained_xml_plan_bytes: IconRegistryResourceLimitId::MaxRetainedXmlPlanBytes
346                .fixed_value() as usize,
347            max_coordinate_magnitude: IconRegistryResourceLimitId::MaxCoordinateMagnitude
348                .fixed_value() as f64,
349        }
350    }
351}