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

agronholm / cbor2 / 23566141930

25 Mar 2026 09:55PM UTC coverage: 93.593% (-1.0%) from 94.565%
23566141930

Pull #278

github

web-flow
Merge 6a37043df into 93c598838
Pull Request #278: Rust conversion

2182 of 2338 new or added lines in 7 files covered. (93.33%)

2279 of 2435 relevant lines covered (93.59%)

1329.34 hits per line

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

91.16
/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,
10
    FRACTION_TYPE, IPV4ADDRESS_TYPE, IPV4INTERFACE_TYPE, IPV4NETWORK_TYPE, IPV6ADDRESS_TYPE,
11
    IPV6INTERFACE_TYPE, 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::sync::PyOnceLock;
18
use pyo3::types::{
19
    PyBytes, PyCFunction, PyComplex, PyDict, PyFrozenSet, PyInt, PyList, PyListMethods, PyMapping,
20
    PySet, PyString, PyTuple,
21
};
22
use pyo3::{IntoPyObjectExt, Py, PyAny, PyErrArguments, intern, pyclass};
23
use std::fmt::{Display, Formatter};
24
use std::mem::{replace, take};
25

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

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

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

64
enum DisplayName<'a> {
65
    String(&'static str),
66
    SemanticTag(usize),
67
    PythonName(Bound<'a, PyAny>),
68
}
69

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

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

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

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

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

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

191
    read_method: Option<Py<PyAny>>,
192
    buffer: Option<Py<PyBytes>>,
193
    read_position: usize,
194
    available_bytes: usize,
195
    fp_is_seekable: bool,
196
}
197

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

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

269
    fn read_exact<const N: usize>(&mut self, py: Python<'_>) -> PyResult<[u8; N]> {
28,308✔
270
        if self.available_bytes == 0 {
28,308✔
271
            // No buffer
272
            let (new_bytes, amount_read) = self.read_from_fp(py, N)?;
240✔
273
            self.read_position = N;
216✔
274
            self.available_bytes = amount_read - N;
216✔
275
            self.buffer = Some(new_bytes.unbind());
216✔
276
            Ok(self.buffer.as_ref().unwrap().as_bytes(py)[..N].try_into()?)
216✔
277
        } else if self.available_bytes < N {
28,068✔
278
            // Combine the remnants of the partial buffer with new data read from the file
NEW
279
            let needed_bytes = N - self.available_bytes;
×
NEW
280
            let mut concatenated_buffer: Vec<u8> = self.buffer.take().unwrap().extract(py)?;
×
NEW
281
            let (new_bytes, amount_read) = self.read_from_fp(py, needed_bytes)?;
×
NEW
282
            concatenated_buffer.extend_from_slice(&new_bytes[..needed_bytes]);
×
NEW
283
            self.buffer = Some(new_bytes.unbind());
×
NEW
284
            self.available_bytes = amount_read - needed_bytes;
×
NEW
285
            self.read_position = needed_bytes;
×
NEW
286
            Ok(concatenated_buffer.try_into().unwrap())
×
287
        } else {
288
            // Return a slice from the existing bytes object
289
            let slice: [u8; N] = self.buffer.as_ref().unwrap().bind(py).as_bytes()
28,068✔
290
                [self.read_position..self.read_position + N]
28,068✔
291
                .try_into()?;
28,068✔
292
            self.available_bytes -= N;
28,068✔
293
            self.read_position += N;
28,068✔
294
            Ok(slice)
28,068✔
295
        }
296
    }
28,308✔
297

298
    fn read_major_and_subtype(&mut self, py: Python<'_>) -> PyResult<(u8, u8)> {
22,656✔
299
        let initial_byte = self.read_exact::<1>(py)?[0];
22,656✔
300
        let major_type = initial_byte >> 5;
22,632✔
301
        let subtype = initial_byte & 31;
22,632✔
302
        Ok((major_type, subtype))
22,632✔
303
    }
22,656✔
304

305
    fn decode_length_finite(&mut self, py: Python<'_>, subtype: u8) -> PyResult<usize> {
7,680✔
306
        match self.decode_length(py, subtype)? {
7,680✔
307
            Some(length) => Ok(length),
7,644✔
308
            None => Err(CBORDecodeError::new_err(
24✔
309
                "indefinite length not allowed here",
24✔
310
            )),
24✔
311
        }
312
    }
7,680✔
313
    //
314
    // Decoders for major tags (0-7)
315
    //
316

317
    /// Decode the length of the next item.
318
    ///
319
    /// This is a low-level operation that may be needed by custom decoder callbacks.
320
    ///
321
    /// :param subtype:
322
    /// :return: the length of the item, or :data:`None` to indicate an indefinite-length item
323
    fn decode_length(&mut self, py: Python<'_>, subtype: u8) -> PyResult<Option<usize>> {
20,160✔
324
        let length = match subtype {
20,160✔
325
            ..24 => Some(subtype as usize),
20,160✔
326
            24 => Some(self.read_exact::<1>(py)?[0] as usize),
2,112✔
327
            25 => Some(u16::from_be_bytes(self.read_exact(py)?) as usize),
1,572✔
328
            26 => Some(u32::from_be_bytes(self.read_exact(py)?) as usize),
420✔
329
            27 => Some(u64::from_be_bytes(self.read_exact(py)?) as usize),
228✔
330
            31 => {
331
                if !self.allow_indefinite {
360✔
332
                    return Err(CBORDecodeError::new_err(
12✔
333
                        "encountered indefinite length but it has been disabled",
12✔
334
                    ));
12✔
335
                }
348✔
336
                None
348✔
337
            }
338
            _ => {
339
                return Err(CBORDecodeError::new_err(format!(
12✔
340
                    "unknown unsigned integer subtype 0x{subtype:x}"
12✔
341
                )));
12✔
342
            }
343
        };
344
        Ok(length)
20,136✔
345
    }
20,160✔
346

347
    fn decode_uint<'py>(&mut self, py: Python<'py>, subtype: u8) -> PyResult<DecoderResult<'py>> {
3,300✔
348
        // Major tag 0
349
        let uint = self.decode_length_finite(py, subtype)?;
3,300✔
350
        Ok(Value(uint.into_bound_py_any(py)?))
3,288✔
351
    }
3,300✔
352

353
    fn decode_negint<'py>(&mut self, py: Python<'py>, subtype: u8) -> PyResult<DecoderResult<'py>> {
768✔
354
        // Major tag 1
355
        let uint = self.decode_length_finite(py, subtype)?;
768✔
356
        let signed_int = -(uint as i128) - 1;
768✔
357
        Ok(Value(signed_int.into_bound_py_any(py)?))
768✔
358
    }
768✔
359

360
    fn decode_bytestring<'py>(
1,128✔
361
        &mut self,
1,128✔
362
        py: Python<'py>,
1,128✔
363
        subtype: u8,
1,128✔
364
    ) -> PyResult<DecoderResult<'py>> {
1,128✔
365
        // Major tag 2
366
        match self.decode_length(py, subtype)? {
1,128✔
367
            None => {
368
                // Indefinite length
369
                let mut bytes = PyBytes::new(py, b"");
72✔
370
                let sys_maxsize = *SYS_MAXSIZE.get(py).unwrap();
72✔
371
                loop {
372
                    let (major_type, subtype) = self.read_major_and_subtype(py)?;
120✔
373
                    match (major_type, subtype) {
120✔
374
                        (2, _) => {
375
                            let length = self.decode_length_finite(py, subtype)?;
84✔
376
                            if length > sys_maxsize {
72✔
377
                                return Err(CBORDecodeError::new_err(format!(
12✔
378
                                    "chunk too long in an indefinite bytestring chunk: {length}"
12✔
379
                                )));
12✔
380
                            }
60✔
381
                            let chunk = self.read(py, length)?;
60✔
382
                            bytes = bytes.add(chunk)?.cast_into()?;
48✔
383
                        }
384
                        (7, 31) => break Ok(Value(bytes.into_any())), // break marker
12✔
385
                        _ => {
386
                            return Err(CBORDecodeError::new_err(format!(
24✔
387
                                "non-byte string (major type {major_type}) found in indefinite \
24✔
388
                                    length byte string"
24✔
389
                            )));
24✔
390
                        }
391
                    }
392
                }
393
            }
394
            Some(length) if length <= 65536 => {
1,056✔
395
                let bytes = self.read(py, length)?;
1,032✔
396
                Ok(StringValue(PyBytes::new(py, &bytes).into_any(), length))
996✔
397
            }
398
            Some(length) => {
24✔
399
                // Incrementally read the bytestring, in chunks of 65536 bytes
400
                let mut bytes = PyBytes::new(py, b"");
24✔
401
                let mut remaining_length = length;
24✔
402
                while remaining_length > 0 {
48✔
403
                    let chunk_size = remaining_length.min(65536);
36✔
404
                    let chunk = self.read(py, chunk_size)?;
36✔
405
                    remaining_length -= chunk_size;
24✔
406
                    bytes = bytes.add(chunk)?.cast_into()?;
24✔
407
                }
408
                Ok(StringValue(bytes.into_any(), length))
12✔
409
            }
410
        }
411
    }
