• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

agronholm / cbor2 / 28702986235

04 Jul 2026 10:13AM UTC coverage: 94.682% (+0.008%) from 94.674%
28702986235

push

github

web-flow
Decode scoped IPv6 addresses instead of rejecting them (#324)

23 of 25 new or added lines in 1 file covered. (92.0%)

2368 of 2501 relevant lines covered (94.68%)

205505.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.65
/rust/decoder.rs
1
use crate::_cbor2::{BREAK_MARKER, SYS_MAXSIZE, UNDEFINED};
2
use crate::decoder::DecoderResult::{
3
    BeginFrame, CompleteFrame, ContinueFrame, Shareable, SharedReference, StringNamespace,
4
    StringReference, StringValue, Value,
5
};
6
#[cfg(not(Py_3_15))]
7
use crate::types::FrozenDict;
8
use crate::types::{
9
    CBORDecodeEOF, CBORDecodeError, CBORSimpleValue, CBORTag, DECIMAL_TYPE, FRACTION_TYPE,
10
    IPV4ADDRESS_TYPE, IPV4INTERFACE_TYPE, IPV4NETWORK_TYPE, IPV6ADDRESS_TYPE, IPV6INTERFACE_TYPE,
11
    IPV6NETWORK_TYPE, UUID_TYPE,
12
};
13
use crate::utils::{PyImportable, create_exc_from, raise_exc_from};
14
use half::f16;
15
use pyo3::exceptions::{PyException, PyLookupError, PyTypeError, PyValueError};
16
use pyo3::prelude::*;
17
use pyo3::types::{
18
    PyBytes, PyCFunction, PyComplex, PyDict, PyFrozenSet, PyInt, PyList, PyListMethods, PyMapping,
19
    PySet, PyString, PyTuple,
20
};
21
use pyo3::{IntoPyObjectExt, Py, PyAny, PyErrArguments, intern, pyclass};
22
use std::fmt::{Display, Formatter};
23
use std::mem::{replace, take};
24

25
const IMMUTABLE_ATTR: &str = "_cbor2_immutable";
26
const NAME_ATTR: &str = "_cbor2_name";
27
const SEEK_CUR: u8 = 1;
28

29
static DATE_FROMISOFORMAT: PyImportable = PyImportable::new("datetime", "date.fromisoformat");
30
static DATE_FROMORDINAL: PyImportable = PyImportable::new("datetime", "date.fromordinal");
31
static DATETIME_FROMISOFORMAT: PyImportable =
32
    PyImportable::new("datetime", "datetime.fromisoformat");
33
static DATETIME_FROMTIMESTAMP: PyImportable =
34
    PyImportable::new("datetime", "datetime.fromtimestamp");
35
static EMAIL_PARSER: PyImportable = PyImportable::new("email.parser", "Parser");
36
static INT_FROMBYTES: PyImportable = PyImportable::new("builtins", "int.from_bytes");
37
static IPADDRESS_FUNC: PyImportable = PyImportable::new("ipaddress", "ip_address");
38
static IPNETWORK_FUNC: PyImportable = PyImportable::new("ipaddress", "ip_network");
39
static IPINTERFACE_FUNC: PyImportable = PyImportable::new("ipaddress", "ip_interface");
40
static RE_COMPILE: PyImportable = PyImportable::new("re", "compile");
41
static UTC: PyImportable = PyImportable::new("datetime", "timezone.utc");
42
#[cfg(Py_3_15)]
43
static FROZEN_DICT: PyImportable = PyImportable::new("builtins", "frozendict");
44

45
enum DecoderResult<'a> {
46
    BeginFrame(
47
        Box<DecoderCallback<'a>>,
48
        bool,
49
        Option<Bound<'a, PyAny>>,
50
        DisplayName<'a>,
51
    ),
52
    ContinueFrame(bool),
53
    CompleteFrame(Bound<'a, PyAny>),
54
    Value(Bound<'a, PyAny>),
55
    StringValue(Bound<'a, PyAny>, usize),
56
    StringNamespace,
57
    StringReference(usize),
58
    Shareable,
59
    SharedReference(usize),
60
}
61

62
enum DisplayName<'a> {
63
    String(&'static str),
64
    SemanticTag(u64),
65
    PythonName(Bound<'a, PyAny>),
66
}
67

68
impl<'a> Display for DisplayName<'a> {
69
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
540✔
70
        match self {
540✔
71
            DisplayName::String(s) => f.write_str(s),
516✔
72
            DisplayName::SemanticTag(tagnum) => write!(f, "semantic tag {}", tagnum),
12✔
73
            DisplayName::PythonName(obj) => write!(f, "{}", obj),
12✔
74
        }
75
    }
540✔
76
}
77

78
type DecoderCallback<'py> =
79
    dyn 'py + FnMut(Bound<'py, PyAny>, bool) -> PyResult<DecoderResult<'py>>;
80

81
struct StackFrame<'py> {
82
    immutable: bool,
83
    decoder_callback: Option<Box<DecoderCallback<'py>>>,
84
    shareable_index: Option<usize>,
85
    typename: DisplayName<'py>,
86
    contains_string_namespace: bool,
87
}
88

89
/// Decorates a function to be a two-stage decoder.
90
///
91
/// :param name: the name displayed in a :exc:`CBORDecodeError` raised by the decoder
92
///     (e.g. "error decoding thingamajig") where name='thingamajig`)
93
/// :param immutable: :data:`True` if the item sent to the decoder should be decoded as immutable
94
#[pyfunction]
95
#[pyo3(signature = (func=None, /, *, name=None, immutable=false))]
96
pub fn shareable_decoder<'py>(
120✔
97
    py: Python<'py>,
120✔
98
    func: Option<Py<PyAny>>,
120✔
99
    name: Option<Py<PyString>>,
120✔
100
    immutable: bool,
120✔
101
) -> PyResult<Bound<'py, PyAny>> {
120✔
102
    match func {
120✔
103
        None => PyCFunction::new_closure(
60✔
104
            py,
60✔
105
            None,
60✔
106
            None,
60✔
107
            move |args: &Bound<'_, PyTuple>,
108
                  _kwargs: Option<&Bound<'_, PyDict>>|
109
                  -> PyResult<Py<PyAny>> {
60✔
110
                let py = args.py();
60✔
111
                let func = args.get_item(0)?;
60✔
112
                let name = name.as_ref().map(|x| x.clone_ref(py));
60✔
113
                shareable_decoder(py, Some(func.unbind()), name, immutable).map(Bound::unbind)
60✔
114
            },
60✔
115
        )
116
        .map(|f| f.into_any()),
60✔
117
        Some(func) => {
60✔
118
            let bound_func = func.bind(py);
60✔
119
            if !bound_func.is_callable() {
60✔
120
                return Err(PyTypeError::new_err(format!("{func} is not callable")));
×
121
            }
60✔
122
            bound_func.setattr(intern!(py, NAME_ATTR), name)?;
60✔
123
            bound_func.setattr(intern!(py, IMMUTABLE_ATTR), immutable)?;
60✔
124
            Ok(bound_func.clone().into_any())
60✔
125
        }
126
    }
127
}
120✔
128

129
fn require_tuple<'py>(value: Bound<'py, PyAny>, length: usize) -> PyResult<Bound<'py, PyTuple>> {
488✔
130
    let array: Bound<'py, PyTuple> = value
488✔
131
        .cast_into()
488✔
132
        .map_err(|_| PyTypeError::new_err("input value must be an array"))?;
488✔
133
    if array.len() != length {
452✔
134
        return Err(PyValueError::new_err(format!(
×
135
            "expected an array with exactly {length} elements"
×
136
        )));
×
137
    }
452✔
138
    Ok(array)
452✔
139
}
488✔
140

141
/// The CBORDecoder class implements a fully featured `CBOR`_ decoder with
142
/// several extensions for handling shared references, big integers, rational
143
/// numbers and so on. Typically, the class is not used directly, but the
144
/// :func:`load` and :func:`loads` functions are called to indirectly construct
145
/// and use the class.
146
///
147
/// When the class is constructed manually, the main entry point is :meth:`decode`.
148
///
149
/// :param fp: the file to read from (any file-like object opened for reading in binary mode)
150
/// :param tag_hook:
151
///     callable that takes 2 arguments: the decoder instance, and the :class:`.CBORTag`
152
///     to be decoded. This callback is invoked for any tags for which there is no
153
///     built-in decoder. The return value is substituted for the :class:`.CBORTag`
154
///     object in the deserialized output
155
/// :param object_hook:
156
///     callable that takes 2 arguments: the decoder instance, and a dictionary. This
157
///     callback is invoked for each deserialized :class:`dict` object. The return value
158
///     is substituted for the dict in the deserialized output.
159
/// :param semantic_decoders:
160
///     An optional mapping for overriding the decoding for select semantic tags.
161
///     The value is a mapping of semantic tags (integers) to callables that take
162
///     the decoder instance as the sole argument.
163
/// :param str_errors:
164
///     determines how to handle Unicode decoding errors (see the `Error Handlers`_
165
///     section in the standard library documentation for details)
166
/// :param read_size: minimum number of bytes to read at once
167
///     (ignored if ``fp`` is not seekable)
168
/// :param max_depth:
169
///     maximum allowed depth for nested containers
170
/// :param allow_indefinite:
171
///     if :data:`False`, raise a :exc:`CBORDecodeError` when encountering an indefinite-length
172
///     string or container in the input stream
173
/// :param allow_duplicate_keys:
174
///     if :data:`False`, raise a :exc:`CBORDecodeError` when a map key that has already been
175
///     decoded in the same map is encountered
176
///
177
/// .. _CBOR: https://cbor.io/
178
#[pyclass(module = "cbor2")]
179
pub struct CBORDecoder {
180
    fp: Option<Py<PyAny>>,
181
    tag_hook: Option<Py<PyAny>>,
182
    object_hook: Option<Py<PyAny>>,
183
    semantic_decoders: Option<Py<PyMapping>>,
184
    str_errors: Option<Py<PyString>>,
185
    #[pyo3(get)]
186
    read_size: usize,
187
    #[pyo3(get)]
188
    max_depth: usize,
189
    #[pyo3(get)]
190
    allow_indefinite: bool,
191
    #[pyo3(get)]
192
    allow_duplicate_keys: bool,
193

194
    read_method: Option<Py<PyAny>>,
195
    buffer: Option<Py<PyBytes>>,
196
    read_position: usize,
197
    available_bytes: usize,
198
    fp_is_seekable: bool,
199
}
200

