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