1,128✔
412

413
    fn decode_string<'py>(&mut self, py: Python<'py>, subtype: u8) -> PyResult<DecoderResult<'py>> {
2,232✔
414
        // Major tag 3
415
        match self.decode_length(py, subtype)? {
2,232✔
416
            None => {
417
                // Indefinite length
418
                let mut string = PyString::new(py, "");
96✔
419
                loop {
420
                    let (major_type, subtype) = self.read_major_and_subtype(py)?;
168✔
421
                    let sys_maxsize = *SYS_MAXSIZE.get(py).unwrap();
168✔
422
                    match (major_type, subtype) {
168✔
423
                        (3, _) => {
424
                            let length = self.decode_length_finite(py, subtype)?;
120✔
425
                            if length > sys_maxsize {
108✔
426
                                return Err(CBORDecodeError::new_err(format!(
12✔
427
                                    "chunk too long in an indefinite text string chunk: {length}"
12✔
428
                                )));
12✔
429
                            }
96✔
430
                            let bytes = self.read(py, length)?;
96✔
431
                            let decoded = match self.str_errors.as_ref() {
84✔
432
                                None => PyString::from_bytes(py, bytes.as_slice()),
84✔
NEW
433
                                Some(str_errors) => bytes
×
NEW
434
                                    .into_bound_py_any(py)?
×
NEW
435
                                    .call_method1(
×
NEW
436
                                        intern!(py, "decode"),
×
NEW
437
                                        (intern!(py, "utf-8"), str_errors),
×
438
                                    )
NEW
439
                                    .and_then(|string| {
×
NEW
440
                                        string.cast_into().map_err(|e| PyErr::from(e))
×
NEW
441
                                    }),
×
442
                            }?;
12✔
443
                            string = string.add(decoded)?.cast_into()?;
72✔
444
                        }
445
                        (7, 31) => break Ok(Value(string.into_any())), // break marker
24✔
446
                        _ => {
447
                            return Err(CBORDecodeError::new_err(format!(
24✔
448
                                "non-text string (major type {major_type}) found in indefinite \
24✔
449
                                    length text string"
24✔
450
                            )));
24✔
451
                        }
452
                    }
453
                }
454
            }
455
            Some(length) if length <= 65536 => {
2,124✔
456
                let bytes = self.read(py, length)?;
2,076✔
457
                let decoded_string: Bound<'_, PyAny> = match self.str_errors.as_ref() {
2,028✔
458
                    None => PyString::from_bytes(py, bytes.as_slice())?.into_any(),
2,004✔
459
                    Some(str_errors) => bytes.into_bound_py_any(py)?.call_method1(
24✔
460
                        intern!(py, "decode"),
24✔
461
                        (intern!(py, "utf-8"), str_errors.bind(py)),
24✔
NEW
462
                    )?,
×
463
                };
464
                Ok(StringValue(decoded_string, length))
1,992✔
465
            }
466
            Some(mut length) => {
48✔
467
                // Incrementally decode the string, in chunks of 65536 bytes
468
                let decoder_class = INCREMENTAL_UTF8_DECODER
48✔
469
                    .get_or_try_init(py, || -> PyResult<Py<PyAny>> {
48✔
470
                        let decoder = py
12✔
471
                            .import("codecs")?
12✔
472
                            .getattr("lookup")?
12✔
473
                            .call1(("utf-8",))?
12✔
474
                            .getattr("incrementaldecoder")?;
12✔
475
                        Ok(decoder.unbind())
12✔
476
                    })?
12✔
477
                    .bind(py);
48✔
478
                let decoder = match self.str_errors.as_ref() {
48✔
479
                    None => decoder_class.call0()?,
24✔
480
                    Some(str_errors) => decoder_class.call1((str_errors,))?,
24✔
481
                };
482
                let mut string = PyString::new(py, "").into_any();
48✔
483
                while length > 0 {
168✔
484
                    let chunk_size = length.min(65536);
120✔
485
                    let chunk = self.read(py, chunk_size)?;
120✔
486
                    length -= chunk_size;
120✔
487
                    let is_final_chunk = length == 0;
120✔
488
                    let decoded_chunk =
120✔
489
                        decoder.call_method1(intern!(py, "decode"), (chunk, is_final_chunk))?;
120✔
490
                    string = string.add(decoded_chunk)?;
120✔
491
                }
492
                Ok(StringValue(string.into_any(), length))
48✔
493
            }
494
        }
495
    }
2,232✔
496

497
    fn decode_array<'py>(
7,380✔
498
        &mut self,
7,380✔
499
        py: Python<'py>,
7,380✔
500
        subtype: u8,
7,380✔
501
        immutable: bool,
7,380✔
502
    ) -> PyResult<DecoderResult<'py>> {
7,380✔
503
        // Major tag 4
504
        let optional_length = self.decode_length(py, subtype)?;
7,380✔
505
        if immutable {
7,380✔
506
            let mut items: Vec<Bound<'py, PyAny>> = Vec::new();
1,500✔
507
            let callback: Box<DecoderCallback<'py>> = if let Some(length) = optional_length {
1,500✔
508
                if length == 0 {
1,476✔
509
                    return Ok(Value(PyTuple::empty(py).into_any()));
36✔
510
                }
1,440✔
511

512
                Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
3,048✔
513
                    items.push(item);
3,048✔
514
                    if items.len() == length {
3,048✔
515
                        Ok(CompleteFrame(
516
                            PyTuple::new(py, take(&mut items))?.into_any(),
1,392✔
517
                        ))
518
                    } else {
519
                        Ok(ContinueFrame(false))
1,656✔
520
                    }
521
                })
3,048✔
522
            } else {
523
                let break_marker = BREAK_MARKER.get(py).unwrap().bind(py);
24✔
524
                Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
72✔
525
                    if item.is(break_marker) {
72✔
526
                        Ok(CompleteFrame(
527
                            PyTuple::new(py, take(&mut items))?.into_any(),
12✔
528
                        ))
529
                    } else {
530
                        items.push(item);
60✔
531
                        Ok(ContinueFrame(false))
60✔
532
                    }
533
                })
72✔
534
            };
535
            Ok(BeginFrame(
1,464✔
536
                callback,
1,464✔
537
                false,
1,464✔
538
                None,
1,464✔
539
                DisplayName::String("array"),
1,464✔
540
            ))
1,464✔
541
        } else {
542
            let mut list = PyList::empty(py);
5,880✔
543
            let container = list.clone().into_any();
5,880✔
544
            let callback: Box<DecoderCallback<'py>> = if let Some(length) = optional_length {
5,880✔
545
                if length == 0 {
5,784✔
546
                    return Ok(Value(PyList::empty(py).into_any()));
36✔
547
                }
5,748✔
548

549
                Box::new(move |item, _immutable: bool| {
5,748✔
550
                    list.append(item)?;
2,208✔
551
                    if list.len() == length {
2,208✔
552
                        Ok(CompleteFrame(
780✔
553
                            replace(&mut list, PyList::empty(py)).into_any(),
780✔
554
                        ))
780✔
555
                    } else {
556
                        Ok(ContinueFrame(false))
1,428✔
557
                    }
558
                })
2,208✔
559
            } else {
560
                let break_marker = BREAK_MARKER.get(py).unwrap().bind(py);
96✔
561
                Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
564✔
562
                    if item.is(break_marker) {
564✔
563
                        Ok(CompleteFrame(
96✔
564
                            replace(&mut list, PyList::empty(py)).into_any(),
96✔
565
                        ))
96✔
566
                    } else {
567
                        list.append(item)?;
468✔
568
                        Ok(ContinueFrame(false))
468✔
569
                    }
570
                })
564✔
571
            };
572
            Ok(BeginFrame(
5,844✔
573
                callback,
5,844✔
574
                false,
5,844✔
575
                Some(container),
5,844✔
576
                DisplayName::String("array"),
5,844✔
577
            ))
5,844✔
578
        }
579
    }
7,380✔
580

581
    fn decode_map<'py>(
1,740✔
582
        &mut self,
1,740✔
583
        py: Python<'py>,
1,740✔
584
        subtype: u8,
1,740✔
585
        immutable: bool,
1,740✔
586
    ) -> PyResult<DecoderResult<'py>> {
1,740✔
587
        // Major tag 5
588

589
        #[cfg(Py_3_15)]
590
        fn create_frozen_dict<'py>(
12✔
591
            py: Python<'py>,
12✔
592
            items: Vec<(Bound<'py, PyAny>, Bound<'py, PyAny>)>,
12✔
593
        ) -> PyResult<Bound<'py, PyAny>> {
12✔
594
            FROZEN_DICT
12✔
595
                .get(py)?
12✔
596
                .call1((items,))?
12✔
597
                .cast_into()
12✔
598
                .map_err(|e| PyErr::from(e))
12✔
599
        }
12✔
600
        #[cfg(not(Py_3_15))]
601
        fn create_frozen_dict<'py>(
132✔
602
            py: Python<'py>,
132✔
603
            items: Vec<(Bound<'py, PyAny>, Bound<'py, PyAny>)>,
132✔
604
        ) -> PyResult<Bound<'py, PyAny>> {