201
impl CBORDecoder {
202
    pub fn new_internal(
4,716✔
203
        py: Python<'_>,
4,716✔
204
        fp: Option<&Bound<'_, PyAny>>,
4,716✔
205
        buffer: Option<Bound<PyBytes>>,
4,716✔
206
        tag_hook: Option<&Bound<'_, PyAny>>,
4,716✔
207
        object_hook: Option<&Bound<'_, PyAny>>,
4,716✔
208
        semantic_decoders: Option<&Bound<'_, PyMapping>>,
4,716✔
209
        str_errors: &str,
4,716✔
210
        read_size: usize,
4,716✔
211
        max_depth: usize,
4,716✔
212
        allow_indefinite: bool,
4,716✔
213
        allow_duplicate_keys: bool,
4,716✔
214
    ) -> PyResult<Self> {
4,716✔
215
        let available_bytes = if let Some(buffer) = buffer.as_ref() {
4,716✔
216
            buffer.len()?
4,272✔
217
        } else {
218
            0
444✔
219
        };
220
        let bound_str_errors = PyString::new(py, str_errors);
4,716✔
221
        let mut this = Self {
4,716✔
222
            fp: None,
4,716✔
223
            tag_hook: None,
4,716✔
224
            object_hook: None,
4,716✔
225
            str_errors: None,
4,716✔
226
            read_size,
4,716✔
227
            max_depth,
4,716✔
228
            allow_indefinite,
4,716✔
229
            allow_duplicate_keys,
4,716✔
230
            semantic_decoders: semantic_decoders.map(|d| d.clone().unbind()),
4,716✔
231
            read_method: None,
4,716✔
232
            buffer: buffer.map(Bound::unbind),
4,716✔
233
            read_position: 0,
234
            available_bytes,
4,716✔
235
            fp_is_seekable: false,
236
        };
237
        if let Some(fp) = fp {
4,716✔
238
            this.set_fp(fp)?
444✔
239
        };
4,272✔
240
        this.set_tag_hook(tag_hook)?;
4,692✔
241
        this.set_object_hook(object_hook)?;
4,680✔
242
        this.set_str_errors(&bound_str_errors)?;
4,668✔
243
        Ok(this)
4,656✔
244
    }
4,716✔
245

246
    fn read_from_fp<'py>(
468✔
247
        &mut self,
468✔
248
        py: Python<'py>,
468✔
249
        minimum_amount: usize,
468✔
250
    ) -> PyResult<(Bound<'py, PyBytes>, usize)> {
468✔
251
        let read_size: usize = if self.fp_is_seekable {
468✔
252
            self.read_size
336✔
253
        } else {
254
            1
132✔
255
        };
256
        let bytes_to_read = minimum_amount.max(read_size);
468✔
257
        let num_read_bytes = if let Some(read) = self.read_method.as_ref() {
468✔
258
            let bytes_from_fp: Bound<PyBytes> =
360✔
259
                read.bind(py).call1((&bytes_to_read,))?.cast_into()?;
360✔
260
            let num_read_bytes = bytes_from_fp.len()?;
360✔
261
            if num_read_bytes >= minimum_amount {
360✔
262
                return Ok((bytes_from_fp, num_read_bytes));
312✔
263
            }
48✔
264
            num_read_bytes
48✔
265
        } else {
266
            0
108✔
267
        };
268
        Err(CBORDecodeEOF::new_err(format!(
156✔
269
            "premature end of stream (expected to read at least {minimum_amount} \
156✔
270
                 bytes, got {num_read_bytes} instead)"
156✔
271
        )))
156✔
272
    }
468✔
273

274
    fn read_exact<const N: usize>(&mut self, py: Python<'_>) -> PyResult<[u8; N]> {
10,425,240✔
275
        if self.available_bytes == 0 {
10,425,240✔
276
            // No buffer
277
            let (new_bytes, amount_read) = self.read_from_fp(py, N)?;
276✔
278
            self.read_position = N;
252✔
279
            self.available_bytes = amount_read - N;
252✔
280
            self.buffer = Some(new_bytes.unbind());
252✔
281
            Ok(self.buffer.as_ref().unwrap().as_bytes(py)[..N].try_into()?)
252✔
282
        } else if self.available_bytes < N {
10,424,964✔
283
            // Combine the remnants of the partial buffer with new data read from the file
284
            let needed_bytes = N - self.available_bytes;
24✔
285
            let mut concatenated_buffer: Vec<u8> = self.buffer.take().unwrap().extract(py)?;
24✔
286
            if self.read_position > 0 {
24✔
287
                concatenated_buffer.drain(..self.read_position);
24✔
288
            }
24✔
289
            concatenated_buffer.truncate(self.available_bytes);
24✔
290
            let (new_bytes, amount_read) = self.read_from_fp(py, needed_bytes)?;
24✔
291
            concatenated_buffer.extend_from_slice(&new_bytes[..needed_bytes]);
24✔
292
            self.buffer = Some(new_bytes.unbind());
24✔
293
            self.available_bytes = amount_read - needed_bytes;
24✔
294
            self.read_position = needed_bytes;
24✔
295
            Ok(concatenated_buffer
24✔
296
                .try_into()
24✔
297
                .expect("buffer size mismatch"))
24✔
298
        } else {
299
            // Return a slice from the existing bytes object
300
            let slice: [u8; N] = self.buffer.as_ref().unwrap().bind(py).as_bytes()
10,424,940✔
301
                [self.read_position..self.read_position + N]
10,424,940✔
302
                .try_into()?;
10,424,940✔
303
            self.available_bytes -= N;
10,424,940✔
304
            self.read_position += N;
10,424,940✔
305
            Ok(slice)
10,424,940✔
306
        }
307
    }
10,425,240✔
308

309
    fn read_major_and_subtype(&mut self, py: Python<'_>) -> PyResult<(u8, u8)> {
10,414,508✔
310
        let initial_byte = self.read_exact::<1>(py)?[0];
10,414,508✔
311
        let major_type = initial_byte >> 5;
10,414,484✔
312
        let subtype = initial_byte & 31;
10,414,484✔
313
        Ok((major_type, subtype))
10,414,484✔
314
    }
10,414,508✔
315

316
    fn decode_length_finite(&mut self, py: Python<'_>, subtype: u8) -> PyResult<u64> {
9,606,996✔
317
        match self.decode_length(py, subtype)? {
9,606,996✔
318
            Some(length) => Ok(length),
9,606,960✔
319
            None => Err(CBORDecodeError::new_err(
24✔
320
                "indefinite length not allowed here",
24✔
321
            )),
24✔
322
        }
323
    }
9,606,996✔
324

325
    /// Like [`decode_length`], but converts `Some(u64)` to `Some(usize)`, returning
326
    /// a [`CBORDecodeError`] if the value exceeds the platform's address space.
327
    fn decode_length_as_usize(&mut self, py: Python<'_>, subtype: u8) -> PyResult<Option<usize>> {
799,372✔
328
        match self.decode_length(py, subtype)? {
799,372✔
329
            Some(length) => usize::try_from(length).map(Some).map_err(|_| {
798,976✔
330
                CBORDecodeError::new_err(format!(
×
331
                    "huge item length {length} exceeds the system address space"
332
                ))
333
            }),
×
334
            None => Ok(None),
384✔
335
        }
336
    }
799,372✔
337

338
    //
339
    // Decoders for major tags (0-7)
340
    //
341

342
    /// Decode the length of the next item.
343
    ///
344
    /// This is a low-level operation that may be needed by custom decoder callbacks.
345
    ///
346
    /// :param subtype:
347
    /// :return: the length of the item, or :data:`None` to indicate an indefinite-length item
348
    fn decode_length(&mut self, py: Python<'_>, subtype: u8) -> PyResult<Option<u64>> {
10,406,368✔
349
        let length = match subtype {
10,406,368✔
350
            ..24 => Some(subtype as u64),
10,406,368✔
351
            24 => Some(self.read_exact::<1>(py)?[0] as u64),
2,184✔
352
            25 => Some(u16::from_be_bytes(self.read_exact(py)?) as u64),
1,144✔
353
            26 => Some(u32::from_be_bytes(self.read_exact(py)?) as u64),
312✔
354
            27 => Some(u64::from_be_bytes(self.read_exact(py)?)),
296✔
355
            31 => {
356
                if !self.allow_indefinite {
420✔
357
                    return Err(CBORDecodeError::new_err(
12✔
358
                        "encountered indefinite length but it has been disabled",
12✔
359
                    ));
12✔
360
                }
408✔
361
                None
408✔
362
            }
363
            _ => {
364
                return Err(CBORDecodeError::new_err(format!(
12✔
365
                    "unknown unsigned integer subtype 0x{subtype:x}"
12✔
366
                )));
12✔
367
            }
368
        };
369
        Ok(length)
10,406,344✔
370
    }
10,406,368✔
371

372
    fn decode_uint<'py>(&mut self, py: Python<'py>, subtype: u8) -> PyResult<DecoderResult<'py>> {
3,264✔
373
        // Major tag 0
374
        let uint: u64 = self.decode_length_finite(py, subtype)?;
3,264✔
375
        Ok(Value(uint.into_bound_py_any(py)?))
3,252✔
376
    }
3,264✔
377

378
    fn decode_negint<'py>(&mut self, py: Python<'py>, subtype: u8) -> PyResult<DecoderResult<'py>> {
380✔
379
        // Major tag 1
380
        let uint: u64 = self.decode_length_finite(py, subtype)?;
380✔
381
        let signed_int = -(uint as i128) - 1;
380✔
382
        Ok(Value(signed_int.into_bound_py_any(py)?))
380✔
383
    }
380✔
384

385
    fn decode_bytestring<'py>(
1,296✔
386
        &mut self,
1,296✔
387
        py: Python<'py>,
1,296✔
388
        subtype: u8,
1,296✔
389
    ) -> PyResult<DecoderResult<'py>> {
1,296✔
390
        // Major tag 2
391
        match self.decode_length_as_usize(py, subtype)? {
1,296✔
392
            None => {
393
                // Indefinite length
394
                let sys_maxsize = *SYS_MAXSIZE.get(py).unwrap();
84✔
395
                let bytes = PyBytes::new_with_writer(py, 0, |writer| {
84✔
396
                    loop {
397
                        let (major_type, subtype) = self.read_major_and_subtype(py)?;
4,800,132✔
398
                        match (major_type, subtype) {
4,800,132✔
399
                            (2, _) => {
400
                                let length = self.decode_length_finite(py, subtype)?;
4,800,084✔
401
                                if length > sys_maxsize {
4,800,072✔
402
                                    return Err(CBORDecodeError::new_err(format!(
12✔
403
                                        "chunk too long in an indefinite bytestring chunk: {length}"
12✔
404
                                    )));
12✔
405
                                }
4,800,060✔
406
                                let length = length as usize;
4,800,060✔
407
                                let chunk = self.read(py, length)?;
4,800,060✔
408
                                writer.write_all(&chunk)?;
4,800,048✔
409
                            }
410
                            (7, 31) => break Ok(()), // break marker
24✔
411
                            _ => {
412
                                return Err(CBORDecodeError::new_err(format!(
24✔
413
                                    "non-byte string (major type {major_type}) found in indefinite \
24✔
414
                                    length byte string"
24✔
415
                                )));
24✔
416
                            }
417
                        }
418
                    }
419
                })?;
84✔
420
                Ok(Value(bytes.into_any()))
24✔
421
            }
422
            Some(length) if length <= 65536 => {
1,212✔
423
                let bytes = self.read(py, length)?;
1,188✔
424
                Ok(StringValue(PyBytes::new(py, &bytes).into_any(), length))
1,152✔
425
            }
426
            Some(length) => {
24✔
427
                // Incrementally read the bytestring, in chunks of 65536 bytes. The claimed
428
                // length is untrusted until the data has actually been read, so no more than
429
                // 64 KiB of it is reserved up front; a truncated payload claiming a huge length
430
                // can then force at most a 64 KiB allocation.
431
                let bytes = PyBytes::new_with_writer(py, length.min(65536), |writer| {
24✔
432
                    let mut remaining_length = length;
24✔
433
                    while remaining_length > 0 {
48✔
434
                        let chunk_size = remaining_length.min(65536);
36✔
435
                        let chunk = self.read(py, chunk_size)?;
36✔
436
                        remaining_length -= chunk_size;
24✔
437
                        writer.write_all(&chunk)?;
24✔
438
                    }
439
                    Ok(())
12✔
440
                })?;
24✔
441
                Ok(StringValue(bytes.into_any(), length))
12✔
442
            }
443
        }
