1#[cfg(feature = "decoder")]
23use crate::status::{DracoError, ErrorKind, Status};
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50#[non_exhaustive]
51pub struct DecodeLimits {
52 pub max_points: u64,
54 pub max_faces: u64,
56 pub max_decoded_bytes: u64,
64}
65
66impl Default for DecodeLimits {
67 fn default() -> Self {
68 Self {
75 max_points: 256_000_000,
76 max_faces: 512_000_000,
77 max_decoded_bytes: 2 << 30,
78 }
79 }
80}
81
82impl DecodeLimits {
83 pub fn permissive() -> Self {
88 Self {
89 max_points: u64::MAX,
90 max_faces: u64::MAX,
91 max_decoded_bytes: u64::MAX,
92 }
93 }
94
95 pub fn fuzzing() -> Self {
98 Self {
99 max_points: 1 << 20,
100 max_faces: 1 << 20,
101 max_decoded_bytes: 64 << 20,
102 }
103 }
104
105 #[cfg(feature = "point_cloud_decode")]
108 pub(crate) fn check_points(self, points: u64) -> Status {
109 Self::check("points", points, self.max_points)
110 }
111
112 #[cfg(feature = "decoder")]
113 pub(crate) fn check_faces(self, faces: u64) -> Status {
114 Self::check("faces", faces, self.max_faces)
115 }
116
117 #[cfg(feature = "decoder")]
118 pub(crate) fn check_decoded_bytes(self, bytes: u64) -> Status {
119 Self::check("decoded attribute bytes", bytes, self.max_decoded_bytes)
120 }
121
122 #[cfg(feature = "decoder")]
123 fn check(what: &str, value: u64, ceiling: u64) -> Status {
124 if value > ceiling {
125 return Err(DracoError::new(
126 ErrorKind::LimitExceeded,
127 format!("stream decodes {value} {what}, over the caller's ceiling of {ceiling}"),
128 ));
129 }
130 Ok(())
131 }
132}
133
134macro_rules! limit_setter {
137 ($name:ident, $field:ident, $doc:expr) => {
138 #[doc = $doc]
139 #[must_use]
140 pub fn $name(mut self, value: u64) -> Self {
141 self.$field = value;
142 self
143 }
144 };
145}
146
147impl DecodeLimits {
148 limit_setter!(with_max_points, max_points, "Sets [`Self::max_points`].");
149 limit_setter!(with_max_faces, max_faces, "Sets [`Self::max_faces`].");
150 limit_setter!(
151 with_max_decoded_bytes,
152 max_decoded_bytes,
153 "Sets [`Self::max_decoded_bytes`]."
154 );
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn the_defaults_clear_every_asset_the_calibration_measured() {
163 let limits = DecodeLimits::default();
164 assert!(limits.check_points(25_000_000).is_ok());
166 assert!(limits.check_faces(6_618_864).is_ok());
167 assert!(limits.check_decoded_bytes(700_000_000).is_ok());
168 }
169
170 #[test]
171 fn the_defaults_refuse_the_claim_that_motivated_them() {
172 let limits = DecodeLimits::default();
173 assert!(limits.check_points(1_073_741_828).is_err());
176 assert!(limits.check_decoded_bytes(1_073_741_828 * 12).is_err());
177 }
178
179 #[test]
180 fn permissive_limits_refuse_nothing() {
181 let limits = DecodeLimits::permissive();
182 assert!(limits.check_points(u64::MAX).is_ok());
183 assert!(limits.check_faces(u64::MAX).is_ok());
184 assert!(limits.check_decoded_bytes(u64::MAX).is_ok());
185 }
186
187 #[test]
188 fn a_refusal_names_the_caller_rather_than_the_file() {
189 let error = DecodeLimits::default()
190 .check_points(u64::MAX)
191 .expect_err("over the ceiling");
192 assert_eq!(error.kind(), ErrorKind::LimitExceeded);
193 assert!(error.message().contains("ceiling"), "{error}");
194 }
195}
196
197#[cfg(all(test, feature = "encoder", feature = "decoder"))]
203mod wiring {
204 use super::*;
205 use crate::decoder_buffer::DecoderBuffer;
206 use crate::draco_types::DataType;
207 use crate::encoder_buffer::EncoderBuffer;
208 use crate::encoder_options::EncoderOptions;
209 use crate::geometry_attribute::{GeometryAttributeType, PointAttribute};
210 use crate::point_cloud::PointCloud;
211 use crate::point_cloud_decoder::PointCloudDecoder;
212 use crate::point_cloud_encoder::PointCloudEncoder;
213
214 const NUM_POINTS: usize = 100_000;
215
216 fn a_stream_of_100k_points() -> Vec<u8> {
217 let mut point_cloud = PointCloud::new();
218 point_cloud.set_num_points(NUM_POINTS);
219 let mut position = PointAttribute::new();
220 position.init(
221 GeometryAttributeType::Position,
222 3,
223 DataType::Float32,
224 false,
225 NUM_POINTS,
226 );
227 point_cloud.add_attribute(position);
228
229 let mut options = EncoderOptions::new();
230 options.set_attribute_int(0, "quantization_bits", 8);
231 let mut encoded = EncoderBuffer::new();
232 let mut encoder = PointCloudEncoder::new();
233 encoder.set_point_cloud(point_cloud);
234 encoder.encode(&options, &mut encoded).expect("encode");
235 encoded.data().to_vec()
236 }
237
238 fn decode_under(limits: DecodeLimits, stream: &[u8]) -> Result<usize, DracoError> {
239 let mut buffer = DecoderBuffer::new(stream).with_limits(limits);
240 let mut decoded = PointCloud::new();
241 PointCloudDecoder::new().decode(&mut buffer, &mut decoded)?;
242 Ok(decoded.num_points())
243 }
244
245 #[test]
246 fn the_defaults_decode_a_stream_this_crate_wrote() {
247 let stream = a_stream_of_100k_points();
248 assert_eq!(
249 decode_under(DecodeLimits::default(), &stream).expect("within the defaults"),
250 NUM_POINTS
251 );
252 }
253
254 #[test]
255 fn a_point_ceiling_below_the_header_refuses_it() {
256 let stream = a_stream_of_100k_points();
257 let error = decode_under(DecodeLimits::default().with_max_points(1_000), &stream)
258 .expect_err("over the ceiling");
259 assert_eq!(error.kind(), ErrorKind::LimitExceeded);
260 assert!(error.message().contains("points"), "{error}");
261 }
262
263 #[test]
267 fn a_byte_ceiling_below_the_attribute_layout_refuses_it() {
268 let stream = a_stream_of_100k_points();
269 let error = decode_under(
270 DecodeLimits::default().with_max_decoded_bytes(NUM_POINTS as u64 * 12 - 1),
271 &stream,
272 )
273 .expect_err("over the ceiling");
274 assert_eq!(error.kind(), ErrorKind::LimitExceeded, "{error}");
275 assert!(
276 error.message().contains("decoded attribute bytes"),
277 "{error}"
278 );
279
280 assert_eq!(
281 decode_under(
282 DecodeLimits::default().with_max_decoded_bytes(NUM_POINTS as u64 * 12),
283 &stream
284 )
285 .expect("exactly the layout fits"),
286 NUM_POINTS
287 );
288 }
289
290 #[test]
293 fn a_face_ceiling_below_the_connectivity_refuses_it() {
294 use crate::mesh::Mesh;
295 use crate::mesh_decoder::MeshDecoder;
296 use crate::mesh_encoder::MeshEncoder;
297
298 const SIDE: u32 = 64;
299 let mut mesh = Mesh::new();
300 let points = (SIDE * SIDE) as usize;
301 mesh.set_num_points(points);
302 let mut position = PointAttribute::new();
303 position.init(
304 GeometryAttributeType::Position,
305 3,
306 DataType::Float32,
307 false,
308 points,
309 );
310 mesh.add_attribute(position);
311 let mut faces = Vec::new();
312 for y in 0..SIDE - 1 {
313 for x in 0..SIDE - 1 {
314 let a = y * SIDE + x;
315 faces.push([a, a + 1, a + SIDE]);
316 faces.push([a + 1, a + SIDE + 1, a + SIDE]);
317 }
318 }
319 mesh.try_set_num_faces(faces.len()).expect("faces");
320 for (index, face) in faces.iter().enumerate() {
321 mesh.set_face_from_indices(index, *face);
322 }
323 let num_faces = faces.len();
324
325 let mut encoded = EncoderBuffer::new();
326 let mut encoder = MeshEncoder::new();
327 encoder.set_mesh(mesh);
328 encoder
329 .encode(&EncoderOptions::new(), &mut encoded)
330 .expect("encode");
331 let stream = encoded.data().to_vec();
332
333 let decode = |limits: DecodeLimits| {
334 let mut buffer = DecoderBuffer::new(&stream).with_limits(limits);
335 let mut decoded = Mesh::new();
336 MeshDecoder::new()
337 .decode(&mut buffer, &mut decoded)
338 .map(|()| decoded.num_faces())
339 };
340
341 assert_eq!(
342 decode(DecodeLimits::default()).expect("within the defaults"),
343 num_faces
344 );
345 let error = decode(DecodeLimits::default().with_max_faces(num_faces as u64 - 1))
346 .expect_err("over the ceiling");
347 assert_eq!(error.kind(), ErrorKind::LimitExceeded, "{error}");
348 assert!(error.message().contains("faces"), "{error}");
349 }
350
351 #[test]
352 fn permissive_limits_decode_what_the_defaults_do() {
353 let stream = a_stream_of_100k_points();
354 assert_eq!(
355 decode_under(DecodeLimits::permissive(), &stream).expect("no ceiling"),
356 NUM_POINTS
357 );
358 }
359}