132✔
605
            FrozenDict::from_items(py, items).map(|dict| dict.into_any())
132✔
606
        }
132✔
607

608
        #[inline]
609
        fn maybe_call_object_hook<'py>(
1,584✔
610
            py: Python<'py>,
1,584✔
611
            dict: Bound<'py, PyAny>,
1,584✔
612
            object_hook: Option<&Py<PyAny>>,
1,584✔
613
            immutable: bool,
1,584✔
614
        ) -> PyResult<Bound<'py, PyAny>> {
1,584✔
615
            if let Some(object_hook) = object_hook {
1,584✔
616
                object_hook.bind(py).call1((dict, immutable))
24✔
617
            } else {
618
                Ok(dict)
1,560✔
619
            }
620
        }
1,584✔
621

622
        let object_hook = self.object_hook.as_ref().map(|hook| hook.clone_ref(py));
1,740✔
623
        let length_or_none = self.decode_length(py, subtype)?;
1,740✔
624

625
        // Return immediately if this is an empty dict
626
        if let Some(length) = length_or_none
1,740✔
627
            && length == 0
1,704✔
628
        {
629
            let container: Bound<'py, PyAny> = if immutable {
396✔
NEW
630
                create_frozen_dict(py, Vec::new())?
×
631
            } else {
632
                PyDict::new(py).into_any()
396✔
633
            };
634
            let transformed =
396✔
635
                maybe_call_object_hook(py, container, object_hook.as_ref(), immutable)?;
396✔
636
            return Ok(Value(transformed));
396✔
637
        };
1,344✔
638

639
        let mut key: Option<Bound<'py, PyAny>> = None;
1,344✔
640
        if immutable {
1,344✔
641
            let mut items: Vec<(Bound<'py, PyAny>, Bound<'py, PyAny>)> = Vec::new();
276✔
642
            let callback: Box<DecoderCallback<'py>> = if let Some(length) = length_or_none {
276✔
643
                Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
348✔
644
                    if let Some(key) = key.take() {
348✔
645
                        items.push((key, item));
168✔
646
                        if items.len() == length {
168✔
647
                            let transformed = maybe_call_object_hook(
144✔
648
                                py,
144✔
649
                                create_frozen_dict(py, take(&mut items))?,
144✔
650
                                object_hook.as_ref(),
144✔
651
                                immutable,
144✔
NEW
652
                            )?;
×
653
                            return Ok(CompleteFrame(transformed));
144✔
654
                        }
24✔
655
                        Ok(ContinueFrame(true))
24✔
656
                    } else {
657
                        key = Some(item);
180✔
658
                        Ok(ContinueFrame(false))
180✔
659
                    }
660
                })
348✔
661
            } else {
NEW
662
                let break_marker = BREAK_MARKER.get(py).unwrap().bind(py);
×
NEW
663
                Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
×
NEW
664
                    if item.is(break_marker) {
×
NEW
665
                        let container = create_frozen_dict(py, take(&mut items))?;
×
NEW
666
                        let transformed = maybe_call_object_hook(
×
NEW
667
                            py,
×
NEW
668
                            container.into_any(),
×
NEW
669
                            object_hook.as_ref(),
×
NEW
670
                            immutable,
×
NEW
671
                        )?;
×
NEW
672
                        return Ok(CompleteFrame(transformed));
×
NEW
673
                    } else if let Some(key) = key.take() {
×
NEW
674
                        items.push((key, item));
×
NEW
675
                        Ok(ContinueFrame(true))
×
676
                    } else {
NEW
677
                        key = Some(item);
×
NEW
678
                        Ok(ContinueFrame(false))
×
679
                    }
NEW
680
                })
×
681
            };
682
            Ok(BeginFrame(callback, true, None, DisplayName::String("map")))
276✔
683
        } else {
684
            let mut dict = PyDict::new(py);
1,068✔
685
            let container = dict.clone().into_any();
1,068✔
686
            let callback: Box<DecoderCallback<'py>> = if let Some(length) = length_or_none {
1,068✔
687
                Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
3,120✔
688
                    if let Some(key) = key.take() {
3,120✔
689
                        dict.set_item(key, item)?;
1,560✔
690
                        if dict.len() == length {
1,560✔
691
                            let dict = replace(&mut dict, PyDict::new(py));
1,008✔
692
                            let transformed = maybe_call_object_hook(
1,008✔
693
                                py,
1,008✔
694
                                dict.into_any(),
1,008✔
695
                                object_hook.as_ref(),
1,008✔
696
                                immutable,
1,008✔
697
                            )?;
12✔
698
                            return Ok(CompleteFrame(transformed));
996✔
699
                        }
552✔
700
                        Ok(ContinueFrame(true))
552✔
701
                    } else {
702
                        key = Some(item);
1,560✔
703
                        Ok(ContinueFrame(false))
1,560✔
704
                    }
705
                })
3,120✔
706
            } else {
707
                let break_marker = BREAK_MARKER.get(py).unwrap().bind(py);
36✔
708
                Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
156✔
709
                    if item.is(break_marker) {
156✔
710
                        let dict = replace(&mut dict, PyDict::new(py));
36✔
711
                        let transformed = maybe_call_object_hook(
36✔
712
                            py,
36✔
713
                            dict.into_any(),
36✔
714
                            object_hook.as_ref(),
36✔
715
                            immutable,
36✔
NEW
716
                        )?;
×
717
                        return Ok(CompleteFrame(transformed));
36✔
718
                    } else if let Some(key) = key.take() {
120✔
719
                        dict.set_item(key, item)?;
60✔
720
                        Ok(ContinueFrame(true))
60✔
721
                    } else {
722
                        key = Some(item);
60✔
723
                        Ok(ContinueFrame(false))
60✔
724
                    }
725
                })
156✔
726
            };
727
            Ok(BeginFrame(
1,068✔
728
                callback,
1,068✔
729
                true,
1,068✔
730
                Some(container),
1,068✔
731
                DisplayName::String("map"),
1,068✔
732
            ))
1,068✔
733
        }
734
    }
1,740✔
735

736
    fn decode_semantic<'py>(
3,408✔
737
        &mut self,
3,408✔
738
        py: Python<'py>,
3,408✔
739
        subtype: u8,
3,408✔
740
        immutable: bool,
3,408✔
741
    ) -> PyResult<DecoderResult<'py>> {
3,408✔
742
        let tagnum = self.decode_length_finite(py, subtype)?;
3,408✔
743
        if let Some(semantic_decoders) = &self.semantic_decoders {
3,408✔
744
            match semantic_decoders.bind(py).get_item(&tagnum) {
120✔
745
                Ok(decoder) => {
96✔
746
                    let name = decoder.getattr_opt(intern!(py, NAME_ATTR))?;
96✔
747

748
                    // If these attributes are present, this callable was decorated with
749
                    // @shareable_decoder
750
                    return if let Some(name) = name {
96✔
751
                        let require_immutable: bool = decoder
60✔
752
                            .getattr_opt(intern!(py, IMMUTABLE_ATTR))?
60✔
753
                            .map(|x| x.is_truthy())
60✔
754
                            .transpose()?
60✔
755
                            .unwrap_or(false);
60✔
756
                        let retval = decoder.call1((immutable,))?;
60✔
757
                        let tuple: Bound<'_, PyTuple> = retval.cast_into()?;
60✔
758
                        if tuple.len() != 2 {
60✔
NEW
759
                            return Err(CBORDecodeError::new_err(format!(
×
NEW
760
                                "{decoder} returned a tuple of {} items, expected 2",
×
NEW
761
                                tuple.len()
×
NEW
762
                            )));
×
763
                        }
60✔
764
                        let container: Bound<'_, PyAny> = tuple.get_item(0)?.cast_into()?;
60✔
765
                        let callback: Bound<'_, PyAny> = tuple.get_item(1)?.cast_into()?;
60✔
766
                        Ok(BeginFrame(
767
                            Box::new(
60✔
768
                                move |item, _immutable: bool| -> PyResult<DecoderResult<'py>> {
60✔
769
                                    callback.call1((item,)).map(CompleteFrame)
60✔
770
                                },
60✔
771
                            ),
772
                            require_immutable,
60✔
773
                            if container.is_none() {
60✔
774
                                None
48✔
775
                            } else {
776
                                Some(container)
12✔
777
                            },
778
                            if name.is_none() {
60✔
779
                                DisplayName::SemanticTag(tagnum)
12✔
780
                            } else {
781
                                DisplayName::PythonName(name.clone())
48✔
782
                            },
783
                        ))
784
                    } else {
785
                        let callback =
36✔
786
                            move |item, new_immutable: bool| -> PyResult<DecoderResult<'py>> {
36✔
787
                                decoder.call1((item, new_immutable)).map(CompleteFrame)
36✔
788
                            };
36✔
789
                        Ok(BeginFrame(
36✔
790
                            Box::new(callback),
36✔
791
                            immutable,
36✔
792
                            None,
36✔
793
                            DisplayName::SemanticTag(tagnum),
36✔
794
                        ))
36✔
795
                    };
796
                }
797
                Err(e) if e.is_instance_of::<PyLookupError>(py) => {}
24✔
NEW
798
                Err(e) => return Err(e),