444
    }
1,296✔
445

446
    fn decode_string<'py>(&mut self, py: Python<'py>, subtype: u8) -> PyResult<DecoderResult<'py>> {
789,008✔
447
        // Major tag 3
448
        match self.decode_length_as_usize(py, subtype)? {
789,008✔
449
            None => {
450
                // Indefinite length
451
                let mut parts: Vec<Bound<'py, PyString>> = Vec::new();
108✔
452
                loop {
453
                    let (major_type, subtype) = self.read_major_and_subtype(py)?;
4,800,180✔
454
                    let sys_maxsize = *SYS_MAXSIZE.get(py).unwrap();
4,800,180✔
455
                    match (major_type, subtype) {
4,800,180✔
456
                        (3, _) => {
457
                            let length = self.decode_length_finite(py, subtype)?;
4,800,120✔
458
                            if length > sys_maxsize {
4,800,108✔
459
                                return Err(CBORDecodeError::new_err(format!(
12✔
460
                                    "chunk too long in an indefinite text string chunk: {length}"
12✔
461
                                )));
12✔
462
                            }
4,800,096✔
463
                            let length = length as usize;
4,800,096✔
464
                            let bytes = self.read(py, length)?;
4,800,096✔
465
                            let decoded = match self.str_errors.as_ref() {
4,800,084✔
466
                                None => PyString::from_bytes(py, bytes.as_slice()),
4,800,084✔
467
                                Some(str_errors) => bytes
×
468
                                    .into_bound_py_any(py)?
×
469
                                    .call_method1(
×
470
                                        intern!(py, "decode"),
×
471
                                        (intern!(py, "utf-8"), str_errors),
×
472
                                    )
473
                                    .and_then(|string| string.cast_into().map_err(PyErr::from)),
×
474
                            }?;
12✔
475
                            parts.push(decoded);
4,800,072✔
476
                        }
477
                        (7, 31) => {
478
                            // break marker
479
                            break PyString::new(py, "")
36✔
480
                                .call_method1(intern!(py, "join"), (PyList::new(py, parts)?,))
36✔
481
                                .map(|joined| Value(joined.into_any()));
36✔
482
                        }
483
                        _ => {
484
                            return Err(CBORDecodeError::new_err(format!(
24✔
485
                                "non-text string (major type {major_type}) found in indefinite \
24✔
486
                                    length text string"
24✔
487
                            )));
24✔
488
                        }
489
                    }
490
                }
491
            }
492
            Some(length) if length <= 65536 => {
788,888✔
493
                let bytes = self.read(py, length)?;
788,828✔
494
                let decoded_string: Bound<'_, PyAny> = match self.str_errors.as_ref() {
788,780✔
495
                    None => PyString::from_bytes(py, bytes.as_slice())?.into_any(),
788,756✔
496
                    Some(str_errors) => bytes.into_bound_py_any(py)?.call_method1(
24✔
497
                        intern!(py, "decode"),
24✔
498
                        (intern!(py, "utf-8"), str_errors.bind(py)),
24✔
499
                    )?,
×
500
                };
501
                Ok(StringValue(decoded_string, length))
788,744✔
502
            }
503
            Some(length) => {
60✔
504
                // Read the string into a single bytes object in chunks of 65536 bytes, then
505
                // decode it in one pass. As with the bytestring path, the claimed length is
506
                // untrusted until the data has actually been read, so no more than 64 KiB of it
507
                // is reserved up front; a truncated payload claiming a huge length can then force
508
                // at most a 64 KiB allocation. Decoding the assembled bytes as a whole also keeps
509
                // multi-byte characters straddling a chunk boundary intact, so no incremental
510
                // decoder is needed.
511
                let bytes = PyBytes::new_with_writer(py, length.min(65536), |writer| {
60✔
512
                    let mut remaining_length = length;
60✔
513
                    while remaining_length > 0 {
204✔
514
                        let chunk_size = remaining_length.min(65536);
144✔
515
                        let chunk = self.read(py, chunk_size)?;
144✔
516
                        remaining_length -= chunk_size;
144✔
517
                        writer.write_all(&chunk)?;
144✔
518
                    }
519
                    Ok(())
60✔
520
                })?;
60✔
521
                let errors = match self.str_errors.as_ref() {
60✔
522
                    None => None,
36✔
523
                    // set_str_errors only ever stores these values
524
                    Some(str_errors) => Some(match str_errors.to_str(py)? {
24✔
525
                        "ignore" => c"ignore",
24✔
526
                        "replace" => c"replace",
24✔
527
                        "backslashreplace" => c"backslashreplace",
×
528
                        "surrogateescape" => c"surrogateescape",
×
529
                        other => {
×
530
                            return Err(CBORDecodeError::new_err(format!(
×
531
                                "invalid str_errors value: '{other}'"
×
532
                            )));
×
533
                        }
534
                    }),
535
                };
536
                let decoded =
60✔
537
                    PyString::from_encoded_object(bytes.as_any(), Some(c"utf-8"), errors)?;
60✔
538
                Ok(StringValue(decoded.into_any(), length))
60✔
539
            }
540
        }
541
    }
789,008✔
542

543
    fn decode_array<'py>(
7,184✔
544
        &mut self,
7,184✔
545
        py: Python<'py>,
7,184✔
546
        subtype: u8,
7,184✔
547
        immutable: bool,
7,184✔
548
    ) -> PyResult<DecoderResult<'py>> {
7,184✔
549
        // Major tag 4
550
        let optional_length = self.decode_length_as_usize(py, subtype)?;
7,184✔
551
        if immutable {
7,184✔
552
            let mut items: Vec<Bound<'py, PyAny>> = Vec::new();
1,040✔
553
            let callback: Box<DecoderCallback<'py>> = if let Some(length) = optional_length {
1,040✔
554
                if length == 0 {
1,016✔
555
                    return Ok(Value(PyTuple::empty(py).into_any()));
56✔
556
                }
960✔
557

558
                Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
2,036✔
559
                    items.push(item);
2,036✔
560
                    if items.len() == length {
2,036✔
561
                        Ok(CompleteFrame(
562
                            PyTuple::new(py, take(&mut items))?.into_any(),
912✔
563
                        ))
564
                    } else {
565
                        Ok(ContinueFrame(false))
1,124✔
566
                    }
567
                })
2,036✔
568
            } else {
569
                let break_marker = BREAK_MARKER.get(py).unwrap().bind(py);
24✔
570
                Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
72✔
571
                    if item.is(break_marker) {
72✔
572
                        Ok(CompleteFrame(
573
                            PyTuple::new(py, take(&mut items))?.into_any(),
12✔
574
                        ))
575
                    } else {
576
                        items.push(item);
60✔
577
                        Ok(ContinueFrame(false))
60✔
578
                    }
579
                })
72✔
580
            };
581
            Ok(BeginFrame(
984✔
582
                callback,
984✔
583
                false,
984✔
584
                None,
984✔
585
                DisplayName::String("array"),
984✔
586
            ))
984✔
587
        } else {
588
            let mut list = PyList::empty(py);
6,144✔
589
            let container = list.clone().into_any();
6,144✔
590
            let callback: Box<DecoderCallback<'py>> = if let Some(length) = optional_length {
6,144✔
591
                if length == 0 {
6,036✔
592
                    return Ok(Value(PyList::empty(py).into_any()));
132✔
593
                }
5,904✔
594

595
                Box::new(move |item, _immutable: bool| {
8,008✔
596
                    list.append(item)?;
8,008✔
597
                    if list.len() == length {
8,008✔
598
                        Ok(CompleteFrame(
936✔
599
                            replace(&mut list, PyList::empty(py)).into_any(),
936✔
600
                        ))
936✔
601
                    } else {
602
                        Ok(ContinueFrame(false))
7,072✔
603
                    }
604
                })
8,008✔
605
            } else {
606
                let break_marker = BREAK_MARKER.get(py).unwrap().bind(py);
108✔
607
                Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
787,044✔
608
                    if item.is(break_marker) {
787,044✔
609
                        Ok(CompleteFrame(
108✔
610
                            replace(&mut list, PyList::empty(py)).into_any(),
108✔
611
                        ))
108✔
612
                    } else {
613
                        list.append(item)?;
786,936✔
614
                        Ok(ContinueFrame(false))
786,936✔
615
                    }
616
                })
787,044✔
617
            };
618
            Ok(BeginFrame(
6,012✔
619
                callback,
6,012✔
620
                false,
6,012✔
621
                Some(container),
6,012✔
622
                DisplayName::String("array"),
6,012✔
623
            ))
6,012✔
624
        }
625
    }
7,184✔
626

627
    fn decode_map<'py>(
1,884✔
628
        &mut self,
1,884✔
629
        py: Python<'py>,
1,884✔
630
        subtype: u8,
1,884✔
631
        immutable: bool,
1,884✔
632
    ) -> PyResult<DecoderResult<'py>> {
1,884✔
633
        // Major tag 5
634

635
        #[cfg(Py_3_15)]
636
        fn create_frozen_dict<'py>(
12✔
637
            py: Python<'py>,
12✔
638
            items: Vec<(Bound<'py, PyAny>, Bound<'py, PyAny>)>,
12✔
639
        ) -> PyResult<Bound<'py, PyAny>> {
12✔
640
            FROZEN_DICT
12✔
641
                .get(py)?
12✔
642
                .call1((items,))?
12✔
643
                .cast_into()
12✔
644
                .map_err(|e| PyErr::from(e))
12✔
645
        }
12✔
646
        #[cfg(not(Py_3_15))]
647
        fn create_frozen_dict<'py>(
132✔
648
            py: Python<'py>,
132✔
649
            items: Vec<(Bound<'py, PyAny>, Bound<'py, PyAny>)>,
132✔
650
        ) -> PyResult<Bound<'py, PyAny>> {
132✔
651
            FrozenDict::from_items(py, items).map(|dict| dict.into_any())
132✔
652
        }
132✔
653

654
        #[inline]
655
        fn maybe_call_object_hook<'py>(
1,680✔
656
            py: Python<'py>,
1,680✔
657
            dict: Bound<'py, PyAny>,
1,680✔
658
            object_hook: Option<&Py<PyAny>>,
1,680✔
659
            immutable: bool,
1,680✔
660
        ) -> PyResult<Bound<'py, PyAny>> {
1,680✔
661
            if let Some(object_hook) = object_hook {
1,680✔
662
                object_hook.bind(py).call1((dict, immutable))
24✔
663
            } else {
664
                Ok(dict)
1,656✔
665
            }
666
        }
1,680✔
667

668
        let object_hook = self.object_hook.as_ref().map(|hook| hook.clone_ref(py));
1,884✔
669
        let allow_duplicate_keys = self.allow_duplicate_keys;
1,884✔
670
        let length_or_none = self.decode_length_as_usize(py, subtype)?;
