1use std::fmt;
3use structfs_core_store::{CodecErrorKind as Kind, CodecOperation, Error, Format, Value};
4
5#[derive(Debug, Clone)]
8pub struct Limits {
9 pub max_input_bytes: usize,
10 pub max_output_bytes: usize,
11 pub max_depth: usize,
12 pub max_nodes: usize,
13 pub max_collection_entries: usize,
14 pub max_string_bytes: usize,
15 pub max_blob_bytes: usize,
16 pub max_payload_bytes: usize,
17 pub max_allocation_bytes: usize,
18 pub max_work: usize,
19 pub max_diagnostic_bytes: usize,
20}
21impl Default for Limits {
22 fn default() -> Self {
23 Self {
24 max_input_bytes: 16 << 20,
25 max_output_bytes: 16 << 20,
26 max_depth: 64,
27 max_nodes: 262144,
28 max_collection_entries: 65536,
29 max_string_bytes: 4 << 20,
30 max_blob_bytes: 8 << 20,
31 max_payload_bytes: 16 << 20,
32 max_allocation_bytes: 64 << 20,
33 max_work: 128 << 20,
34 max_diagnostic_bytes: 256,
35 }
36 }
37}
38
39#[derive(Debug)]
40pub(crate) struct Failure(pub Kind);
41pub(crate) type Result<T> = std::result::Result<T, Failure>;
42impl fmt::Display for Failure {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 write!(f, "{:?}", self.0)
45 }
46}
47impl std::error::Error for Failure {}
48impl serde::ser::Error for Failure {
49 fn custom<T: fmt::Display>(_: T) -> Self {
50 Self(Kind::TypeMismatch)
51 }
52}
53impl serde::de::Error for Failure {
54 fn custom<T: fmt::Display>(_: T) -> Self {
55 Self(Kind::TypeMismatch)
56 }
57}
58impl Failure {
59 pub(crate) fn core(self, format: &Format, operation: CodecOperation, limits: &Limits) -> Error {
60 let mut message = format!("{:?}", self.0);
61 message.truncate(limits.max_diagnostic_bytes.min(message.len()));
62 Error::Codec {
63 kind: self.0,
64 operation,
65 format: format.clone(),
66 message,
67 }
68 }
69}
70pub(crate) fn ensure(ok: bool, kind: Kind) -> Result<()> {
71 if ok {
72 Ok(())
73 } else {
74 Err(Failure(kind))
75 }
76}
77pub(crate) struct Budget<'a> {
78 pub limits: &'a Limits,
79 nodes: usize,
80 payload: usize,
81 allocation: usize,
82 work: usize,
83}
84fn add(counter: &mut usize, n: usize, max: usize) -> Result<()> {
85 *counter = counter.checked_add(n).ok_or(Failure(Kind::ResourceLimit))?;
86 ensure(*counter <= max, Kind::ResourceLimit)
87}
88impl<'a> Budget<'a> {
89 pub fn new(limits: &'a Limits) -> Self {
90 Self {
91 limits,
92 nodes: 0,
93 payload: 0,
94 allocation: 0,
95 work: 0,
96 }
97 }
98 pub fn work(&mut self, n: usize) -> Result<()> {
99 add(&mut self.work, n, self.limits.max_work)
100 }
101 pub fn allocate(&mut self, n: usize) -> Result<()> {
102 add(&mut self.allocation, n, self.limits.max_allocation_bytes)
103 }
104 pub fn node(&mut self, depth: usize) -> Result<()> {
105 ensure(depth <= self.limits.max_depth.min(256), Kind::ResourceLimit)?;
106 add(&mut self.nodes, 1, self.limits.max_nodes)?;
107 self.allocate(128)?;
108 self.work(1)
109 }
110 pub fn entries(&mut self, n: usize) -> Result<()> {
111 ensure(n <= self.limits.max_collection_entries, Kind::ResourceLimit)?;
112 self.work(1)
113 }
114 pub fn payload(&mut self, n: usize, blob: bool) -> Result<()> {
115 ensure(
116 n <= if blob {
117 self.limits.max_blob_bytes
118 } else {
119 self.limits.max_string_bytes
120 },
121 Kind::ResourceLimit,
122 )?;
123 add(&mut self.payload, n, self.limits.max_payload_bytes)?;
124 self.allocate(n.saturating_mul(2))?;
125 self.work(n)
126 }
127 pub fn key_work(&mut self, bytes: usize, entries: usize) -> Result<()> {
128 let comparisons = (usize::BITS - entries.max(1).leading_zeros()) as usize;
131 self.work(bytes.saturating_mul(comparisons).saturating_mul(16))
132 }
133 pub fn tree(&mut self, v: &Value, depth: usize) -> Result<()> {
134 self.node(depth)?;
135 match v {
136 Value::String(s) => self.payload(s.len(), false)?,
137 Value::Bytes(b) => self.payload(b.len(), true)?,
138 Value::Array(a) => {
139 self.entries(a.len())?;
140 for v in a {
141 self.tree(v, depth + 1)?;
142 }
143 }
144 Value::Map(m) => {
145 self.entries(m.len())?;
146 for (k, v) in m {
147 self.payload(k.len(), false)?;
148 self.key_work(k.len(), m.len())?;
149 self.tree(v, depth + 1)?;
150 }
151 }
152 _ => {}
153 }
154 Ok(())
155 }
156}
157
158pub fn validate_value(value: &Value, limits: &Limits) -> std::result::Result<(), Error> {
160 Budget::new(limits)
161 .tree(value, 0)
162 .map_err(|e| e.core(&Format::VALUE, CodecOperation::Encode, limits))
163}