×
799
            }
800
        };
3,288✔
801

802
        // No semantic decoder lookup map – fall back to the hard coded switchboard
803
        let (callback, typename): (Box<DecoderCallback<'py>>, &str) = match tagnum {
3,312✔
804
            0 => (
456✔
805
                Box::new(Self::decode_datetime_string),
456✔
806
                "string-form datetime",
456✔
807
            ),
456✔
808
            1 => (Box::new(Self::decode_epoch_datetime), "epoch-form datetime"),
60✔
809
            2 => (Box::new(Self::decode_positive_bignum), "positive bignum"),
132✔
810
            3 => (Box::new(Self::decode_negative_bignum), "negative bignum"),
36✔
811
            4 => (Box::new(Self::decode_fraction), "decimal fraction"),
264✔
812
            5 => (Box::new(Self::decode_bigfloat), "bigfloat"),
24✔
813
            25 => (Box::new(Self::decode_stringref), "string reference"),
96✔
814
            28 => return Ok(Shareable),
240✔
815
            29 => (Box::new(Self::decode_sharedref), "shared reference"),
180✔
816
            30 => (Box::new(Self::decode_rational), "rational"),
252✔
817
            35 => (Box::new(Self::decode_regexp), "regular expression"),
36✔
818
            36 => (Box::new(Self::decode_mime), "MIME message"),
36✔
819
            37 => (Box::new(Self::decode_uuid), "UUID"),
300✔
820
            52 => (Box::new(Self::decode_ipv4), "IPv4 address"),
120✔
821
            54 => (Box::new(Self::decode_ipv6), "IPv6 address"),
72✔
822
            100 => (Box::new(Self::decode_epoch_date), "epoch-form date"),
12✔
823
            256 => return Ok(StringNamespace),
36✔
824
            258 => return self.decode_set(py, immutable),
408✔
825
            260 => (Box::new(Self::decode_ipaddress), "IP address"),
84✔
826
            261 => (Box::new(Self::decode_ipnetwork), "IP network"),
84✔
827
            1004 => (Box::new(Self::decode_date_string), "string-form date"),
12✔
828
            43000 => (Box::new(Self::decode_complex), "complex number"),
252✔
829
            55799 => (
24✔
830
                Box::new(Self::decode_self_describe_cbor),
24✔
831
                "self-described CBOR value",
24✔
832
            ),
24✔
833
            _ => {
834
                // For a tag with no designated decoder, check if we have a tag hook, and call
835
                // that with the tag object, using its return value as the decoded value.
836
                let tag = CBORTag::new(tagnum.into_bound_py_any(py)?, py.None().into_bound(py))?;
96✔
837
                let bound_tag = Bound::new(py, tag)?.into_any();
96✔
838
                let container = bound_tag.clone();
96✔
839
                let mut tag_hook = self
96✔
840
                    .tag_hook
96✔
841
                    .as_ref()
96✔
842
                    .map(|hook| hook.clone_ref(py).into_bound(py));
96✔
843
                let callback = Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
96✔
844
                    let tag: &Bound<'py, CBORTag> = bound_tag.cast()?;
84✔
845
                    tag.borrow_mut().value = item.unbind();
84✔
846
                    if let Some(tag_hook) = tag_hook.take() {
84✔
847
                        tag_hook.call1((&bound_tag, immutable)).map(CompleteFrame)
60✔
848
                    } else {
849
                        Ok(CompleteFrame(bound_tag.clone()))
24✔
850
                    }
851
                });
84✔
852
                return Ok(BeginFrame(
96✔
853
                    callback,
96✔
854
                    true,
96✔
855
                    Some(container),
96✔
856
                    DisplayName::SemanticTag(tagnum),
96✔
857
                ));
96✔
858
            }
859
        };
860
        Ok(BeginFrame(
2,532✔
861
            callback,
2,532✔
862
            true,
2,532✔
863
            None,
2,532✔
864
            DisplayName::String(typename),
2,532✔
865
        ))
2,532✔
866
    }
3,408✔
867

868
    fn decode_special<'py>(
2,388✔
869
        &mut self,
2,388✔
870
        py: Python<'py>,
2,388✔
871
        subtype: u8,
2,388✔
872
    ) -> PyResult<DecoderResult<'py>> {
2,388✔
873
        // Major tag 7
874
        match subtype {
2,388✔
875
            0..20 => {
2,388✔
876
                let value = subtype.into_pyobject(py)?;
72✔
877
                CBORSimpleValue::new(value)?.into_bound_py_any(py)
72✔
878
            }
879
            20 => Ok(false.into_bound_py_any(py)?),
132✔
880
            21 => Ok(true.into_bound_py_any(py)?),
144✔
881
            22 => Ok(py.None().into_bound_py_any(py)?),
504✔
882
            23 => Ok(UNDEFINED.get(py).unwrap().into_bound_py_any(py)?),
24✔
883
            24 => {
884
                let value = self.read_exact::<1>(py)?[0];
84✔
885
                if value < 0x20 {
84✔
886
                    return Err(CBORDecodeError::new_err(
36✔
887
                        "invalid two-byte sequence for simple value",
36✔
888
                    ));
36✔
889
                }
48✔
890
                CBORSimpleValue::new(value.into_pyobject(py)?)?.into_bound_py_any(py)
48✔
891
            }
892
            25 => {
893
                let bytes = self.read_exact::<2>(py)?;
684✔
894
                f16::from_be_bytes(bytes).to_f32().into_bound_py_any(py)
684✔
895
            }
896
            26 => {
897
                let bytes = self.read_exact::<4>(py)?;
108✔
898
                f32::from_be_bytes(bytes).into_bound_py_any(py)
108✔
899
            }
900
            27 => {
901
                let bytes = self.read_exact::<8>(py)?;
444✔
902
                f64::from_be_bytes(bytes).into_bound_py_any(py)
444✔
903
            }
904
            31 => Ok(BREAK_MARKER.get(py).unwrap().into_bound_py_any(py)?),
156✔
905
            _ => Err(CBORDecodeError::new_err(format!(
36✔
906
                "undefined reserved major type 7 subtype 0x{subtype:x}"
36✔
907
            ))),
36✔
908
        }
909
        .map(Value)
2,352✔
910
    }
2,388✔
911

912
    //
913
    // Decoders for semantic tags (major tag 6)
914
    //
915

916
    fn decode_datetime_string<'py>(
456✔
917
        value: Bound<'py, PyAny>,
456✔
918
        _immutable: bool,
456✔
919
    ) -> PyResult<DecoderResult<'py>> {
456✔
920
        // Semantic tag 0
921
        let py = value.py();
456✔
922
        let value_type = value.get_type();
456✔
923
        let mut datetime_str: Bound<'py, PyString> = value.cast_into().map_err(|e| {
456✔
NEW
924
            create_exc_from(
×
NEW
925
                py,
×
NEW
926
                CBORDecodeError::new_err(format!(
×
927
                    "expected string for tag, got {} instead",
NEW
928
                    value_type.to_string()
×
929
                )),
NEW
930
                Some(PyErr::from(e)),
×
931
            )
NEW
932
        })?;
×
933

934
        // Python 3.10 has impaired parsing of the ISO format:
935
        // * It doesn't handle the standard "Z" suffix
936
        // * It doesn't handle the fractional seconds part having fewer than 6 digits
937
        if py.version_info() <= (3, 10) {
456✔
938
            // Convert Z to +00:00
939
            let mut temp_str = datetime_str.to_string().replacen("Z", "+00:00", 1);
114✔
940

941
            // Pad any microseconds part with zeros
942
            if let Some((first, second)) = temp_str.split_once('.') {
114✔
943
                if let Some(index) = second.find(|c: char| !c.is_numeric()) {
576✔
944
                    let (mut micros, tz_part) = second.split_at(index);
78✔
945
                    // Cut off excess zeroes from the start of the microseconds part
946
                    if micros.len() >= 6 {
78✔
947
                        micros = &micros[..6];
63✔
948
                    }
63✔
949

950
                    // Reconstitute the datetime string, right-padding the microseconds part
951
                    // with zeroes
952
                    temp_str = format!("{first}.{micros:0<6}{tz_part}");
78✔
NEW
953
                }
×
954
            }
36✔
955

956
            datetime_str = temp_str.into_pyobject(py)?;
114✔
957
        }
342✔
958

959
        DATETIME_FROMISOFORMAT
456✔
960
            .get(py)?
456✔
961
            .call1((&datetime_str,))
456✔
962
            .map(CompleteFrame)
456✔
963
    }
456✔
964

965
    fn decode_epoch_datetime(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
60✔
966
        // Semantic tag 1
967
        let py = value.py();
60✔
968
        let utc = UTC.get(py)?;
60✔
969
        DATETIME_FROMTIMESTAMP
60✔
970
            .get(py)?
60✔
971
            .call1((value, utc))
60✔
972
            .map(CompleteFrame)
60✔
973
    }
60✔
974

975
    fn decode_positive_bignum(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
108✔
976
        // Semantic tag 2
977
        let py = value.py();
108✔
978
        INT_FROMBYTES
108✔
979
            .get(py)?
108✔
980
            .call1((value, intern!(py, "big")))
108✔
981
            .map(CompleteFrame)
108✔
982
    }