1,884✔
671

672
        // Return immediately if this is an empty dict
673
        if let Some(length) = length_or_none
1,884✔
674
            && length == 0
1,824✔
675
        {
676
            let container: Bound<'py, PyAny> = if immutable {
420✔
677
                create_frozen_dict(py, Vec::new())?
×
678
            } else {
679
                PyDict::new(py).into_any()
420✔
680
            };
681
            let transformed =
420✔
682
                maybe_call_object_hook(py, container, object_hook.as_ref(), immutable)?;
420✔
683
            return Ok(Value(transformed));
420✔
684
        };
1,464✔
685

686
        let mut key: Option<Bound<'py, PyAny>> = None;
1,464✔
687
        if immutable {
1,464✔
688
            let seen_keys: Option<Bound<'py, PySet>> = if allow_duplicate_keys {
300✔
689
                None
276✔
690
            } else {
691
                Some(PySet::empty(py)?)
24✔
692
            };
693
            let check_duplicate = move |key: &Bound<'py, PyAny>| -> PyResult<()> {
300✔
694
                let seen = seen_keys.as_ref().unwrap();
48✔
695
                if seen.contains(key)? {
48✔
696
                    let repr = key.repr()?;
24✔
697
                    return Err(CBORDecodeError::new_err(format!(
24✔
698
                        "Duplicate map key: {}",
699
                        repr.to_str()?
24✔
700
                    )));
701
                }
24✔
702
                seen.add(key.clone())
24✔
703
            };
48✔
704

705
            let mut items: Vec<(Bound<'py, PyAny>, Bound<'py, PyAny>)> = Vec::new();
300✔
706
            let callback: Box<DecoderCallback<'py>> = if let Some(length) = length_or_none {
300✔
707
                Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
396✔
708
                    if let Some(key) = key.take() {
396✔
709
                        if !allow_duplicate_keys {
192✔
710
                            check_duplicate(&key)?;
24✔
711
                        }
168✔
712
                        items.push((key, item));
180✔
713
                        if items.len() == length {
180✔
714
                            let transformed = maybe_call_object_hook(
144✔
715
                                py,
144✔
716
                                create_frozen_dict(py, take(&mut items))?,
144✔
717
                                object_hook.as_ref(),
144✔
718
                                immutable,
144✔
719
                            )?;
×
720
                            return Ok(CompleteFrame(transformed));
144✔
721
                        }
36✔
722
                        Ok(ContinueFrame(true))
36✔
723
                    } else {
724
                        key = Some(item);
204✔
725
                        Ok(ContinueFrame(false))
204✔
726
                    }
727
                })
396✔
728
            } else {
729
                let break_marker = BREAK_MARKER.get(py).unwrap().bind(py);
12✔
730
                Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
48✔
731
                    if item.is(break_marker) {
48✔
732
                        let container = create_frozen_dict(py, take(&mut items))?;
×
733
                        let transformed = maybe_call_object_hook(
×
734
                            py,
×
735
                            container.into_any(),
×
736
                            object_hook.as_ref(),
×
737
                            immutable,
×
738
                        )?;
×
739
                        Ok(CompleteFrame(transformed))
×
740
                    } else if let Some(key) = key.take() {
48✔
741
                        if !allow_duplicate_keys {
24✔
742
                            check_duplicate(&key)?;
24✔
743
                        }
×
744
                        items.push((key, item));
12✔
745
                        Ok(ContinueFrame(true))
12✔
746
                    } else {
747
                        key = Some(item);
24✔
748
                        Ok(ContinueFrame(false))
24✔
749
                    }
750
                })
48✔
751
            };
752
            Ok(BeginFrame(callback, true, None, DisplayName::String("map")))
300✔
753
        } else {
754
            fn check_duplicate(key: &Bound<PyAny>, dict: &Bound<PyDict>) -> PyResult<()> {
48✔
755
                if dict.contains(key)? {
48✔
756
                    let repr = key.repr()?;
24✔
757
                    return Err(CBORDecodeError::new_err(format!(
24✔
758
                        "Duplicate map key: {}",
759
                        repr.to_str()?
24✔
760
                    )));
761
                }
24✔
762
                Ok(())
24✔
763
            }
48✔
764

765
            let mut dict = PyDict::new(py);
1,164✔
766
            let container = dict.clone().into_any();
1,164✔
767
            let callback: Box<DecoderCallback<'py>> = if let Some(length) = length_or_none {
1,164✔
768
                let mut count = 0usize;
1,116✔
769
                Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
3,544✔
770
                    if let Some(key) = key.take() {
3,544✔
771
                        if !allow_duplicate_keys {
1,772✔
772
                            check_duplicate(&key, &dict)?;
24✔
773
                        }
1,748✔
774
                        dict.set_item(&key, item)?;
1,760✔
775
                        count += 1;
1,760✔
776
                        if count == length {
1,760✔
777
                            let dict = replace(&mut dict, PyDict::new(py));
1,080✔
778
                            let transformed = maybe_call_object_hook(
1,080✔
779
                                py,
1,080✔
780
                                dict.into_any(),
1,080✔
781
                                object_hook.as_ref(),
1,080✔
782
                                immutable,
1,080✔
783
                            )?;
12✔
784
                            return Ok(CompleteFrame(transformed));
1,068✔
785
                        }
680✔
786
                        Ok(ContinueFrame(true))
680✔
787
                    } else {
788
                        key = Some(item);
1,772✔
789
                        Ok(ContinueFrame(false))
1,772✔
790
                    }
791
                })
3,544✔
792
            } else {
793
                let break_marker = BREAK_MARKER.get(py).unwrap().bind(py);
48✔
794
                Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
204✔
795
                    if item.is(break_marker) {
204✔
796
                        let dict = replace(&mut dict, PyDict::new(py));
36✔
797
                        let transformed = maybe_call_object_hook(
36✔
798
                            py,
36✔
799
                            dict.into_any(),
36✔
800
                            object_hook.as_ref(),
36✔
801
                            immutable,
36✔
802
                        )?;
×
803
                        Ok(CompleteFrame(transformed))
36✔
804
                    } else if let Some(key) = key.take() {
168✔
805
                        if !allow_duplicate_keys {
84✔
806
                            check_duplicate(&key, &dict)?;
24✔
807
                        }
60✔
808
                        dict.set_item(&key, item)?;
72✔
809
                        Ok(ContinueFrame(true))
72✔
810
                    } else {
811
                        key = Some(item);
84✔
812
                        Ok(ContinueFrame(false))
84✔
813
                    }
814
                })
204✔
815
            };
816
            Ok(BeginFrame(
1,164✔
817
                callback,
1,164✔
818
                true,
1,164✔
819
                Some(container),
1,164✔
820
                DisplayName::String("map"),
1,164✔
821
            ))
1,164✔
822
        }
823
    }
1,884✔
824

825
    fn decode_semantic<'py>(
3,148✔
826
        &mut self,
3,148✔
827
        py: Python<'py>,
3,148✔
828
        subtype: u8,
3,148✔
829
        immutable: bool,
3,148✔
830
    ) -> PyResult<DecoderResult<'py>> {
3,148✔
831
        let tagnum = self.decode_length_finite(py, subtype)?;
3,148✔
832
        if let Some(semantic_decoders) = &self.semantic_decoders {
3,148✔
833
            match semantic_decoders.bind(py).get_item(tagnum) {
120✔
834
                Ok(decoder) => {
96✔
835
                    let name = decoder.getattr_opt(intern!(py, NAME_ATTR))?;
96✔
836

837
                    // If these attributes are present, this callable was decorated with
838
                    // @shareable_decoder
839
                    return if let Some(name) = name {
96✔
840
                        let require_immutable: bool = decoder
60✔
841
                            .getattr_opt(intern!(py, IMMUTABLE_ATTR))?
60✔
842
                            .map(|x| x.is_truthy())
60✔
843
                            .transpose()?
60✔
844
                            .unwrap_or(false);
60✔
845
                        let retval = decoder.call1((immutable,))?;
60✔
846
                        let tuple: Bound<'_, PyTuple> = retval.cast_into()?;
60✔
847
                        if tuple.len() != 2 {
60✔
848
                            return Err(CBORDecodeError::new_err(format!(
×
849
                                "{decoder} returned a tuple of {} items, expected 2",
×
850
                                tuple.len()
×
851
                            )));
×
852
                        }
60✔
853
                        let container: Bound<'_, PyAny> = tuple.get_item(0)?.cast_into()?;
60✔
854
                        let callback: Bound<'_, PyAny> = tuple.get_item(1)?.cast_into()?;
60✔
855
                        Ok(BeginFrame(
856
                            Box::new(
60✔
857
                                move |item, _immutable: bool| -> PyResult<DecoderResult<'py>> {
60✔
858
                                    callback.call1((item,)).map(CompleteFrame)
60✔
859
                                },
60✔
860
                            ),
861
                            require_immutable,
60✔
862
                            if container.is_none() {
60✔
863
                                None
48✔
864
                            } else {
865
                                Some(container)
12✔
866
                            },
867
                            if name.is_none() {
60✔
868
                                DisplayName::SemanticTag(tagnum)
12✔
869
                            } else {
870
                                DisplayName::PythonName(name.clone())
48✔
871
                            },
872
                        ))
873
                    } else {
874
                        let callback =
36✔
875
                            move |item, new_immutable: bool| -> PyResult<DecoderResult<'py>> {
36✔
876
                                decoder.call1((item, new_immutable)).map(CompleteFrame)
36✔
877
                            };
36✔
878
                        Ok(BeginFrame(
36✔
879
                            Box::new(callback),
36✔
880
                            immutable,
36✔
881
                            None,
36✔
882
                            DisplayName::SemanticTag(tagnum),
36✔
883
                        ))
36✔
884
                    };
885
                }
886
                Err(e) if e.is_instance_of::<PyLookupError>(py) => {}
24✔
887
                Err(e) => return Err(e),
×
888
            }
889
        };
3,028✔
890

891
        // No semantic decoder lookup map – fall back to the hard coded switchboard
892
        let (callback, typename): (Box<DecoderCallback<'py>>, &str) = match tagnum {
3,052✔
893
            0 => (
604✔
894
                Box::new(Self::decode_datetime_string),
604✔
895
                "string-form datetime",
604✔
896
            ),
604✔
897
            1 => (Box::new(Self::decode_epoch_datetime), "epoch-form datetime"),
60✔
898
            2 => (Box::new(Self::decode_positive_bignum), "positive bignum"),
84✔
899
            3 => (Box::new(Self::decode_negative_bignum), "negative bignum"),
36✔
900
            4 => (Box::new(Self::decode_fraction), "decimal fraction"),
112✔
901
            5 => (Box::new(Self::decode_bigfloat), "bigfloat"),
24✔
902
            25 => (Box::new(Self::decode_stringref), "string reference"),
132✔
903
            28 => return Ok(Shareable),
240✔
904
            29 => (Box::new(Self::decode_sharedref), "shared reference"),
180✔
905
            30 => (Box::new(Self::decode_rational), "rational"),
100✔
906
            35 => (Box::new(Self::decode_regexp), "regular expression"),
36✔
907
            36 => (Box::new(Self::decode_mime), "MIME message"),
36✔
908
            37 => (Box::new(Self::decode_uuid), "UUID"),
200✔
909
            52 => (Box::new(Self::decode_ipv4), "IPv4 address"),
172✔
910
            54 => (Box::new(Self::decode_ipv6), "IPv6 address"),
132✔
911
            100 => (Box::new(Self::decode_epoch_date), "epoch-form date"),
12✔
912
            256 => return Ok(StringNamespace),
72✔
913
            258 => return self.decode_set(py, immutable),
268✔
914
            260 => (Box::new(Self::decode_ipaddress), "IP address"),
84✔
915
            261 => (Box::new(Self::decode_ipnetwork), "IP network"),
84✔
916
            1004 => (Box::new(Self::decode_date_string), "string-form date"),
12✔
917
            43000 => (Box::new(Self::decode_complex), "complex number"),
252✔
918
            55799 => (
24✔
919
                Box::new(Self::decode_self_describe_cbor),
24✔
920
                "self-described CBOR value",
24✔
921
            ),
24✔
922
            _ => {
923
                // For a tag with no designated decoder, check if we have a tag hook, and call
924
                // that with the tag object, using its return value as the decoded value.
925
                let tag = CBORTag::new(tagnum.into_bound_py_any(py)?, py.None().into_bound(py))?;
96✔
926
                let bound_tag = Bound::new(py, tag)?.into_any();
96✔
927
                let container = bound_tag.clone();
96✔
928
                let mut tag_hook = self
96✔
929
                    .tag_hook
96✔
930
                    .as_ref()
96✔
931
                    .map(|hook| hook.clone_ref(py).into_bound(py));
96✔
932
                let callback = Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
96✔
933
                    let tag: &Bound<'py, CBORTag> = bound_tag.cast()?;
84✔
934
                    tag.borrow_mut().value = item.unbind();
84✔
935
                    if let Some(tag_hook) = tag_hook.take() {
84✔
936
                        tag_hook.call1((&bound_tag, immutable)).map(CompleteFrame)
60✔
937
                    } else {
938
                        Ok(CompleteFrame(bound_tag.clone()))
24✔
939
                    }
940
                });
84✔
941
                return Ok(BeginFrame(
96✔
942
                    callback,
96✔
943
                    true,
96✔
944
                    Some(container),
96✔
945
                    DisplayName::SemanticTag(tagnum),
96✔
946
                ));
96✔
947
            }
948
        };
949
        Ok(BeginFrame(
2,376✔
950
            callback,
2,376✔
951
            true,
2,376✔
952
            None,
2,376✔
953
            DisplayName::String(typename),
2,376✔
954
        ))
2,376✔
955
    }
3,148✔
956

957
    fn decode_special<'py>(
8,008✔
958
        &mut self,
8,008✔
959
        py: Python<'py>,
8,008✔
960
        subtype: u8,
8,008✔
961
    ) -> PyResult<DecoderResult<'py>> {
8,008✔
962
        // Major tag 7
963
        match subtype {
8,008✔
964
            0..20 => {
8,008✔
965
                let value = subtype.into_pyobject(py)?;
72✔
966
                CBORSimpleValue::new(value)?.into_bound_py_any(py)
72✔
967
            }
968
            20 => Ok(false.into_bound_py_any(py)?),
128✔
969
            21 => Ok(true.into_bound_py_any(py)?),
180✔
970
            22 => Ok(py.None().into_bound_py_any(py)?),
604✔
971
            23 => Ok(UNDEFINED.get(py).unwrap().into_bound_py_any(py)?),
24✔
972
            24 => {
973
                let value = self.read_exact::<1>(py)?[0];
84✔
974
                if value < 0x20 {
84✔
975
                    return Err(CBORDecodeError::new_err(
36✔
976
                        "invalid two-byte sequence for simple value",
36✔
977
                    ));
36✔
978
                }
48✔
979
                CBORSimpleValue::new(value.into_pyobject(py)?)?.into_bound_py_any(py)
48✔
980
            }
981
            25 => {
982
                let bytes = self.read_exact::<2>(py)?;
740✔
983
                f16::from_be_bytes(bytes).to_f32().into_bound_py_any(py)
740✔
984
            }
985
            26 => {
986
                let bytes = self.read_exact::<4>(py)?;
108✔
987
                f32::from_be_bytes(bytes).into_bound_py_any(py)
108✔
988
            }
989
            27 => {
990
                let bytes = self.read_exact::<8>(py)?;
5,864✔
991
                f64::from_be_bytes(bytes).into_bound_py_any(py)
5,864✔
992
            }
993
            31 => Ok(BREAK_MARKER.get(py).unwrap().into_bound_py_any(py)?),
168✔
994
            _ => Err(CBORDecodeError::new_err(format!(
36✔
995
                "undefined reserved major type 7 subtype 0x{subtype:x}"
36✔
996
            ))),
36✔
997
        }
998
        .map(Value)
7,972✔
999
    }
8,008✔
1000

1001
    //
1002
    // Decoders for semantic tags (major tag 6)
1003
    //
1004

1005
    fn decode_datetime_string<'py>(
604✔
1006
        value: Bound<'py, PyAny>,
604✔
1007
        _immutable: bool,
604✔
1008
    ) -> PyResult<DecoderResult<'py>> {
604✔
1009
        // Semantic tag 0
1010
        let py = value.py();
604✔
1011
        let value_type = value.get_type();
604✔
1012
        let mut datetime_str: Bound<'py, PyString> = value.cast_into().map_err(|e| {
604✔
1013
            create_exc_from(
×
1014
                py,
×
1015
                CBORDecodeError::new_err(format!(
×
1016
                    "expected string for tag, got {} instead",
1017
                    value_type
1018
                )),
1019
                Some(PyErr::from(e)),
×
1020
            )
1021
        })?;
×
1022

1023
        // Python 3.10 has impaired parsing of the ISO format:
1024
        // * It doesn't handle the standard "Z" suffix
1025
        // * It doesn't handle the fractional seconds part having fewer than 6 digits
1026
        if py.version_info() <= (3, 10) {
604✔
1027
            // Convert Z to +00:00
1028
            let mut temp_str = datetime_str.to_string().replacen("Z", "+00:00", 1);
134✔
1029

1030
            // Pad any microseconds part with zeros
1031
            if let Some((first, second)) = temp_str.split_once('.')
134✔
1032
                && let Some(index) = second.find(|c: char| !c.is_numeric())
681✔
1033
            {
1034
                let (mut micros, tz_part) = second.split_at(index);
93✔
1035
                // Cut off excess zeroes from the start of the microseconds part
1036
                if micros.len() >= 6 {
93✔
1037
                    micros = &micros[..6];
78✔
1038
                }
78✔
1039

1040
                // Reconstitute the datetime string, right-padding the microseconds part
1041
                // with zeroes
1042
                temp_str = format!("{first}.{micros:0<6}{tz_part}");
93✔
1043
            }
41✔
1044

1045
            datetime_str = temp_str.into_pyobject(py)?;
134✔
1046
        }
470✔
1047

1048
        DATETIME_FROMISOFORMAT
604✔
1049
            .get(py)?
604✔
1050
            .call1((&datetime_str,))
604✔
1051
            .map(CompleteFrame)
604✔
1052
    }
604✔
1053

1054
    fn decode_epoch_datetime(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
60✔
1055
        // Semantic tag 1
1056
        let py = value.py();
60✔
1057
        let utc = UTC.get(py)?;
60✔
1058
        DATETIME_FROMTIMESTAMP
60✔
1059
            .get(py)?
60✔
1060
            .call1((value, utc))
60✔
1061
            .map(CompleteFrame)
60✔
1062
    }
60✔
1063

1064
    fn decode_positive_bignum(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
60✔
1065
        // Semantic tag 2
1066
        let py = value.py();
60✔
1067
        INT_FROMBYTES
60✔
1068
            .get(py)?
60✔
1069
            .call1((value, intern!(py, "big")))
60✔
1070
            .map(CompleteFrame)
60✔
1071
    }
60✔
1072

1073
    fn decode_negative_bignum(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
36✔
1074
        // Semantic tag 3
1075
        let py = value.py();
36✔
1076
        let int = INT_FROMBYTES.get(py)?.call1((value, intern!(py, "big")))?;
36✔
1077
        int.neg()?.add(-1).map(CompleteFrame)
36✔
1078
    }
36✔
1079

1080
    fn decode_fraction(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
112✔
1081
        // Semantic tag 4
1082
        let py = value.py();
112✔
1083
        let tuple = require_tuple(value, 2)?;
112✔
1084
        let decimal_class = DECIMAL_TYPE.get(py)?;
100✔
1085
        {
1086
            let exp = tuple.get_item(0)?;
100✔
1087
            let sig_tuple = decimal_class
100✔
1088
                .call1((tuple.get_item(1)?,))?
100✔
1089
                .call_method0(intern!(py, "as_tuple"))?
100✔
1090
                .cast_into::<PyTuple>()?;
100✔
1091
            let sign = sig_tuple.get_item(0)?;
100✔
1092
            let digits = sig_tuple.get_item(1)?;
100✔
1093
            let args_tuple = PyTuple::new(py, [sign, digits, exp])?;
100✔
1094
            decimal_class.call1((args_tuple,)).map(CompleteFrame)
100✔
1095
        }
1096
    }
112✔
1097

1098
    fn decode_bigfloat(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
24✔
1099
        // Semantic tag 5
1100
        let py = value.py();
24✔
1101
        let tuple = require_tuple(value, 2)?;
24✔
1102
        let decimal_class = DECIMAL_TYPE.get(py)?;
12✔
1103
        {
1104
            let exp = decimal_class.call1((tuple.get_item(0)?,))?;
12✔
1105
            let sig = decimal_class.call1((tuple.get_item(1)?,))?;
12✔
1106
            let exp = PyInt::new(py, 2).pow(exp, py.None())?;
12✔
1107
            sig.mul(exp).map(CompleteFrame)
12✔
1108
        }
1109
    }
24✔
1110

1111
    fn decode_stringref(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
132✔
1112
        // Semantic tag 25
1113
        let index: usize = value.extract()?;
132✔
1114
        Ok(StringReference(index))
132✔
1115
    }
132✔
1116

1117
    fn decode_sharedref(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
180✔
1118
        // Semantic tag 29
1119
        let index: usize = value.extract()?;
180✔
1120
        Ok(SharedReference(index))
180✔
1121
    }
180✔
1122

1123
    fn decode_rational(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
100✔
1124
        // Semantic tag 30
1125
        let py = value.py();
100✔
1126
        let tuple = require_tuple(value, 2)?;
100✔
1127
        FRACTION_TYPE.get(py)?.call1(tuple).map(CompleteFrame)
88✔
1128
    }
100✔
1129

1130
    fn decode_regexp(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
36✔
1131
        // Semantic tag 35
1132
        RE_COMPILE
36✔
1133
            .get(value.py())?
36✔
1134
            .call1((value,))
36✔
1135
            .map(CompleteFrame)
36✔
1136
    }
36✔
1137

1138
    fn decode_mime(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
24✔
1139
        // Semantic tag 36
1140
        let py = value.py();
24✔
1141
        let parser = EMAIL_PARSER.get(py)?.call0()?;
24✔
1142
        parser
24✔
1143
            .call_method1(intern!(py, "parsestr"), (value,))
24✔
1144
            .map(CompleteFrame)
24✔
1145
    }