108✔
983

984
    fn decode_negative_bignum(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
36✔
985
        // Semantic tag 3
986
        let py = value.py();
36✔
987
        let int = INT_FROMBYTES.get(py)?.call1((value, intern!(py, "big")))?;
36✔
988
        int.neg()?.add(-1).map(CompleteFrame)
36✔
989
    }
36✔
990

991
    fn decode_fraction(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
264✔
992
        // Semantic tag 4
993
        let py = value.py();
264✔
994
        let tuple = require_tuple(value, 2)?;
264✔
995
        let decimal_class = DECIMAL_TYPE.get(py)?;
252✔
996
        {
997
            let exp = tuple.get_item(0)?;
252✔
998
            let sig_tuple = decimal_class
252✔
999
                .call1((tuple.get_item(1)?,))?
252✔
1000
                .call_method0(intern!(py, "as_tuple"))?
252✔
1001
                .cast_into::<PyTuple>()?;
252✔
1002
            let sign = sig_tuple.get_item(0)?;
252✔
1003
            let digits = sig_tuple.get_item(1)?;
252✔
1004
            let args_tuple = PyTuple::new(py, [sign, digits, exp])?;
252✔
1005
            decimal_class.call1((args_tuple,)).map(CompleteFrame)
252✔
1006
        }
1007
    }
264✔
1008

1009
    fn decode_bigfloat(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
24✔
1010
        // Semantic tag 5
1011
        let py = value.py();
24✔
1012
        let tuple = require_tuple(value, 2)?;
24✔
1013
        let decimal_class = DECIMAL_TYPE.get(py)?;
12✔
1014
        {
1015
            let exp = decimal_class.call1((tuple.get_item(0)?,))?;
12✔
1016
            let sig = decimal_class.call1((tuple.get_item(1)?,))?;
12✔
1017
            let exp = PyInt::new(py, 2).pow(exp, py.None())?;
12✔
1018
            sig.mul(exp).map(CompleteFrame)
12✔
1019
        }
1020
    }
24✔
1021

1022
    fn decode_stringref(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
96✔
1023
        // Semantic tag 25
1024
        let index: usize = value.extract()?;
96✔
1025
        Ok(StringReference(index))
96✔
1026
    }
96✔
1027

1028
    fn decode_sharedref(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
180✔
1029
        // Semantic tag 29
1030
        let index: usize = value.extract()?;
180✔
1031
        Ok(SharedReference(index))
180✔
1032
    }
180✔
1033

1034
    fn decode_rational(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
252✔
1035
        // Semantic tag 30
1036
        let py = value.py();
252✔
1037
        let tuple = require_tuple(value, 2)?;
252✔
1038
        FRACTION_TYPE.get(py)?.call1(tuple).map(CompleteFrame)
240✔
1039
    }
252✔
1040

1041
    fn decode_regexp(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
36✔
1042
        // Semantic tag 35
1043
        RE_COMPILE
36✔
1044
            .get(value.py())?
36✔
1045
            .call1((value,))
36✔
1046
            .map(CompleteFrame)
36✔
1047
    }
36✔
1048

1049
    fn decode_mime(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
24✔
1050
        // Semantic tag 36
1051
        let py = value.py();
24✔
1052
        let parser = EMAIL_PARSER.get(py)?.call0()?;
24✔
1053
        parser
24✔
1054
            .call_method1(intern!(py, "parsestr"), (value,))
24✔
1055
            .map(CompleteFrame)
24✔
1056
    }
24✔
1057

1058
    fn decode_uuid(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
300✔
1059
        // Semantic tag 37
1060
        let py = value.py();
300✔
1061
        let kwargs = PyDict::new(py);
300✔
1062
        kwargs.set_item(intern!(py, "bytes"), value)?;
300✔
1063
        UUID_TYPE
300✔
1064
            .get(py)?
300✔
1065
            .call((), Some(&kwargs))
300✔
1066
            .map(CompleteFrame)
300✔
1067
    }
300✔
1068

1069
    fn decode_ipv4(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
120✔
1070
        // Semantic tag 52
1071
        let py = value.py();
120✔
1072
        let addr = if let Ok(bytes) = value.cast::<PyBytes>() {
120✔
1073
            // The decoded value was a bytestring, so this is an IPv4 address
1074
            IPV4ADDRESS_TYPE.get(py)?.call1((bytes,))?
96✔
1075
        } else if let Ok(tuple) = value.cast_into::<PyTuple>()
24✔
1076
            && tuple.len() == 2
24✔
1077
        {
1078
            // The decoded value was a 2-item array. Check the types of the elements:
1079
            // (int, bytes) -> network
1080
            // (bytes, int) -> interface
1081
            let first_item = tuple.get_item(0)?;
24✔
1082
            let second_item = tuple.get_item(1)?;
24✔
1083
            if let Ok(prefix) = first_item.cast::<PyInt>()
24✔
1084
                && let Ok(address) = second_item.cast::<PyBytes>()
12✔
1085
            {
1086
                let mut address_vec: Vec<u8> = address.extract()?;
12✔
1087
                address_vec.resize(4, 0);
12✔
1088
                IPV4NETWORK_TYPE.get(py)?.call1(((address_vec, prefix),))?
12✔
1089
            } else if let Ok(address) = first_item.cast::<PyBytes>()
12✔
1090
                && let Ok(prefix) = second_item.cast::<PyInt>()
12✔
1091
            {
1092
                IPV4INTERFACE_TYPE.get(py)?.call1(((address, prefix),))?
12✔
1093
            } else {
NEW
1094
                return Err(CBORDecodeError::new_err("invalid types in input array"));
×
1095
            }
1096
        } else {
NEW
1097
            return Err(CBORDecodeError::new_err(
×
NEW
1098
                "input value must be a bytestring or an array of 2 elements",
×
NEW
1099
            ));
×
1100
        };
1101
        Ok(CompleteFrame(addr))
120✔
1102
    }
120✔
1103

1104
    fn decode_ipv6(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
72✔
1105
        // Semantic tag 54
1106
        let py = value.py();
72✔
1107
        let ipv6addr_class = IPV6ADDRESS_TYPE.get(py)?;
72✔
1108
        let addr = if let Ok(bytes) = value.cast::<PyBytes>() {
72✔
1109
            // The decoded value was a bytestring, so this is an IPv6 address
1110
            ipv6addr_class.call1((bytes,))?
36✔
1111
        } else if let Ok(tuple) = value.cast_into::<PyTuple>()
36✔
1112
            && (2..=3).contains(&tuple.len())
36✔
1113
        {
1114
            // The decoded value was a 2-item (or 3 with zone ID) array.
1115
            // Check the types of the elements:
1116
            // (int, bytes) -> network
1117
            // (bytes, int) -> interface
1118
            let first_item = tuple.get_item(0)?;
36✔
1119
            let second_item = tuple.get_item(1)?;
36✔
1120
            let zone_id = tuple.get_item(2).ok();
36✔
1121
            let (class, addr_bytes, prefix) = if let Ok(prefix) = first_item.cast::<PyInt>()
36✔
1122
                && let Ok(address) = second_item.cast::<PyBytes>()
12✔
1123
            {
1124
                let mut address_vec: Vec<u8> = address.extract()?;
12✔
1125
                address_vec.resize(16, 0);
12✔
1126
                Ok((
1127
                    IPV6NETWORK_TYPE.get(py)?,
12✔
1128
                    PyBytes::new(py, address_vec.as_slice()),
12✔
1129
                    prefix,
12✔
1130
                ))
1131
            } else if let Ok(address) = first_item.cast_into::<PyBytes>()
24✔
1132
                && let Ok(prefix) = second_item.cast::<PyInt>()
24✔
1133
            {
1134
                Ok((IPV6INTERFACE_TYPE.get(py)?, address, prefix))
24✔
1135
            } else {
NEW
1136
                Err(CBORDecodeError::new_err("invalid types in input array"))
×
NEW
1137
            }?;
×
1138
            let addr_obj = ipv6addr_class.call1((addr_bytes,))?;
36✔
1139

1140
            // Format the zone ID suffix if a zone ID was included
1141
            // (bytes or integer as the last item of a 3-tuple)
1142
            let zone_id_suffix = if let Some(zone_id) = zone_id {
36✔
1143
                if let Ok(zone_id_bytes) = zone_id.cast::<PyBytes>() {
24✔
1144
                    let zone_id_str = String::from_utf8(zone_id_bytes.as_bytes().to_vec())?;
12✔
1145
                    format!("%{zone_id_str}")
12✔
1146
                } else if let Ok(zone_id_int) = zone_id.cast::<PyInt>() {
12✔
1147
                    format!("%{zone_id_int}")
12✔
1148
                } else {
NEW
1149
                    return Err(CBORDecodeError::new_err(
×
NEW
1150
                        "zone ID must be an integer or a bytestring",
×
NEW
1151
                    ));
×
1152
                }
1153
            } else {
1154
                String::default()
12✔
1155
            };
1156

1157
            let formatted_addr = format!("{addr_obj}{zone_id_suffix}/{prefix}");
36✔
1158
            class.call1((formatted_addr,))?
36✔
1159
        } else {
NEW
1160
            return Err(CBORDecodeError::new_err(
×
NEW
1161
                "input value must be a bytestring or an array of 2 elements",
×
NEW
1162
            ));
×
1163
        };
1164
        Ok(CompleteFrame(addr))
72✔
1165
    }
72✔
1166

1167
    fn decode_epoch_date(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
12✔
1168
        // Semantic tag 100
1169
        let py = value.py();
12✔
1170
        let value = value.extract::<i32>()? + 719163;
12✔
1171
        DATE_FROMORDINAL.get(py)?.call1((value,)).map(CompleteFrame)
12✔
1172
    }
12✔
1173

1174
    fn decode_ipaddress(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
84✔
1175
        // Semantic tag 260 (deprecated)
1176
        let py = value.py();
84✔
1177
        let value = value.cast_into::<PyBytes>()?;
84✔
1178
        let addr_obj = match value.len()? {
72✔
1179
            4 | 16 => IPADDRESS_FUNC.get(py)?.call1((value,)),
48✔
1180
            6 => Ok(Bound::new(py, CBORTag::new_internal(260, value.into_any()))?.into_any()), // MAC address
12✔
1181
            length => Err(CBORDecodeError::new_err(format!(
12✔
1182
                "invalid IP address length ({length})"
12✔
1183
            ))),
12✔
1184
        }?;
12✔
1185
        Ok(CompleteFrame(addr_obj))
60✔
1186
    }
84✔
1187

1188
    fn decode_ipnetwork<'py>(
84✔
1189
        value: Bound<'py, PyAny>,
84✔
1190
        _immutable: bool,
84✔
1191
    ) -> PyResult<DecoderResult<'py>> {
84✔
1192
        // Semantic tag 261 (deprecated)
1193
        let py = value.py();
84✔
1194
        let value: Bound<'py, PyMapping> = value.cast_into()?;
84✔
1195
        let length = value.len()?;
84✔
1196
        if length != 1 {
84✔
1197
            return Err(CBORDecodeError::new_err(format!(
12✔
1198
                "invalid input map length for IP network: {}",
12✔
1199
                length
12✔
1200
            )));
12✔
1201
        }
72✔
1202
        let first_item = value.items()?.get_item(0)?;
72✔
1203
        let mask_length = first_item.get_item(1)?;
72✔
1204
        if !mask_length.is_exact_instance_of::<PyInt>() {
72✔
1205
            return Err(CBORDecodeError::new_err(format!(
12✔
1206
                "invalid mask length for IP network: {mask_length}"
12✔
1207
            )));
12✔
1208
        }
60✔
1209

1210
        let addr_obj = match IPNETWORK_FUNC.get(py)?.call1((&first_item,)) {
60✔
1211
            Ok(ip_network) => Ok(ip_network),
48✔
1212
            Err(e) => {
12✔
1213
                // A CompleteFrameError may indicate that the bytestring has host bits set, so try parsing
1214
                // it as an IP interface instead
1215
                if e.is_instance_of::<PyValueError>(py) {
12✔
1216
                    IPINTERFACE_FUNC.get(py)?.call1((first_item,))
12✔
1217
                } else {
NEW
1218
                    Err(e)
×
1219
                }
1220
            }
NEW
1221
        }?;
×
1222
        Ok(CompleteFrame(addr_obj))
60✔
1223
    }
84✔
1224

1225
    fn decode_date_string(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
12✔
1226
        // Semantic tag 1004
1227
        let py = value.py();
12✔
1228
        let date = DATE_FROMISOFORMAT.get(py)?.call1((value,))?;
12✔
1229
        Ok(CompleteFrame(date))
12✔
1230
    }
12✔
1231

1232
    fn decode_complex(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
252✔
1233
        // Semantic tag 43000
1234
        let py = value.py();
252✔
1235
        let tuple = require_tuple(value, 2)?;
252✔
1236
        let real: f64 = tuple.get_item(0)?.extract()?;
252✔
1237
        let imag: f64 = tuple.get_item(1)?.extract()?;
252✔
1238
        Ok(CompleteFrame(
252✔
1239
            PyComplex::from_doubles(py, real, imag).into_any(),
252✔
1240
        ))
252✔
1241
    }
252✔
1242

1243
    fn decode_self_describe_cbor(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
24✔
1244
        // Semantic tag 55799
1245
        Ok(CompleteFrame(value))
24✔
1246
    }
24✔
1247

1248
    fn decode_set<'py>(
408✔
1249
        &mut self,
408✔
1250
        py: Python<'py>,
408✔
1251
        immutable: bool,
408✔
1252
    ) -> PyResult<DecoderResult<'py>> {
408✔
1253
        // Semantic tag 258
1254
        let mut set_or_none = if immutable {
408✔
1255
            None
96✔
1256
        } else {
1257
            Some(PySet::empty(py)?.into_any())
312✔
1258
        };
1259
        let container = set_or_none.as_ref().map(|set| set.clone());
408✔
1260
        let callback = move |item: Bound<'py, PyAny>, _immutable: bool| {
408✔
1261
            let container: Bound<'py, PyAny> = if let Some(set) = set_or_none.take() {
396✔
1262
                set.call_method1(intern!(py, "update"), (item,))?;
300✔
1263
                set.into_any()
300✔
1264
            } else {
1265
                let tuple = item.cast_into::<PyTuple>()?;
96✔
1266
                PyFrozenSet::new(py, tuple)?.into_any()
96✔
1267
            };
1268
            Ok(CompleteFrame(container))
396✔
1269
        };
396✔
1270
        Ok(BeginFrame(
408✔
1271
            Box::new(callback),
408✔
1272
            true,
408✔
1273
            container,
408✔
1274
            DisplayName::String("set"),
408✔
1275
        ))
408✔
1276
    }
408✔
1277
}
1278

NEW
1279
#[pymethods]
×
1280
impl CBORDecoder {
1281
    #[new]
1282
    #[pyo3(signature = (
1283
        fp,
1284
        *,
1285
        tag_hook = None,
1286
        object_hook = None,
1287
        semantic_decoders = None,
1288
        str_errors = "strict",
1289
        read_size = 4096,
1290
        max_depth = 400,
1291
        allow_indefinite = true,
1292
    ))]
1293
    pub fn new(
384✔
1294
        py: Python<'_>,
384✔
1295
        fp: &Bound<'_, PyAny>,
384✔
1296
        tag_hook: Option<&Bound<'_, PyAny>>,
384✔
1297
        object_hook: Option<&Bound<'_, PyAny>>,
384✔
1298
        semantic_decoders: Option<&Bound<'_, PyMapping>>,
384✔
1299
        str_errors: &str,
384✔
1300
        read_size: usize,
384✔
1301
        max_depth: usize,
384✔
1302
        allow_indefinite: bool,
384✔
1303
    ) -> PyResult<Self> {
384✔
1304
        Self::new_internal(
384✔
1305
            py,
384✔
1306
            Some(fp),
384✔
1307
            None,
384✔
1308
            tag_hook,
384✔
1309
            object_hook,
384✔
1310
            semantic_decoders,
384✔
1311
            str_errors,
384✔
1312
            read_size,
384✔
1313
            max_depth,
384✔
1314
            allow_indefinite,
384✔
1315
        )
1316
    }
384✔
1317

1318
    #[getter]
NEW
1319
    fn fp(&self, py: Python<'_>) -> Option<Py<PyAny>> {
×
NEW
1320
        self.fp.as_ref().map(|fp| fp.clone_ref(py))
×
NEW
1321
    }
×
1322

1323
    #[setter]
1324
    fn set_fp(&mut self, fp: &Bound<'_, PyAny>) -> PyResult<()> {
396✔
1325
        let result = fp.call_method0("readable");
396✔
1326
        if let Ok(readable) = &result
396✔
1327
            && readable.is_truthy()?
384✔
1328
        {
1329
            self.fp_is_seekable = fp.call_method0("seekable")?.is_truthy()?;
372✔
1330
            let fp = fp.clone();
372✔
1331
            self.read_method = Some(fp.getattr("read")?.unbind());
372✔
1332
            self.fp = Some(fp.unbind());
372✔
1333
            self.available_bytes = 0;
372✔
1334
            self.read_position = 0;
372✔
1335
            self.buffer = None;
372✔
1336
            Ok(())
372✔
1337
        } else {
1338
            raise_exc_from(
24✔
1339
                fp.py(),
24✔
1340
                PyValueError::new_err("fp must be a readable file-like object"),
24✔
1341
                result.err(),
24✔
1342
            )
1343
        }
1344
    }
396✔
1345

1346
    #[getter]
1347
    fn tag_hook(&self, py: Python<'_>) -> Option<Py<PyAny>> {
12✔
1348
        self.tag_hook
12✔
1349
            .as_ref()
12✔
1350
            .map(|tag_hook| tag_hook.clone_ref(py))
12✔
1351
    }
12✔
1352

1353
    #[setter]
1354
    fn set_tag_hook(&mut self, tag_hook: Option<&Bound<'_, PyAny>>) -> PyResult<()> {
4,416✔
1355
        if let Some(tag_hook) = tag_hook {
4,416✔
1356
            if !tag_hook.is_callable() {
132✔
1357
                return Err(PyErr::new::<PyTypeError, _>(
12✔
1358
                    "tag_hook must be callable or None",
12✔
1359
                ));
12✔
1360
            }
120✔
1361

1362
            self.tag_hook = Some(tag_hook.clone().unbind());
120✔
1363
        } else {
4,284✔
1364
            self.tag_hook = None;
4,284✔
1365
        }
4,284✔
1366
        Ok(())
4,404✔
1367
    }
4,416✔
1368

1369
    #[getter]
1370
    fn object_hook(&self, py: Python<'_>) -> Option<Py<PyAny>> {
12✔
1371
        self.object_hook
12✔
1372
            .as_ref()
12✔
1373
            .map(|object_hook| object_hook.clone_ref(py))
12✔
1374
    }
12✔
1375

1376
    #[setter]
1377
    fn set_object_hook(&mut self, object_hook: Option<&Bound<'_, PyAny>>) -> PyResult<()> {
4,404✔
1378
        if let Some(object_hook) = object_hook {
4,404✔
1379
            if !object_hook.is_callable() {
48✔
1380
                return Err(PyErr::new::<PyTypeError, _>(
12✔
1381
                    "object_hook must be callable or None",
12✔
1382
                ));
12✔
1383
            }
36✔
1384

1385
            self.object_hook = Some(object_hook.clone().unbind());
36✔
1386
        } else {
4,356✔
1387
            self.object_hook = None;
4,356✔
1388
        }
4,356✔
1389
        Ok(())
4,392✔
1390
    }
4,404✔
1391

1392
    #[getter]
1393
    fn str_errors(&self, py: Python<'_>) -> Py<PyString> {
60✔
1394
        if let Some(str_errors) = self.str_errors.as_ref() {
60✔
1395
            str_errors.clone_ref(py)
48✔
1396
        } else {
1397
            intern!(py, "strict").clone().unbind()
12✔
1398
        }
1399
    }
60✔
1400

1401
    #[setter]
1402
    fn set_str_errors(&mut self, str_errors: &Bound<'_, PyString>) -> PyResult<()> {
4,392✔
1403
        let as_string: &str = str_errors.extract()?;
4,392✔
1404
        self.str_errors = match as_string {
4,392✔
1405
            "strict" => None,
4,392✔
1406
            "ignore" | "replace" | "backslashreplace" | "surrogateescape" => {
108✔
1407
                Some(str_errors.clone().unbind())
96✔
1408
            }
1409
            _ => {
1410
                return Err(PyValueError::new_err(format!(
12✔
1411
                    "invalid str_errors value: '{str_errors}'"
12✔
1412
                )));
12✔
1413
            }
1414
        };
1415
        Ok(())
4,380✔
1416
    }
4,392✔
1417

1418
    /// Read bytes from the data stream.
1419
    ///
1420
    /// :param amount: the number of bytes to read
1421
    #[pyo3(signature = (amount, /))]
1422
    fn read(&mut self, py: Python<'_>, amount: usize) -> PyResult<Vec<u8>> {
3,456✔
1423
        if amount == 0 {
3,456✔
1424
            return Ok(Vec::default());
96✔
1425
        }
3,360✔
1426

1427
        if self.available_bytes == 0 {
3,360✔
1428
            // No buffer
1429
            let (new_bytes, amount_read) = self.read_from_fp(py, amount)?;
72✔
1430
            self.read_position = amount;
12✔
1431
            self.available_bytes = amount_read - amount;
12✔
1432
            let new_buffer = new_bytes.as_bytes()[..amount].to_vec();
12✔
1433
            self.buffer = Some(new_bytes.unbind());
12✔
1434
            Ok(new_buffer)
12✔
1435
        } else if self.available_bytes < amount {
3,288✔
1436
            // Combine the remnants of the partial buffer with new data read from the file
1437
            let needed_bytes = amount - self.available_bytes;
72✔
1438
            let mut concatenated_buffer: Vec<u8> =
72✔
1439
                self.buffer.take().unwrap().as_bytes(py).to_vec();
72✔
1440
            let (new_bytes, amount_read) = self.read_from_fp(py, needed_bytes)?;
72✔
NEW
1441
            concatenated_buffer.extend_from_slice(&new_bytes[..needed_bytes]);
×
NEW
1442
            self.buffer = Some(new_bytes.unbind());
×
NEW
1443
            self.available_bytes = amount_read - needed_bytes;
×
NEW
1444
            self.read_position = needed_bytes;
×
NEW
1445
            Ok(concatenated_buffer)
×
1446
        } else {
1447
            // Return a slice from the existing bytes object
1448
            let vec = self.buffer.as_ref().unwrap().as_bytes(py)
3,216✔
1449
                [self.read_position..self.read_position + amount]
3,216✔
1450
                .to_vec();
3,216✔
1451
            self.available_bytes -= amount;
3,216✔
1452
            self.read_position += amount;
3,216✔
1453
            Ok(vec)
3,216✔
1454
        }
1455
    }
3,456✔
1456

1457
    /// Decode the next value from the stream.
1458
    ///
1459
    /// :param immutable: if :data:`True`, decode the next item as an immutable type
1460
    ///     (e.g. :class:`tuple` instead of a :class:`list`), if possible
1461
    /// :return: the decoded object
1462
    /// :raises CBORDecodeError: if there is any problem decoding the stream
1463
    #[pyo3(signature = (*, immutable = false))]
1464
    pub fn decode<'py>(&mut self, py: Python<'py>, immutable: bool) -> PyResult<Bound<'py, PyAny>> {
4,284✔
1465
        let mut frames: Vec<StackFrame> = Vec::new();
4,284✔
1466

1467
        fn add_frame<'a>(
12,060✔
1468
            frames: &mut Vec<StackFrame<'a>>,
12,060✔
1469
            max_depth: usize,
12,060✔
1470
            frame: StackFrame<'a>,
12,060✔
1471
        ) -> PyResult<()> {
12,060✔
1472
            if frames.len() == max_depth {
12,060✔
1473
                return Err(CBORDecodeError::new_err(format!(
24✔
1474
                    "maximum container nesting depth ({max_depth}) exceeded",
24✔
1475
                )));
24✔
1476
            }
12,036✔
1477

1478
            frames.push(frame);
12,036✔
1479
            Ok(())
12,036✔
1480
        }
12,060✔
1481

1482
        fn wrap_exception(py: Python<'_>, err: PyErr, typename: &DisplayName) -> PyErr {
588✔
1483
            if err.is_instance_of::<CBORDecodeEOF>(py) {
588✔
1484
                err
120✔
1485
            } else if err.is_instance_of::<CBORDecodeError>(py) {
468✔
1486
                CBORDecodeError::new_err(format!(
228✔
1487
                    "error decoding {}: {}",
1488
                    typename,
1489
                    err.arguments(py)
228✔
1490
                ))
1491
            } else {
1492
                create_exc_from(
240✔
1493
                    py,
240✔
1494
                    CBORDecodeError::new_err(format!("error decoding {}", typename)),
240✔
1495
                    Some(err),
240✔
1496
                )
1497
            }
1498
        }
588✔
1499

1500
        let mut shareables: Vec<Option<Bound<'py, PyAny>>> = Vec::new();
4,284✔
1501
        let mut string_namespaces: Vec<Vec<Bound<'py, PyAny>>> = Vec::new();
4,284✔
1502
        let mut value: Option<Bound<'py, PyAny>> = None;
4,284✔
1503
        let mut current_immutable: bool = immutable;
4,284✔
1504
        loop {
1505
            let result: PyResult<DecoderResult<'py>> = if let Some(previous_value) = value.take() {
35,040✔
1506
                // Call the decoder callback of the last frame
1507
                let frame = frames.last_mut().unwrap();
12,672✔
1508
                if let Some(decoder_callback) = frame.decoder_callback.as_mut() {
12,672✔
1509
                    decoder_callback(previous_value, frame.immutable)
12,588✔
1510
                        .map_err(|e| wrap_exception(py, e, &frame.typename))
12,588✔
1511
                } else if frame.contains_string_namespace {
84✔
1512
                    string_namespaces
12✔
1513
                        .pop()
12✔
1514
                        .expect("no string namespaces to pop from");
12✔
1515
                    Ok(CompleteFrame(previous_value))
12✔
1516
                } else if let Some(shareable_index) = frame.shareable_index {
72✔
1517
                    shareables[shareable_index].get_or_insert_with(|| previous_value.clone());
72✔
1518
                    Ok(CompleteFrame(previous_value))
72✔
1519
                } else {
NEW
1520
                    panic!("no decoder callback, shareable index or string namespace");
×
1521
                }
1522
            } else {
1523
                let (major_type, subtype) = self.read_major_and_subtype(py)?;
22,368✔
1524
                match major_type {
22,344✔
1525
                    0 => self.decode_uint(py, subtype),
3,300✔
1526
                    1 => self.decode_negint(py, subtype),
768✔
1527
                    2 => self.decode_bytestring(py, subtype),
1,128✔
1528
                    3 => self.decode_string(py, subtype),
2,232✔
1529
                    4 => self.decode_array(py, subtype, current_immutable),
7,380✔
1530
                    5 => self.decode_map(py, subtype, current_immutable),
1,740✔
1531
                    6 => self.decode_semantic(py, subtype, current_immutable),
3,408✔
1532
                    7 => self.decode_special(py, subtype),
2,388✔
NEW
1533
                    _ => Err(CBORDecodeError::new_err(format!(
×
NEW
1534
                        "invalid major type: {major_type}"
×
NEW
1535
                    ))),
×
1536
                }
1537
                .map_err(|e| {
22,344✔
1538
                    let typename = match major_type {
360✔
1539
                        0 => "unsigned integer",
12✔
NEW
1540
                        1 => "negative integer",
×
1541
                        2 => "byte string",
108✔
1542
                        3 => "text string",
168✔
NEW
1543
                        4 => "array",
×
NEW
1544
                        5 => "map",
×
NEW
1545
                        6 => "semantic tag",
×
1546
                        7 => "special value",
72✔
NEW
1547
                        _ => unreachable!("invalid major types should have been handled earlier"),
×
1548
                    };
1549
                    wrap_exception(py, e, &DisplayName::String(typename))
360✔
1550
                })
360✔
1551
            };