24✔
1146

1147
    fn decode_uuid(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
200✔
1148
        // Semantic tag 37
1149
        let py = value.py();
200✔
1150
        let kwargs = PyDict::new(py);
200✔
1151
        kwargs.set_item(intern!(py, "bytes"), value)?;
200✔
1152
        UUID_TYPE
200✔
1153
            .get(py)?
200✔
1154
            .call((), Some(&kwargs))
200✔
1155
            .map(CompleteFrame)
200✔
1156
    }
200✔
1157

1158
    fn decode_ipv4(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
172✔
1159
        // Semantic tag 52
1160
        let py = value.py();
172✔
1161
        let addr = if let Ok(bytes) = value.cast::<PyBytes>() {
172✔
1162
            // The decoded value was a bytestring, so this is an IPv4 address
1163
            IPV4ADDRESS_TYPE.get(py)?.call1((bytes,))?
136✔
1164
        } else if let Ok(tuple) = value.cast_into::<PyTuple>()
36✔
1165
            && tuple.len() == 2
36✔
1166
        {
1167
            // The decoded value was a 2-item array. Check the types of the elements:
1168
            // (int, bytes) -> network
1169
            // (bytes, int) -> interface
1170
            let first_item = tuple.get_item(0)?;
36✔
1171
            let second_item = tuple.get_item(1)?;
36✔
1172
            if let Ok(prefix) = first_item.cast::<PyInt>()
36✔
1173
                && let Ok(address) = second_item.cast::<PyBytes>()
24✔
1174
            {
1175
                let mut address_vec: Vec<u8> = address.extract()?;
24✔
1176
                if address_vec.len() > 4 {
24✔
1177
                    return Err(CBORDecodeError::new_err(format!(
12✔
1178
                        "address byte string for IPv4 network is too long ({} bytes)",
12✔
1179
                        address_vec.len()
12✔
1180
                    )));
12✔
1181
                }
12✔
1182
                address_vec.resize(4, 0);
12✔
1183
                IPV4NETWORK_TYPE.get(py)?.call1(((address_vec, prefix),))?
12✔
1184
            } else if let Ok(address) = first_item.cast::<PyBytes>()
12✔
1185
                && let Ok(prefix) = second_item.cast::<PyInt>()
12✔
1186
            {
1187
                IPV4INTERFACE_TYPE.get(py)?.call1(((address, prefix),))?
12✔
1188
            } else {
1189
                return Err(CBORDecodeError::new_err("invalid types in input array"));
×
1190
            }
1191
        } else {
1192
            return Err(CBORDecodeError::new_err(
×
1193
                "input value must be a bytestring or an array of 2 elements",
×
1194
            ));
×
1195
        };
1196
        Ok(CompleteFrame(addr))
160✔
1197
    }
172✔
1198

1199
    fn decode_ipv6(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
132✔
1200
        // Semantic tag 54
1201
        let py = value.py();
132✔
1202
        let ipv6addr_class = IPV6ADDRESS_TYPE.get(py)?;
132✔
1203
        let addr = if let Ok(bytes) = value.cast::<PyBytes>() {
132✔
1204
            // The decoded value was a bytestring, so this is an IPv6 address
1205
            ipv6addr_class.call1((bytes,))?
60✔
1206
        } else if let Ok(tuple) = value.cast_into::<PyTuple>()
72✔
1207
            && (2..=3).contains(&tuple.len())
72✔
1208
        {
1209
            // The decoded value was a 2-item (or 3 with zone ID) array.
1210
            // Check the types of the elements:
1211
            // (bytes, null) -> scoped address
1212
            // (int, bytes)  -> network
1213
            // (bytes, int)  -> interface
1214
            let first_item = tuple.get_item(0)?;
72✔
1215
            let second_item = tuple.get_item(1)?;
72✔
1216
            let zone_id = tuple.get_item(2).ok();
72✔
1217

1218
            // Format the zone ID suffix if a zone ID was included
1219
            // (bytes or integer as the last item of a 3-tuple)
1220
            let zone_id_suffix = if let Some(zone_id) = zone_id {
72✔
1221
                if let Ok(zone_id_bytes) = zone_id.cast::<PyBytes>() {
48✔
1222
                    let zone_id_str = String::from_utf8(zone_id_bytes.as_bytes().to_vec())?;
36✔
1223
                    format!("%{zone_id_str}")
36✔
1224
                } else if let Ok(zone_id_int) = zone_id.cast::<PyInt>() {
12✔
1225
                    format!("%{zone_id_int}")
12✔
1226
                } else {
1227
                    return Err(CBORDecodeError::new_err(
×
1228
                        "zone ID must be an integer or a bytestring",
×
1229
                    ));
×
1230
                }
1231
            } else {
1232
                String::default()
24✔
1233
            };
1234

1235
            if second_item.is_none()
72✔
1236
                && let Ok(address) = first_item.cast::<PyBytes>()
24✔
1237
            {
1238
                // A scoped address is encoded as [address, null, zone id]; with no prefix
1239
                // length it decodes to a bare IPv6 address rather than a network or interface.
1240
                let addr_obj = ipv6addr_class.call1((address,))?;
24✔
1241
                ipv6addr_class.call1((format!("{addr_obj}{zone_id_suffix}"),))?
24✔
1242
            } else {
1243
                let (class, addr_bytes, prefix) = if let Ok(prefix) = first_item.cast::<PyInt>()
48✔
1244
                    && let Ok(address) = second_item.cast::<PyBytes>()
24✔
1245
                {
1246
                    let mut address_vec: Vec<u8> = address.extract()?;
24✔
1247
                    if address_vec.len() > 16 {
24✔
1248
                        return Err(CBORDecodeError::new_err(format!(
12✔
1249
                            "address byte string for IPv6 network is too long ({} bytes)",
12✔
1250
                            address_vec.len()
12✔
1251
                        )));
12✔
1252
                    }
12✔
1253
                    address_vec.resize(16, 0);
12✔
1254
                    Ok((
1255
                        IPV6NETWORK_TYPE.get(py)?,
12✔
1256
                        PyBytes::new(py, address_vec.as_slice()),
12✔
1257
                        prefix,
12✔
1258
                    ))
1259
                } else if let Ok(address) = first_item.cast_into::<PyBytes>()
24✔
1260
                    && let Ok(prefix) = second_item.cast::<PyInt>()
24✔
1261
                {
1262
                    Ok((IPV6INTERFACE_TYPE.get(py)?, address, prefix))
24✔
1263
                } else {
NEW
1264
                    Err(CBORDecodeError::new_err("invalid types in input array"))
×
NEW
1265
                }?;
×
1266
                let addr_obj = ipv6addr_class.call1((addr_bytes,))?;
36✔
1267
                let formatted_addr = format!("{addr_obj}{zone_id_suffix}/{prefix}");
36✔
1268
                class.call1((formatted_addr,))?
36✔
1269
            }
1270
        } else {
1271
            return Err(CBORDecodeError::new_err(
×
1272
                "input value must be a bytestring or an array of 2 elements",
×
1273
            ));
×
1274
        };
1275
        Ok(CompleteFrame(addr))
120✔
1276
    }
132✔
1277

1278
    fn decode_epoch_date(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
12✔
1279
        // Semantic tag 100
1280
        let py = value.py();
12✔
1281
        let value = value.extract::<i32>()? + 719163;
12✔
1282
        DATE_FROMORDINAL.get(py)?.call1((value,)).map(CompleteFrame)
12✔
1283
    }
12✔
1284

1285
    fn decode_ipaddress(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
84✔
1286
        // Semantic tag 260 (deprecated)
1287
        let py = value.py();
84✔
1288
        let value = value.cast_into::<PyBytes>()?;
84✔
1289
        let addr_obj = match value.len()? {
72✔
1290
            4 | 16 => IPADDRESS_FUNC.get(py)?.call1((value,)),
48✔
1291
            6 => Ok(Bound::new(py, CBORTag::new_internal(260, value.into_any()))?.into_any()), // MAC address
12✔
1292
            length => Err(CBORDecodeError::new_err(format!(
12✔
1293
                "invalid IP address length ({length})"
12✔
1294
            ))),
12✔
1295
        }?;
12✔
1296
        Ok(CompleteFrame(addr_obj))
60✔
1297
    }
84✔
1298

1299
    fn decode_ipnetwork<'py>(
84✔
1300
        value: Bound<'py, PyAny>,
84✔
1301
        _immutable: bool,
84✔
1302
    ) -> PyResult<DecoderResult<'py>> {
84✔
1303
        // Semantic tag 261 (deprecated)
1304
        let py = value.py();
84✔
1305
        let value: Bound<'py, PyMapping> = value.cast_into()?;
84✔
1306
        let length = value.len()?;
84✔
1307
        if length != 1 {
84✔
1308
            return Err(CBORDecodeError::new_err(format!(
12✔
1309
                "invalid input map length for IP network: {}",
12✔
1310
                length
12✔
1311
            )));
12✔
1312
        }
72✔
1313
        let first_item = value.items()?.get_item(0)?;
72✔
1314
        let mask_length = first_item.get_item(1)?;
72✔
1315
        if !mask_length.is_exact_instance_of::<PyInt>() {
72✔
1316
            return Err(CBORDecodeError::new_err(format!(
12✔
1317
                "invalid mask length for IP network: {mask_length}"
12✔
1318
            )));
12✔
1319
        }
60✔
1320

1321
        let addr_obj = match IPNETWORK_FUNC.get(py)?.call1((&first_item,)) {
60✔
1322
            Ok(ip_network) => Ok(ip_network),
48✔
1323
            Err(e) => {
12✔
1324
                // A CompleteFrameError may indicate that the bytestring has host bits set, so try parsing
1325
                // it as an IP interface instead
1326
                if e.is_instance_of::<PyValueError>(py) {
12✔
1327
                    IPINTERFACE_FUNC.get(py)?.call1((first_item,))
12✔
1328
                } else {
1329
                    Err(e)
×
1330
                }
1331
            }
1332
        }?;
×
1333
        Ok(CompleteFrame(addr_obj))
60✔
1334
    }
84✔
1335

1336
    fn decode_date_string(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
12✔
1337
        // Semantic tag 1004
1338
        let py = value.py();
12✔
1339
        let date = DATE_FROMISOFORMAT.get(py)?.call1((value,))?;
12✔
1340
        Ok(CompleteFrame(date))
12✔
1341
    }
12✔
1342

1343
    fn decode_complex(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
252✔
1344
        // Semantic tag 43000
1345
        let py = value.py();
252✔
1346
        let tuple = require_tuple(value, 2)?;
252✔
1347
        let real: f64 = tuple.get_item(0)?.extract()?;
252✔
1348
        let imag: f64 = tuple.get_item(1)?.extract()?;
252✔
1349
        Ok(CompleteFrame(
252✔
1350
            PyComplex::from_doubles(py, real, imag).into_any(),
252✔
1351
        ))
252✔
1352
    }
252✔
1353

1354
    fn decode_self_describe_cbor(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
24✔
1355
        // Semantic tag 55799
1356
        Ok(CompleteFrame(value))
24✔
1357
    }
24✔
1358

1359
    fn decode_set<'py>(
268✔
1360
        &mut self,
268✔
1361
        py: Python<'py>,
268✔
1362
        immutable: bool,
268✔
1363
    ) -> PyResult<DecoderResult<'py>> {
268✔
1364
        // Semantic tag 258
1365
        let mut set_or_none = if immutable {
268✔
1366
            None
36✔
1367
        } else {
1368
            Some(PySet::empty(py)?.into_any())
232✔
1369
        };
1370
        let container = set_or_none.clone();
268✔
1371
        let callback = move |item: Bound<'py, PyAny>, _immutable: bool| {
268✔
1372
            let container: Bound<'py, PyAny> = if let Some(set) = set_or_none.take() {
256✔
1373
                set.call_method1(intern!(py, "update"), (item,))?;
220✔
1374
                set.into_any()
220✔
1375
            } else {
1376
                let tuple = item.cast_into::<PyTuple>()?;
36✔
1377
                PyFrozenSet::new(py, tuple)?.into_any()
36✔
1378
            };
1379
            Ok(CompleteFrame(container))
256✔
1380
        };
256✔
1381
        Ok(BeginFrame(
268✔
1382
            Box::new(callback),
268✔
1383
            true,
268✔
1384
            container,
268✔
1385
            DisplayName::String("set"),
268✔
1386
        ))
268✔
1387
    }
268✔
1388
}
1389

1390
#[pymethods]
×
1391
impl CBORDecoder {
1392
    #[new]
1393
    #[pyo3(signature = (
1394
        fp,
1395
        *,
1396
        tag_hook = None,
1397
        object_hook = None,
1398
        semantic_decoders = None,
1399
        str_errors = "strict",
1400
        read_size = 4096,
1401
        max_depth = 400,
1402
        allow_indefinite = true,
1403
        allow_duplicate_keys = true,
1404
    ))]
1405
    pub fn new(
444✔
1406
        py: Python<'_>,
444✔
1407
        fp: &Bound<'_, PyAny>,
444✔
1408
        tag_hook: Option<&Bound<'_, PyAny>>,
444✔
1409
        object_hook: Option<&Bound<'_, PyAny>>,
444✔
1410
        semantic_decoders: Option<&Bound<'_, PyMapping>>,
444✔
1411
        str_errors: &str,
444✔
1412
        read_size: usize,
444✔
1413
        max_depth: usize,
444✔
1414
        allow_indefinite: bool,
444✔
1415
        allow_duplicate_keys: bool,
444✔
1416
    ) -> PyResult<Self> {
444✔
1417
        Self::new_internal(
444✔
1418
            py,
444✔
1419
            Some(fp),
444✔
1420
            None,
444✔
1421
            tag_hook,
444✔
1422
            object_hook,
444✔
1423
            semantic_decoders,
444✔
1424
            str_errors,
444✔
1425
            read_size,
444✔
1426
            max_depth,
444✔
1427
            allow_indefinite,
444✔
1428
            allow_duplicate_keys,
444✔
1429
        )
1430
    }
444✔
1431

1432
    #[getter]
1433
    fn fp(&self, py: Python<'_>) -> Option<Py<PyAny>> {
×
1434
        self.fp.as_ref().map(|fp| fp.clone_ref(py))
×
1435
    }
×
1436

1437
    #[setter]
1438
    fn set_fp(&mut self, fp: &Bound<'_, PyAny>) -> PyResult<()> {
456✔
1439
        let result = fp.call_method0("readable");
456✔
1440
        if let Ok(readable) = &result
456✔
1441
            && readable.is_truthy()?
444✔
1442
        {
1443
            self.fp_is_seekable = fp.call_method0("seekable")?.is_truthy()?;
432✔
1444
            let fp = fp.clone();
432✔
1445
            self.read_method = Some(fp.getattr("read")?.unbind());
432✔
1446
            self.fp = Some(fp.unbind());
432✔
1447
            self.available_bytes = 0;
432✔
1448
            self.read_position = 0;
432✔
1449
            self.buffer = None;
432✔
1450
            Ok(())
432✔
1451
        } else {
1452
            raise_exc_from(
24✔
1453
                fp.py(),
24✔
1454
                PyValueError::new_err("fp must be a readable file-like object"),
24✔
1455
                result.err(),
24✔
1456
            )
1457
        }
1458
    }
456✔
1459

1460
    #[getter]
1461
    fn tag_hook(&self, py: Python<'_>) -> Option<Py<PyAny>> {
12✔
1462
        self.tag_hook
12✔
1463
            .as_ref()
12✔
1464
            .map(|tag_hook| tag_hook.clone_ref(py))
12✔
1465
    }
12✔
1466

1467
    #[setter]
1468
    fn set_tag_hook(&mut self, tag_hook: Option<&Bound<'_, PyAny>>) -> PyResult<()> {
4,692✔
1469
        if let Some(tag_hook) = tag_hook {
4,692✔
1470
            if !tag_hook.is_callable() {
132✔
1471
                return Err(PyErr::new::<PyTypeError, _>(
12✔
1472
                    "tag_hook must be callable or None",
12✔
1473
                ));
12✔
1474
            }
120✔
1475

1476
            self.tag_hook = Some(tag_hook.clone().unbind());
120✔
1477
        } else {
4,560✔
1478
            self.tag_hook = None;
4,560✔
1479
        }
4,560✔
1480
        Ok(())
4,680✔
1481
    }
4,692✔
1482

1483
    #[getter]
1484
    fn object_hook(&self, py: Python<'_>) -> Option<Py<PyAny>> {
12✔
1485
        self.object_hook
12✔
1486
            .as_ref()
12✔
1487
            .map(|object_hook| object_hook.clone_ref(py))
12✔
1488
    }
12✔
1489

1490
    #[setter]
1491
    fn set_object_hook(&mut self, object_hook: Option<&Bound<'_, PyAny>>) -> PyResult<()> {
4,680✔
1492
        if let Some(object_hook) = object_hook {
4,680✔
1493
            if !object_hook.is_callable() {
48✔
1494
                return Err(PyErr::new::<PyTypeError, _>(
12✔
1495
                    "object_hook must be callable or None",
12✔
1496
                ));
12✔
1497
            }
36✔
1498

1499
            self.object_hook = Some(object_hook.clone().unbind());
36✔
1500
        } else {
4,632✔
1501
            self.object_hook = None;
4,632✔
1502
        }
4,632✔
1503
        Ok(())
4,668✔
1504
    }
4,680✔
1505

1506
    #[getter]
1507
    fn str_errors(&self, py: Python<'_>) -> Py<PyString> {
60✔
1508
        if let Some(str_errors) = self.str_errors.as_ref() {
60✔
1509
            str_errors.clone_ref(py)
48✔
1510
        } else {
1511
            intern!(py, "strict").clone().unbind()
12✔
1512
        }
1513
    }
60✔
1514

1515
    #[setter]
1516
    fn set_str_errors(&mut self, str_errors: &Bound<'_, PyString>) -> PyResult<()> {
4,668✔
1517
        let as_string: &str = str_errors.extract()?;
4,668✔
1518
        self.str_errors = match as_string {
4,668✔
1519
            "strict" => None,
4,668✔
1520
            "ignore" | "replace" | "backslashreplace" | "surrogateescape" => {
108✔
1521
                Some(str_errors.clone().unbind())
96✔
1522
            }
1523
            _ => {
1524
                return Err(PyValueError::new_err(format!(
12✔
1525
                    "invalid str_errors value: '{str_errors}'"
12✔
1526
                )));
12✔
1527
            }
1528
        };
1529
        Ok(())
4,656✔
1530
    }
4,668✔
1531

1532
    /// Read bytes from the data stream.
1533
    ///
1534
    /// :param amount: the number of bytes to read
1535
    #[pyo3(signature = (amount, /))]
1536
    fn read(&mut self, py: Python<'_>, amount: usize) -> PyResult<Vec<u8>> {
10,390,388✔
1537
        if amount == 0 {
10,390,388✔
1538
            return Ok(Vec::default());
224✔
1539
        }
10,390,164✔
1540

1541
        if self.available_bytes == 0 {
10,390,164✔
1542
            // No buffer
1543
            let (new_bytes, amount_read) = self.read_from_fp(py, amount)?;
72✔
1544
            self.read_position = amount;
12✔
1545
            self.available_bytes = amount_read - amount;
12✔
1546
            let new_buffer = new_bytes.as_bytes()[..amount].to_vec();
12✔
1547
            self.buffer = Some(new_bytes.unbind());
12✔
1548
            Ok(new_buffer)
12✔
1549
        } else if self.available_bytes < amount {
10,390,092✔
1550
            // Combine the remnants of the partial buffer with new data read from the file
1551
            let needed_bytes = amount - self.available_bytes;
96✔
1552
            let mut concatenated_buffer: Vec<u8> =
96✔
1553
                self.buffer.take().unwrap().as_bytes(py)[self.read_position..].to_vec();
96✔
1554
            let (new_bytes, amount_read) = self.read_from_fp(py, needed_bytes)?;
96✔
1555
            concatenated_buffer.extend_from_slice(&new_bytes[..needed_bytes]);
24✔
1556
            self.buffer = Some(new_bytes.unbind());
24✔
1557
            self.available_bytes = amount_read - needed_bytes;
24✔
1558
            self.read_position = needed_bytes;
24✔
1559
            Ok(concatenated_buffer)
24✔
1560
        } else {
1561
            // Return a slice from the existing bytes object
1562
            let vec = self.buffer.as_ref().unwrap().as_bytes(py)
10,389,996✔
1563
                [self.read_position..self.read_position + amount]
10,389,996✔
1564
                .to_vec();
10,389,996✔
1565
            self.available_bytes -= amount;
10,389,996✔
1566
            self.read_position += amount;
10,389,996✔
1567
            Ok(vec)
10,389,996✔
1568
        }
1569
    }
10,390,388✔
1570

1571
    /// Decode the next value from the stream.
1572
    ///
1573
    /// :param immutable: if :data:`True`, decode the next item as an immutable type
1574
    ///     (e.g. :class:`tuple` instead of a :class:`list`), if possible
1575
    /// :return: the decoded object
1576
    /// :raises CBORDecodeError: if there is any problem decoding the stream
1577
    #[pyo3(signature = (*, immutable = false))]
1578
    pub fn decode<'py>(&mut self, py: Python<'py>, immutable: bool) -> PyResult<Bound<'py, PyAny>> {
4,536✔
1579
        let mut frames: Vec<StackFrame> = Vec::new();
4,536✔
1580

1581
        fn add_frame<'a>(
11,608✔
1582
            frames: &mut Vec<StackFrame<'a>>,
11,608✔
1583
            max_depth: usize,
11,608✔
1584
            frame: StackFrame<'a>,
11,608✔
1585
        ) -> PyResult<()> {
11,608✔
1586
            if frames.len() == max_depth {
11,608✔
1587
                return Err(CBORDecodeError::new_err(format!(
24✔
1588
                    "maximum container nesting depth ({max_depth}) exceeded",
24✔
1589
                )));
24✔
1590
            }
11,584✔
1591

1592
            frames.push(frame);
11,584✔
1593
            Ok(())
11,584✔
1594
        }
11,608✔
1595