1552

1553
            match result {
34,428✔
1554
                Ok(BeginFrame(callback, requested_immutable, container, typename)) => {
11,784✔
1555
                    if let Some(frame) = frames.last_mut()
11,784✔
1556
                        && let Some(container) = container
9,228✔
1557
                        && let Some(shareable_index) = frame.shareable_index
5,892✔
1558
                    {
156✔
1559
                        frames.pop();
156✔
1560
                        shareables[shareable_index] = Some(container.clone());
156✔
1561
                    }
11,628✔
1562
                    current_immutable = current_immutable || requested_immutable;
11,784✔
1563
                    add_frame(
11,784✔
1564
                        &mut frames,
11,784✔
1565
                        self.max_depth,
11,784✔
1566
                        StackFrame {
11,784✔
1567
                            immutable: current_immutable,
11,784✔
1568
                            decoder_callback: Some(callback),
11,784✔
1569
                            shareable_index: None,
11,784✔
1570
                            typename,
11,784✔
1571
                            contains_string_namespace: false,
11,784✔
1572
                        },
11,784✔
1573
                    )?;
24✔
1574
                }
1575
                Ok(ContinueFrame(require_immutable)) => {
6,048✔
1576
                    // If require_immutable is true, the next value must be immutable
1577
                    // Otherwise, restore the immutable flag to the previous value
1578
                    current_immutable = if frames.len() >= 2 {
6,048✔
1579
                        frames.get(frames.len() - 2).unwrap().immutable
3,120✔
1580
                    } else {
1581
                        immutable
2,928✔
1582
                    } || require_immutable;
3,384✔
1583
                }
1584
                Ok(CompleteFrame(new_value)) => {
6,120✔
1585
                    frames
6,120✔
1586
                        .pop()
6,120✔
1587
                        .expect("received frame completion but there are no frames on the stack");
6,120✔
1588
                    current_immutable = frames.last().map_or(immutable, |frame| frame.immutable);
6,120✔
1589
                    value = Some(new_value);
6,120✔
1590
                }
1591
                Ok(Value(new_value)) => {
6,876✔
1592
                    value = Some(new_value);
6,876✔
1593
                }
6,876✔
1594
                Ok(StringNamespace) => {
1595
                    add_frame(
36✔
1596
                        &mut frames,
36✔
1597
                        self.max_depth,
36✔
1598
                        StackFrame {
36✔
1599
                            immutable: current_immutable,
36✔
1600
                            decoder_callback: None,
36✔
1601
                            shareable_index: None,
36✔
1602
                            typename: DisplayName::String("string namespace"),
36✔
1603
                            contains_string_namespace: true,
36✔
1604
                        },
36✔
NEW
1605
                    )?;
×
1606
                    string_namespaces.push(Vec::new());
36✔
1607
                }
1608
                Ok(StringValue(string, length)) => {
3,048✔
1609
                    // Conditionally add the string to the innermost string namespace
1610
                    if let Some(namespace) = string_namespaces.last_mut() {
3,048✔
1611
                        if match namespace.len() {
48✔
1612
                            0..24 => length >= 3,
48✔
NEW
1613
                            24..256 => length >= 4,
×
NEW
1614
                            256..65536 => length >= 5,
×
NEW
1615
                            65536..4294967296 => length >= 6,
×
NEW
1616
                            _ => length >= 11,
×
1617
                        } {
48✔
1618
                            namespace.push(string.clone());
48✔
1619
                        }
48✔
1620
                    }
3,000✔
1621
                    value = Some(string);
3,048✔
1622
                }
1623
                Ok(StringReference(index)) => {
96✔
1624
                    frames
96✔
1625
                        .pop()
96✔
1626
                        .expect("  received string reference but there are no frames on the stack");
96✔
1627
                    if let Some(namespace) = string_namespaces.last() {
96✔
1628
                        if let Some(string) = namespace.get(index) {
84✔
1629
                            value = Some(string.clone());
72✔
1630
                        } else {
72✔
1631
                            return Err(CBORDecodeError::new_err(format!(
12✔
1632
                                "string reference {index} not found"
12✔
1633
                            )));
12✔
1634
                        }
1635
                    } else {
1636
                        return Err(CBORDecodeError::new_err(
12✔
1637
                            "string reference outside of namespace",
12✔
1638
                        ));
12✔
1639
                    }
1640
                    current_immutable = frames
72✔
1641
                        .last()
72✔
1642
                        .map_or(current_immutable, |frame| frame.immutable);
72✔
1643
                }
1644
                Ok(Shareable) => {
1645
                    add_frame(
240✔
1646
                        &mut frames,
240✔
1647
                        self.max_depth,
240✔
1648
                        StackFrame {
240✔
1649
                            immutable: current_immutable,
240✔
1650
                            decoder_callback: None,
240✔
1651
                            shareable_index: Some(shareables.len()),
240✔
1652
                            typename: DisplayName::String("shareable value"),
240✔
1653
                            contains_string_namespace: false,
240✔
1654
                        },
240✔
NEW
1655
                    )?;
×
1656
                    shareables.push(None);
240✔
1657
                }
1658
                Ok(SharedReference(index)) => {
180✔
1659
                    frames
180✔
1660
                        .pop()
180✔
1661
                        .expect("received shared reference but there are no frames on the stack");
180✔
1662
                    value = match shareables.get(index) {
180✔
1663
                        Some(Some(value)) => Some(value.clone()),
144✔
1664
                        Some(None) => {
1665
                            return Err(CBORDecodeError::new_err(format!(
12✔
1666
                                "shared value {index} has not been initialized"
12✔
1667
                            )));
12✔
1668
                        }
1669
                        None => {
1670
                            return Err(CBORDecodeError::new_err(format!(
24✔
1671
                                "shared reference {index} not found"
24✔
1672
                            )));
24✔
1673
                        }
1674
                    };
1675
                    current_immutable = frames
144✔
1676
                        .last()
144✔
1677
                        .map_or(current_immutable, |frame| frame.immutable);
144✔
1678
                }
1679
                Err(err) => {
588✔
1680
                    // If an Exception was raised, wrap it in a CBORDecodeError
1681
                    // If a ValueError was raised, wrap it in a CBORDecodeError
1682
                    return if err.is_instance_of::<CBORDecodeError>(py) {
588✔
1683
                        Err(err)
588✔
NEW
1684
                    } else if err.is_instance_of::<PyValueError>(py) {
×
NEW
1685
                        Err(create_exc_from(
×
NEW
1686
                            py,
×
NEW
1687
                            CBORDecodeError::new_err(err.to_string()),
×
NEW
1688
                            Some(err),
×
NEW
1689
                        ))
×
NEW
1690
                    } else if err.is_instance_of::<PyException>(py) {
×
NEW
1691
                        Err(create_exc_from(
×
NEW
1692
                            py,
×
NEW
1693
                            CBORDecodeError::new_err(err.to_string()),
×
NEW
1694
                            Some(err),
×
NEW
1695
                        ))
×
1696
                    } else {
NEW
1697
                        Err(err)
×
1698
                    };
1699
                }
1700
            }
1701

1702
            if frames.is_empty() {
34,344✔
1703
                // If fp was seekable and excess data has been read, empty the buffer and
1704
                // rewind the file
1705
                if self.available_bytes > 0
3,588✔
1706
                    && let Some(fp) = &self.fp
24✔
1707
                {
1708
                    let offset = -(self.available_bytes as isize);
24✔
1709
                    fp.call_method1(py, intern!(py, "seek"), (offset, SEEK_CUR))?;
24✔
1710
                    self.buffer = None;
24✔
1711
                    self.available_bytes = 0;
24✔
1712
                    self.read_position = 0;
24✔
1713
                }
3,564✔
1714
                return Ok(value.expect("stack is empty but final return value is missing"));
3,588✔
1715
            }
30,756✔
1716
        }
1717
    }
4,284✔
1718
}
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