1596
        fn wrap_exception(py: Python<'_>, err: PyErr, typename: &DisplayName) -> PyErr {
660✔
1597
            if err.is_instance_of::<CBORDecodeEOF>(py) {
660✔
1598
                err
120✔
1599
            } else if err.is_instance_of::<CBORDecodeError>(py) {
540✔
1600
                CBORDecodeError::new_err(format!(
300✔
1601
                    "error decoding {}: {}",
1602
                    typename,
1603
                    err.arguments(py)
300✔
1604
                ))
1605
            } else {
1606
                create_exc_from(
240✔
1607
                    py,
240✔
1608
                    CBORDecodeError::new_err(format!("error decoding {}", typename)),
240✔
1609
                    Some(err),
240✔
1610
                )
1611
            }
1612
        }
660✔
1613

1614
        let mut shareables: Vec<Option<Bound<'py, PyAny>>> = Vec::new();
4,536✔
1615
        let mut string_namespaces: Vec<Vec<Bound<'py, PyAny>>> = Vec::new();
4,536✔
1616
        let mut value: Option<Bound<'py, PyAny>> = None;
4,536✔
1617
        let mut current_immutable: bool = immutable;
4,536✔
1618
        loop {
1619
            let result: PyResult<DecoderResult<'py>> = if let Some(previous_value) = value.take() {
1,618,444✔
1620
                // Call the decoder callback of the last frame
1621
                let frame = frames.last_mut().unwrap();
804,248✔
1622
                if let Some(decoder_callback) = frame.decoder_callback.as_mut() {
804,248✔
1623
                    decoder_callback(previous_value, frame.immutable)
804,128✔
1624
                        .map_err(|e| wrap_exception(py, e, &frame.typename))
804,128✔
1625
                } else if frame.contains_string_namespace {
120✔
1626
                    string_namespaces
48✔
1627
                        .pop()
48✔
1628
                        .expect("no string namespaces to pop from");
48✔
1629
                    Ok(CompleteFrame(previous_value))
48✔
1630
                } else if let Some(shareable_index) = frame.shareable_index {
72✔
1631
                    shareables[shareable_index].get_or_insert_with(|| previous_value.clone());
72✔
1632
                    Ok(CompleteFrame(previous_value))
72✔
1633
                } else {
1634
                    panic!("no decoder callback, shareable index or string namespace");
×
1635
                }
1636
            } else {
1637
                let (major_type, subtype) = self.read_major_and_subtype(py)?;
814,196✔
1638
                match major_type {
814,172✔
1639
                    0 => self.decode_uint(py, subtype),
3,264✔
1640
                    1 => self.decode_negint(py, subtype),
380✔
1641
                    2 => self.decode_bytestring(py, subtype),
1,296✔
1642
                    3 => self.decode_string(py, subtype),
789,008✔
1643
                    4 => self.decode_array(py, subtype, current_immutable),
7,184✔
1644
                    5 => self.decode_map(py, subtype, current_immutable),
1,884✔
1645
                    6 => self.decode_semantic(py, subtype, current_immutable),
3,148✔
1646
                    7 => self.decode_special(py, subtype),
8,008✔
1647
                    _ => Err(CBORDecodeError::new_err(format!(
×
1648
                        "invalid major type: {major_type}"
×
1649
                    ))),
×
1650
                }
1651
                .map_err(|e| {
814,172✔
1652
                    let typename = match major_type {
360✔
1653
                        0 => "unsigned integer",
12✔
1654
                        1 => "negative integer",
×
1655
                        2 => "byte string",
108✔
1656
                        3 => "text string",
168✔
1657
                        4 => "array",
×
1658
                        5 => "map",
×
1659
                        6 => "semantic tag",
×
1660
                        7 => "special value",
72✔
1661
                        _ => unreachable!("invalid major types should have been handled earlier"),
×
1662
                    };
1663
                    wrap_exception(py, e, &DisplayName::String(typename))
360✔
1664
                })
360✔
1665
            };
1666

1667
            match result {
1,617,760✔
1668
                Ok(BeginFrame(callback, requested_immutable, container, typename)) => {
11,296✔
1669
                    if let Some(frame) = frames.last_mut()
11,296✔
1670
                        && let Some(container) = container
8,656✔
1671
                        && let Some(shareable_index) = frame.shareable_index
6,076✔
1672
                    {
156✔
1673
                        frames.pop();
156✔
1674
                        shareables[shareable_index] = Some(container.clone());
156✔
1675
                    }
11,140✔
1676
                    current_immutable = current_immutable || requested_immutable;
11,296✔
1677
                    add_frame(
11,296✔
1678
                        &mut frames,
11,296✔
1679
                        self.max_depth,
11,296✔
1680
                        StackFrame {
11,296✔
1681
                            immutable: current_immutable,
11,296✔
1682
                            decoder_callback: Some(callback),
11,296✔
1683
                            shareable_index: None,
11,296✔
1684
                            typename,
11,296✔
1685
                            contains_string_namespace: false,
11,296✔
1686
                        },
11,296✔
1687
                    )?;
24✔
1688
                }
1689
                Ok(ContinueFrame(require_immutable)) => {
798,076✔
1690
                    // If require_immutable is true, the next value must be immutable
1691
                    // Otherwise, restore the immutable flag to the previous value
1692
                    current_immutable = if frames.len() >= 2 {
798,076✔
1693
                        frames.get(frames.len() - 2).unwrap().immutable
789,208✔
1694
                    } else {
1695
                        immutable
8,868✔
1696
                    } || require_immutable;
796,616✔
1697
                    frames.last_mut().unwrap().immutable = current_immutable;
798,076✔
1698
                }
1699
                Ok(CompleteFrame(new_value)) => {
5,560✔
1700
                    frames
5,560✔
1701
                        .pop()
5,560✔
1702
                        .expect("received frame completion but there are no frames on the stack");
5,560✔
1703
                    current_immutable = frames.last().map_or(immutable, |frame| frame.immutable);
5,560✔
1704
                    value = Some(new_value);
5,560✔
1705
                }
1706
                Ok(Value(new_value)) => {
12,236✔
1707
                    value = Some(new_value);
12,236✔
1708
                }
12,236✔
1709
                Ok(StringNamespace) => {
1710
                    add_frame(
72✔
1711
                        &mut frames,
72✔
1712
                        self.max_depth,
72✔
1713
                        StackFrame {
72✔
1714
                            immutable: current_immutable,
72✔
1715
                            decoder_callback: None,
72✔
1716
                            shareable_index: None,
72✔
1717
                            typename: DisplayName::String("string namespace"),
72✔
1718
                            contains_string_namespace: true,
72✔
1719
                        },
72✔
1720
                    )?;
×
1721
                    string_namespaces.push(Vec::new());
72✔
1722
                }
1723
                Ok(StringValue(string, length)) => {
789,968✔
1724
                    // Conditionally add the string to the innermost string namespace
1725
                    if let Some(namespace) = string_namespaces.last_mut()
789,968✔
1726
                        && match namespace.len() {
786,540✔
1727
                            0..24 => length >= 3,
786,540✔
1728
                            24..256 => length >= 4,
786,168✔
1729
                            256..65536 => length >= 5,
783,384✔
1730
                            65536..=4294967295 => length >= 7,
24✔
1731
                            _ => length >= 11,
×
1732
                        }
1733
                    {
786,528✔
1734
                        namespace.push(string.clone());
786,528✔
1735
                    }
786,528✔
1736
                    value = Some(string);
789,968✔
1737
                }
1738
                Ok(StringReference(index)) => {
132✔
1739
                    frames
132✔
1740
                        .pop()
132✔
1741
                        .expect("  received string reference but there are no frames on the stack");
132✔
1742
                    if let Some(namespace) = string_namespaces.last() {
132✔
1743
                        if let Some(string) = namespace.get(index) {
120✔
1744
                            value = Some(string.clone());
108✔
1745
                        } else {
108✔
1746
                            return Err(CBORDecodeError::new_err(format!(
12✔
1747
                                "string reference {index} not found"
12✔
1748
                            )));
12✔
1749
                        }
1750
                    } else {
1751
                        return Err(CBORDecodeError::new_err(
12✔
1752
                            "string reference outside of namespace",
12✔
1753
                        ));
12✔
1754
                    }
1755
                    current_immutable = frames
108✔
1756
                        .last()
108✔
1757
                        .map_or(current_immutable, |frame| frame.immutable);
108✔
1758
                }
1759
                Ok(Shareable) => {
1760
                    add_frame(
240✔
1761
                        &mut frames,
240✔
1762
                        self.max_depth,
240✔
1763
                        StackFrame {
240✔
1764
                            immutable: current_immutable,
240✔
1765
                            decoder_callback: None,
240✔
1766
                            shareable_index: Some(shareables.len()),
240✔
1767
                            typename: DisplayName::String("shareable value"),
240✔
1768
                            contains_string_namespace: false,
240✔
1769
                        },
240✔
1770
                    )?;
×
1771
                    shareables.push(None);
240✔
1772
                }
1773
                Ok(SharedReference(index)) => {
180✔
1774
                    frames
180✔
1775
                        .pop()
180✔
1776
                        .expect("received shared reference but there are no frames on the stack");
180✔
1777
                    value = match shareables.get(index) {
180✔
1778
                        Some(Some(value)) => Some(value.clone()),
144✔
1779
                        Some(None) => {
1780
                            return Err(CBORDecodeError::new_err(format!(
12✔
1781
                                "shared value {index} has not been initialized"
12✔
1782
                            )));
12✔
1783
                        }
1784
                        None => {
1785
                            return Err(CBORDecodeError::new_err(format!(
24✔
1786
                                "shared reference {index} not found"
24✔
1787
                            )));
24✔
1788
                        }
1789
                    };
1790
                    current_immutable = frames
144✔
1791
                        .last()
144✔
1792
                        .map_or(current_immutable, |frame| frame.immutable);
144✔
1793
                }
1794
                Err(err) => {
660✔
1795
                    // If an Exception was raised, wrap it in a CBORDecodeError
1796
                    // If a ValueError was raised, wrap it in a CBORDecodeError
1797
                    return if err.is_instance_of::<CBORDecodeError>(py) {
660✔
1798
                        Err(err)
660✔
1799
                    } else if err.is_instance_of::<PyValueError>(py)
×
1800
                        || err.is_instance_of::<PyException>(py)
×
1801
                    {
1802
                        Err(create_exc_from(
×
1803
                            py,
×
1804
                            CBORDecodeError::new_err(err.to_string()),
×
1805
                            Some(err),
×
1806
                        ))
×
1807
                    } else {
1808
                        Err(err)
×
1809
                    };
1810
                }
1811
            }
1812

1813
            if frames.is_empty() {
1,617,676✔
1814
                // If fp was seekable and excess data has been read, empty the buffer and
1815
                // rewind the file
1816
                if self.available_bytes > 0
3,768✔
1817
                    && let Some(fp) = &self.fp
24✔
1818
                {
1819
                    let offset = -(self.available_bytes as isize);
24✔
1820
                    fp.call_method1(py, intern!(py, "seek"), (offset, SEEK_CUR))?;
24✔
1821
                    self.buffer = None;
24✔
1822
                    self.available_bytes = 0;
24✔
1823
                    self.read_position = 0;
24✔
1824
                }
3,744✔
1825
                return Ok(value.expect("stack is empty but final return value is missing"));
3,768✔
1826
            }
1,613,908✔
1827
        }
1828
    }
4,536✔
1829
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc