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

agronholm / cbor2 / 23325444499

20 Mar 2026 01:40AM UTC coverage: 92.653% (-1.9%) from 94.55%
23325444499

Pull #278

github

web-flow
Merge 30796d059 into a8d92dc3d
Pull Request #278: Rust conversion

2173 of 2353 new or added lines in 7 files covered. (92.35%)

2270 of 2450 relevant lines covered (92.65%)

1473.34 hits per line

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

89.89
/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
use crate::types::{
7
    BreakMarkerType, CBORDecodeEOF, CBORDecodeError, CBORDecodeValueError, CBORSimpleValue,
8
    CBORTag, DECIMAL_TYPE, FRACTION_TYPE, IPV4ADDRESS_TYPE, IPV4INTERFACE_TYPE, IPV4NETWORK_TYPE,
9
    IPV6ADDRESS_TYPE, IPV6INTERFACE_TYPE, IPV6NETWORK_TYPE, UUID_TYPE,
10
};
11
use crate::utils::{PyImportable, create_exc_from, raise_exc_from};
12
use half::f16;
13
use pyo3::exceptions::{PyException, PyLookupError, PyTypeError, PyValueError};
14
use pyo3::prelude::*;
15
use pyo3::sync::PyOnceLock;
16
use pyo3::types::{
17
    PyBytes, PyCFunction, PyComplex, PyDict, PyFrozenSet, PyInt, PyList, PyListMethods, PyMapping,
18
    PySet, PyString, PyTuple,
19
};
20
use pyo3::{IntoPyObjectExt, Py, PyAny, intern, pyclass};
21
use std::mem::{replace, take};
22

23
#[cfg(not(Py_3_15))]
24
use crate::types::FrozenDict;
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(Box<DecoderCallback<'a>>, bool, Option<Bound<'a, PyAny>>),
49
    ContinueFrame(bool),
50
    CompleteFrame(Bound<'a, PyAny>),
51
    Value(Bound<'a, PyAny>),
52
    StringValue(Bound<'a, PyAny>, usize),
53
    StringNamespace,
54
    StringReference(usize),
55
    Shareable,
56
    SharedReference(usize),
57
}
58

59
type DecoderCallback<'py> =
60
    dyn 'py + FnMut(Bound<'py, PyAny>, bool) -> PyResult<DecoderResult<'py>>;
61

62
struct StackFrame<'py> {
63
    immutable: bool,
64
    decoder_callback: Option<Box<DecoderCallback<'py>>>,
65
    shareable_index: Option<usize>,
66
    contains_string_namespace: bool,
67
}
68

69
/// Decorates a function to be a two-stage decoder.
70
///
71
/// :param name: the name displayed in a :exc:`CBORDecodeError` raised by the decoder
72
///     (e.g. "Error decoding thingamajig") where name='thingamajig`)
73
/// :param immutable: :data:`True` if the item sent to the decoder should be decoded as immutable
74
#[pyfunction]
75
#[pyo3(signature = (func=None, /, *, name=None, immutable=false))]
76
pub fn shareable_decoder<'py>(
72✔
77
    py: Python<'py>,
72✔
78
    func: Option<Py<PyAny>>,
72✔
79
    name: Option<Py<PyString>>,
72✔
80
    immutable: bool,
72✔
81
) -> PyResult<Bound<'py, PyAny>> {
72✔
82
    match func {
72✔
83
        None => PyCFunction::new_closure(
36✔
84
            py,
36✔
85
            None,
36✔
86
            None,
36✔
87
            move |args: &Bound<'_, PyTuple>,
88
                  _kwargs: Option<&Bound<'_, PyDict>>|
89
                  -> PyResult<Py<PyAny>> {
36✔
90
                let py = args.py();
36✔
91
                let func = args.get_item(0)?;
36✔
92
                let name = name.as_ref().map(|x| x.clone_ref(py));
36✔
93
                shareable_decoder(py, Some(func.unbind()), name, immutable).map(Bound::unbind)
36✔
94
            },
36✔
95
        )
96
        .map(|f| f.into_any()),
36✔
97
        Some(func) => {
36✔
98
            let bound_func = func.bind(py);
36✔
99
            if !bound_func.is_callable() {
36✔
NEW
100
                return Err(PyTypeError::new_err(format!("{func} is not callable")));
×
101
            }
36✔
102
            bound_func.setattr(intern!(py, NAME_ATTR), name)?;
36✔
103
            bound_func.setattr(intern!(py, IMMUTABLE_ATTR), immutable)?;
36✔
104
            Ok(bound_func.clone().into_any())
36✔
105
        }
106
    }
107
}
72✔
108

109
/// The CBORDecoder class implements a fully featured `CBOR`_ decoder with
110
/// several extensions for handling shared references, big integers, rational
111
/// numbers and so on. Typically, the class is not used directly, but the
112
/// :func:`load` and :func:`loads` functions are called to indirectly construct
113
/// and use the class.
114
///
115
/// When the class is constructed manually, the main entry point is:meth:`decode`.
116
///
117
/// :param fp: the file to read from (any file-like object opened for reading in binary mode)
118
/// :param tag_hook:
119
///     callable that takes 2 arguments: the decoder instance, and the :class:`.CBORTag`
120
///     to be decoded. This callback is invoked for any tags for which there is no
121
///     built-in decoder. The return value is substituted for the :class:`.CBORTag`
122
///     object in the deserialized output
123
/// :param object_hook:
124
///     callable that takes 2 arguments: the decoder instance, and a dictionary. This
125
///     callback is invoked for each deserialized :class:`dict` object. The return value
126
///     is substituted for the dict in the deserialized output.
127
/// :param semantic_decoders:
128
///     An optional mapping for overriding the decoding for select semantic tags.
129
///     The value is a mapping of semantic tags (integers) to callables that take
130
///     the decoder instance as the sole argument.
131
/// :param str_errors:
132
///     determines how to handle Unicode decoding errors (see the `Error Handlers`_
133
///     section in the standard library documentation for details)
134
/// :param read_size: minimum number of bytes to read at once
135
///     (ignored if ``fp`` is not seekable)
136
/// :param max_depth:
137
///     maximum allowed depth for nested containers
138
/// :param allow_indefinite:
139
///     if :data:`False`, raise a :exc:`CBORDecodeError` when encountering an indefinite-length
140
///     string or container in the input stream
141
///
142
/// .. _CBOR: https://cbor.io/
143
#[pyclass(module = "cbor2")]
144
pub struct CBORDecoder {
145
    fp: Option<Py<PyAny>>,
146
    tag_hook: Option<Py<PyAny>>,
147
    object_hook: Option<Py<PyAny>>,
148
    semantic_decoders: Option<Py<PyMapping>>,
149
    str_errors: Option<Py<PyString>>,
150
    #[pyo3(get)]
151
    read_size: usize,
152
    #[pyo3(get)]
153
    max_depth: usize,
154
    #[pyo3(get)]
155
    allow_indefinite: bool,
156

157
    read_method: Option<Py<PyAny>>,
158
    buffer: Option<Py<PyBytes>>,
159
    read_position: usize,
160
    available_bytes: usize,
161
    fp_is_seekable: bool,
162
}
163

164
impl CBORDecoder {
165
    pub fn new_internal(
4,344✔
166
        py: Python<'_>,
4,344✔
167
        fp: Option<&Bound<'_, PyAny>>,
4,344✔
168
        buffer: Option<Bound<PyBytes>>,
4,344✔
169
        tag_hook: Option<&Bound<'_, PyAny>>,
4,344✔
170
        object_hook: Option<&Bound<'_, PyAny>>,
4,344✔
171
        semantic_decoders: Option<&Bound<'_, PyMapping>>,
4,344✔
172
        str_errors: &str,
4,344✔
173
        read_size: usize,
4,344✔
174
        max_depth: usize,
4,344✔
175
        allow_indefinite: bool,
4,344✔
176
    ) -> PyResult<Self> {
4,344✔
177
        let available_bytes = if let Some(buffer) = buffer.as_ref() {
4,344✔
178
            buffer.len()?
3,984✔
179
        } else {
180
            0
360✔
181
        };
182
        let bound_str_errors = PyString::new(py, str_errors);
4,344✔
183
        let mut this = Self {
4,344✔
184
            fp: None,
4,344✔
185
            tag_hook: None,
4,344✔
186
            object_hook: None,
4,344✔
187
            str_errors: None,
4,344✔
188
            read_size,
4,344✔
189
            max_depth,
4,344✔
190
            allow_indefinite,
4,344✔
191
            semantic_decoders: semantic_decoders.map(|d| d.clone().unbind()),
4,344✔
192
            read_method: None,
4,344✔
193
            buffer: buffer.map(Bound::unbind),
4,344✔
194
            read_position: 0,
195
            available_bytes,
4,344✔
196
            fp_is_seekable: false,
197
        };
198
        if let Some(fp) = fp {
4,344✔
199
            this.set_fp(fp)?
360✔
200
        };
3,984✔
201
        this.set_tag_hook(tag_hook)?;
4,320✔
202
        this.set_object_hook(object_hook)?;
4,308✔
203
        this.set_str_errors(&bound_str_errors)?;
4,296✔
204
        Ok(this)
4,284✔
205
    }
4,344✔
206

207
    fn read_from_fp<'py>(
384✔
208
        &mut self,
384✔
209
        py: Python<'py>,
384✔
210
        minimum_amount: usize,
384✔
211
    ) -> PyResult<(Bound<'py, PyBytes>, usize)> {
384✔
212
        let read_size: usize = if self.fp_is_seekable {
384✔
213
            self.read_size
252✔
214
        } else {
215
            1
132✔
216
        };
217
        let bytes_to_read = minimum_amount.max(read_size);
384✔
218
        let num_read_bytes = if let Some(read) = self.read_method.as_ref() {
384✔
219
            let bytes_from_fp: Bound<PyBytes> =
276✔
220
                read.bind(py).call1((&bytes_to_read,))?.cast_into()?;
276✔
221
            let num_read_bytes = bytes_from_fp.len()?;
276✔
222
            if num_read_bytes >= minimum_amount {
276✔
223
                return Ok((bytes_from_fp, num_read_bytes));
228✔
224
            }
48✔
225
            num_read_bytes
48✔
226
        } else {
227
            0
108✔
228
        };
229
        Err(CBORDecodeEOF::new_err(format!(
156✔
230
            "premature end of stream (expected to read at least {minimum_amount} \
156✔
231
                 bytes, got {num_read_bytes} instead)"
156✔
232
        )))
156✔
233
    }
384✔
234

235
    fn read_exact<const N: usize>(&mut self, py: Python<'_>) -> PyResult<[u8; N]> {
35,364✔
236
        if self.available_bytes == 0 {
35,364✔
237
            // No buffer
238
            let (new_bytes, amount_read) = self.read_from_fp(py, N)?;
240✔
239
            self.read_position = N;
216✔
240
            self.available_bytes = amount_read - N;
216✔
241
            self.buffer = Some(new_bytes.unbind());
216✔
242
            Ok(self.buffer.as_ref().unwrap().as_bytes(py)[..N].try_into()?)
216✔
243
        } else if self.available_bytes < N {
35,124✔
244
            // Combine the remnants of the partial buffer with new data read from the file
NEW
245
            let needed_bytes = N - self.available_bytes;
×
NEW
246
            let mut concatenated_buffer: Vec<u8> = self.buffer.take().unwrap().extract(py)?;
×
NEW
247
            let (new_bytes, amount_read) = self.read_from_fp(py, needed_bytes)?;
×
NEW
248
            concatenated_buffer.extend_from_slice(&new_bytes[..needed_bytes]);
×
NEW
249
            self.buffer = Some(new_bytes.unbind());
×
NEW
250
            self.available_bytes = amount_read - needed_bytes;
×
NEW
251
            self.read_position = needed_bytes;
×
NEW
252
            Ok(concatenated_buffer.try_into().unwrap())
×
253
        } else {
254
            // Return a slice from the existing bytes object
255
            let slice: [u8; N] = self.buffer.as_ref().unwrap().bind(py).as_bytes()
35,124✔
256
                [self.read_position..self.read_position + N]
35,124✔
257
                .try_into()?;
35,124✔
258
            self.available_bytes -= N;
35,124✔
259
            self.read_position += N;
35,124✔
260
            Ok(slice)
35,124✔
261
        }
262
    }
35,364✔
263

264
    fn read_major_and_subtype(&mut self, py: Python<'_>) -> PyResult<(u8, u8)> {
29,760✔
265
        let initial_byte = self.read_exact::<1>(py)?[0];
29,760✔
266
        let major_type = initial_byte >> 5;
29,736✔
267
        let subtype = initial_byte & 31;
29,736✔
268
        Ok((major_type, subtype))
29,736✔
269
    }
29,760✔
270

271
    fn decode_length_finite(&mut self, py: Python<'_>, subtype: u8) -> PyResult<usize> {
7,656✔
272
        match self.decode_length(py, subtype)? {
7,656✔
273
            Some(length) => Ok(length),
7,620✔
274
            None => Err(CBORDecodeValueError::new_err(
24✔
275
                "indefinite length not allowed here",
24✔
276
            )),
24✔
277
        }
278
    }
7,656✔
279
    //
280
    // Decoders for major tags (0-7)
281
    //
282

283
    /// Decode the length of the next item.
284
    ///
285
    /// This is a low-level operation that may be needed by custom decoder callbacks.
286
    ///
287
    /// :param subtype:
288
    /// :return: the length of the item, or :data:`None` to indicate an indefinite-length item
289
    fn decode_length(&mut self, py: Python<'_>, subtype: u8) -> PyResult<Option<usize>> {
27,264✔
290
        let length = match subtype {
27,264✔
291
            ..24 => Some(subtype as usize),
27,264✔
292
            24 => Some(self.read_exact::<1>(py)?[0] as usize),
2,112✔
293
            25 => Some(u16::from_be_bytes(self.read_exact(py)?) as usize),
1,572✔
294
            26 => Some(u32::from_be_bytes(self.read_exact(py)?) as usize),
372✔
295
            27 => Some(u64::from_be_bytes(self.read_exact(py)?) as usize),
228✔
296
            31 => {
297
                if !self.allow_indefinite {
360✔
298
                    return Err(CBORDecodeError::new_err(
12✔
299
                        "encountered indefinite length but it has been disabled",
12✔
300
                    ));
12✔
301
                }
348✔
302
                None
348✔
303
            },
304
            _ => {
305
                return Err(CBORDecodeValueError::new_err(format!(
12✔
306
                    "unknown unsigned integer subtype 0x{subtype:x}"
12✔
307
                )));
12✔
308
            }
309
        };
310
        Ok(length)
27,240✔
311
    }
27,264✔
312

313
    fn decode_uint<'py>(&mut self, py: Python<'py>, subtype: u8) -> PyResult<DecoderResult<'py>> {
3,300✔
314
        // Major tag 0
315
        let uint = self.decode_length_finite(py, subtype)?;
3,300✔
316
        Ok(Value(uint.into_bound_py_any(py)?))
3,288✔
317
    }
3,300✔
318

319
    fn decode_negint<'py>(&mut self, py: Python<'py>, subtype: u8) -> PyResult<DecoderResult<'py>> {
768✔
320
        // Major tag 1
321
        let uint = self.decode_length_finite(py, subtype)?;
768✔
322
        let signed_int = -(uint as i128) - 1;
768✔
323
        Ok(Value(signed_int.into_bound_py_any(py)?))
768✔
324
    }
768✔
325

326
    fn decode_bytestring<'py>(
1,128✔
327
        &mut self,
1,128✔
328
        py: Python<'py>,
1,128✔
329
        subtype: u8,
1,128✔
330
    ) -> PyResult<DecoderResult<'py>> {
1,128✔
331
        // Major tag 2
332
        match self.decode_length(py, subtype)? {
1,128✔
333
            None => {
334
                // Indefinite length
335
                let mut bytes = PyBytes::new(py, b"");
72✔
336
                let sys_maxsize = *SYS_MAXSIZE.get(py).unwrap();
72✔
337
                loop {
338
                    let (major_type, subtype) = self.read_major_and_subtype(py)?;
120✔
339
                    match (major_type, subtype) {
120✔
340
                        (2, _) => {
341
                            let length = self.decode_length_finite(py, subtype)?;
84✔
342
                            if length > sys_maxsize {
72✔
343
                                return Err(CBORDecodeValueError::new_err(format!(
12✔
344
                                    "chunk too long in an indefinite bytestring chunk: {length}"
12✔
345
                                )));
12✔
346
                            }
60✔
347
                            let chunk = self.read(py, length)?;
60✔
348
                            bytes = bytes.add(chunk)?.cast_into()?;
48✔
349
                        }
350
                        (7, 31) => break Ok(Value(bytes.into_any())), // break marker
12✔
351
                        _ => {
352
                            return Err(CBORDecodeValueError::new_err(format!(
24✔
353
                                "non-byte string (major type {major_type}) found in indefinite \
24✔
354
                                    length byte string"
24✔
355
                            )));
24✔
356
                        }
357
                    }
358
                }
359
            }
360
            Some(length) if length <= 65536 => {
1,056✔
361
                let bytes = self.read(py, length)?;
1,032✔
362
                Ok(StringValue(PyBytes::new(py, &bytes).into_any(), length))
996✔
363
            }
364
            Some(length) => {
24✔
365
                // Incrementally read the bytestring, in chunks of 65536 bytes
366
                let mut bytes = PyBytes::new(py, b"");
24✔
367
                let mut remaining_length = length;
24✔
368
                while remaining_length > 0 {
48✔
369
                    let chunk_size = remaining_length.min(65536);
36✔
370
                    let chunk = self.read(py, chunk_size)?;
36✔
371
                    remaining_length -= chunk_size;
24✔
372
                    bytes = bytes.add(chunk)?.cast_into()?;
24✔
373
                }
374
                Ok(StringValue(bytes.into_any(), length))
12✔
375
            }
376
        }
377
    }
1,128✔
378

379
    fn decode_string<'py>(&mut self, py: Python<'py>, subtype: u8) -> PyResult<DecoderResult<'py>> {
2,160✔
380
        // Major tag 3
381
        match self.decode_length(py, subtype)? {
2,160✔
382
            None => {
383
                // Indefinite length
384
                let mut string = PyString::new(py, "");
96✔
385
                loop {
386
                    let (major_type, subtype) = self.read_major_and_subtype(py)?;
168✔
387
                    let sys_maxsize = *SYS_MAXSIZE.get(py).unwrap();
168✔
388
                    match (major_type, subtype) {
168✔
389
                        (3, _) => {
390
                            let length = self.decode_length_finite(py, subtype)?;
120✔
391
                            if length > sys_maxsize {
108✔
392
                                return Err(CBORDecodeValueError::new_err(format!(
12✔
393
                                    "chunk too long in an indefinite text string chunk: {length}"
12✔
394
                                )));
12✔
395
                            }
96✔
396
                            let bytes = self.read(py, length)?;
96✔
397
                            let decoded = match self.str_errors.as_ref() {
84✔
398
                                None => PyString::from_bytes(py, bytes.as_slice()),
84✔
NEW
399
                                Some(str_errors) => bytes
×
NEW
400
                                    .into_bound_py_any(py)?
×
NEW
401
                                    .call_method1(
×
NEW
402
                                        intern!(py, "decode"),
×
NEW
403
                                        (intern!(py, "utf-8"), str_errors),
×
404
                                    )
NEW
405
                                    .and_then(|string| {
×
NEW
406
                                        string.cast_into().map_err(|e| PyErr::from(e))
×
NEW
407
                                    }),
×
408
                            }
409
                            .map_err(|e| {
84✔
410
                                let exc =
12✔
411
                                    CBORDecodeValueError::new_err("error decoding text string");
12✔
412
                                exc.set_cause(py, Some(e));
12✔
413
                                exc
12✔
414
                            })?;
12✔
415
                            string = string.add(decoded)?.cast_into()?;
72✔
416
                        }
417
                        (7, 31) => break Ok(Value(string.into_any())), // break marker
24✔
418
                        _ => {
419
                            return Err(CBORDecodeValueError::new_err(format!(
24✔
420
                                "non-text string (major type {major_type}) found in indefinite \
24✔
421
                                    length text string"
24✔
422
                            )));
24✔
423
                        }
424
                    }
425
                }
426
            }
427
            Some(length) if length <= 65536 => {
2,052✔
428
                let bytes = self.read(py, length)?;
2,028✔
429
                let decode_result: PyResult<Bound<'_, PyString>> = match self.str_errors.as_ref() {
1,980✔
430
                    None => PyString::from_bytes(py, bytes.as_slice()),
1,968✔
431
                    Some(str_errors) => bytes
12✔
432
                        .into_bound_py_any(py)?
12✔
433
                        .call_method1(
12✔
434
                            intern!(py, "decode"),
12✔
435
                            (intern!(py, "utf-8"), str_errors.bind(py)),
12✔
NEW
436
                        )?
×
437
                        .cast_into()
12✔
438
                        .map_err(PyErr::from),
12✔
439
                };
440
                if let Ok(decoded_bytes) = decode_result {
1,980✔
441
                    Ok(StringValue(
442
                        decoded_bytes.cast_into().map_err(PyErr::from)?,
1,956✔
443
                        length,
1,956✔
444
                    ))
445
                } else {
446
                    raise_exc_from(
24✔
447
                        py,
24✔
448
                        CBORDecodeValueError::new_err("error decoding text string"),
24✔
449
                        Some(decode_result.unwrap_err()),
24✔
450
                    )
451
                }
452
            }
453
            Some(mut length) => {
24✔
454
                // Incrementally decode the string, in chunks of 65536 bytes
455
                let decoder_class = INCREMENTAL_UTF8_DECODER
24✔
456
                    .get_or_try_init(py, || -> PyResult<Py<PyAny>> {
24✔
457
                        let decoder = py
12✔
458
                            .import("codecs")?
12✔
459
                            .getattr("lookup")?
12✔
460
                            .call1(("utf-8",))?
12✔
461
                            .getattr("incrementaldecoder")?;
12✔
462
                        Ok(decoder.unbind())
12✔
463
                    })?
12✔
464
                    .bind(py);
24✔
465
                let decoder = match self.str_errors.as_ref() {
24✔
466
                    None => decoder_class.call0()?,
24✔
NEW
467
                    Some(str_errors) => decoder_class.call1((str_errors,))?,
×
468
                };
469
                let mut string = PyString::new(py, "");
24✔
470
                while length > 0 {
84✔
471
                    let chunk_size = length.min(65536);
60✔
472
                    let chunk = self.read(py, chunk_size)?;
60✔
473
                    length -= chunk_size;
60✔
474
                    let is_final_chunk = length == 0;
60✔
475
                    let decode_result =
60✔
476
                        decoder.call_method1(intern!(py, "decode"), (chunk, is_final_chunk));
60✔
477
                    let decoded_chunk: Bound<'_, PyString> = match decode_result {
60✔
478
                        Ok(decoded_chunk) => decoded_chunk.cast_into()?,
60✔
NEW
479
                        Err(e) => {
×
NEW
480
                            return raise_exc_from(
×
NEW
481
                                py,
×
NEW
482
                                CBORDecodeValueError::new_err("error decoding text string"),
×
NEW
483
                                Some(e),
×
484
                            );
485
                        }
486
                    };
487
                    string = string.add(decoded_chunk)?.cast_into()?;
60✔
488
                }
489
                Ok(StringValue(string.into_any(), length))
24✔
490
            }
491
        }
492
    }
2,160✔
493

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

509
                Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
3,048✔
510
                    items.push(item);
3,048✔
511
                    if items.len() == length {
3,048✔
512
                        Ok(CompleteFrame(
513
                            PyTuple::new(py, take(&mut items))?.into_any(),
1,392✔
514
                        ))
515
                    } else {
516
                        Ok(ContinueFrame(false))
1,656✔
517
                    }
518
                })
3,048✔
519
            } else {
520
                Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
72✔
521
                    if item.is_exact_instance_of::<BreakMarkerType>() {
72✔
522
                        Ok(CompleteFrame(
523
                            PyTuple::new(py, take(&mut items))?.into_any(),
12✔
524
                        ))
525
                    } else {
526
                        items.push(item);
60✔
527
                        Ok(ContinueFrame(false))
60✔
528
                    }
529
                })
72✔
530
            };
531
            Ok(BeginFrame(callback, false, None))
1,464✔
532
        } else {
533
            let mut list = PyList::empty(py);
13,080✔
534
            let container = list.clone().into_any();
13,080✔
535
            let callback: Box<DecoderCallback<'py>> = if let Some(length) = optional_length {
13,080✔
536
                if length == 0 {
12,984✔
537
                    return Ok(Value(PyList::empty(py).into_any()));
36✔
538
                }
12,948✔
539

540
                Box::new(move |item, _immutable: bool| {
12,948✔
541
                    list.append(item)?;
2,208✔
542
                    if list.len() == length {
2,208✔
543
                        Ok(CompleteFrame(
780✔
544
                            replace(&mut list, PyList::empty(py)).into_any(),
780✔
545
                        ))
780✔
546
                    } else {
547
                        Ok(ContinueFrame(false))
1,428✔
548
                    }
549
                })
2,208✔
550
            } else {
551
                Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
564✔
552
                    if item.is_exact_instance_of::<BreakMarkerType>() {
564✔
553
                        Ok(CompleteFrame(
96✔
554
                            replace(&mut list, PyList::empty(py)).into_any(),
96✔
555
                        ))
96✔
556
                    } else {
557
                        list.append(item)?;
468✔
558
                        Ok(ContinueFrame(false))
468✔
559
                    }
560
                })
564✔
561
            };
562
            Ok(BeginFrame(callback, false, Some(container)))
13,044✔
563
        }
564
    }
14,580✔
565

566
    fn decode_map<'py>(
1,740✔
567
        &mut self,
1,740✔
568
        py: Python<'py>,
1,740✔
569
        subtype: u8,
1,740✔
570
        immutable: bool,
1,740✔
571
    ) -> PyResult<DecoderResult<'py>> {
1,740✔
572
        // Major tag 5
573

574
        #[cfg(Py_3_15)]
575
        fn create_frozen_dict<'py>(
12✔
576
            py: Python<'py>,
12✔
577
            items: Vec<(Bound<'py, PyAny>, Bound<'py, PyAny>)>,
12✔
578
        ) -> PyResult<Bound<'py, PyAny>> {
12✔
579
            FROZEN_DICT
12✔
580
                .get(py)?
12✔
581
                .call1((items,))?
12✔
582
                .cast_into()
12✔
583
                .map_err(|e| PyErr::from(e))
12✔
584
        }
12✔
585
        #[cfg(not(Py_3_15))]
586
        fn create_frozen_dict<'py>(
132✔
587
            py: Python<'py>,
132✔
588
            items: Vec<(Bound<'py, PyAny>, Bound<'py, PyAny>)>,
132✔
589
        ) -> PyResult<Bound<'py, PyAny>> {
132✔
590
            FrozenDict::from_items(py, items).map(|dict| dict.into_any())
132✔
591
        }
132✔
592

593
        #[inline]
594
        fn maybe_call_object_hook<'py>(
1,584✔
595
            py: Python<'py>,
1,584✔
596
            dict: Bound<'py, PyAny>,
1,584✔
597
            object_hook: Option<&Py<PyAny>>,
1,584✔
598
            immutable: bool,
1,584✔
599
        ) -> PyResult<Bound<'py, PyAny>> {
1,584✔
600
            if let Some(object_hook) = object_hook {
1,584✔
601
                object_hook.bind(py).call1((dict, immutable))
24✔
602
            } else {
603
                Ok(dict)
1,560✔
604
            }
605
        }
1,584✔
606

607
        let object_hook = self.object_hook.as_ref().map(|hook| hook.clone_ref(py));
1,740✔
608
        let length_or_none = self.decode_length(py, subtype)?;
1,740✔
609

610
        // Return immediately if this is an empty dict
611
        if let Some(length) = length_or_none
1,740✔
612
            && length == 0
1,704✔
613
        {
614
            let container: Bound<'py, PyAny> = if immutable {
396✔
NEW
615
                create_frozen_dict(py, Vec::new())?
×
616
            } else {
617
                PyDict::new(py).into_any()
396✔
618
            };
619
            let transformed =
396✔
620
                maybe_call_object_hook(py, container, object_hook.as_ref(), immutable)?;
396✔
621
            return Ok(Value(transformed));
396✔
622
        };
1,344✔
623

624
        let mut key: Option<Bound<'py, PyAny>> = None;
1,344✔
625
        if immutable {
1,344✔
626
            let mut items: Vec<(Bound<'py, PyAny>, Bound<'py, PyAny>)> = Vec::new();
276✔
627
            let callback: Box<DecoderCallback<'py>> = if let Some(length) = length_or_none {
276✔
628
                Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
348✔
629
                    if let Some(key) = key.take() {
348✔
630
                        items.push((key, item));
168✔
631
                        if items.len() == length {
168✔
632
                            let transformed = maybe_call_object_hook(
144✔
633
                                py,
144✔
634
                                create_frozen_dict(py, take(&mut items))?,
144✔
635
                                object_hook.as_ref(),
144✔
636
                                immutable,
144✔
NEW
637
                            )?;
×
638
                            return Ok(CompleteFrame(transformed));
144✔
639
                        }
24✔
640
                        Ok(ContinueFrame(true))
24✔
641
                    } else {
642
                        key = Some(item);
180✔
643
                        Ok(ContinueFrame(false))
180✔
644
                    }
645
                })
348✔
646
            } else {
NEW
647
                Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
×
NEW
648
                    if item.is_exact_instance_of::<BreakMarkerType>() {
×
NEW
649
                        let container = create_frozen_dict(py, take(&mut items))?;
×
NEW
650
                        let transformed = maybe_call_object_hook(
×
NEW
651
                            py,
×
NEW
652
                            container.into_any(),
×
NEW
653
                            object_hook.as_ref(),
×
NEW
654
                            immutable,
×
NEW
655
                        )?;
×
NEW
656
                        return Ok(CompleteFrame(transformed));
×
NEW
657
                    } else if let Some(key) = key.take() {
×
NEW
658
                        items.push((key, item));
×
NEW
659
                        Ok(ContinueFrame(true))
×
660
                    } else {
NEW
661
                        key = Some(item);
×
NEW
662
                        Ok(ContinueFrame(false))
×
663
                    }
NEW
664
                })
×
665
            };
666
            Ok(BeginFrame(callback, true, None))
276✔
667
        } else {
668
            let mut dict = PyDict::new(py);
1,068✔
669
            let container = dict.clone().into_any();
1,068✔
670
            let callback: Box<DecoderCallback<'py>> = if let Some(length) = length_or_none {
1,068✔
671
                Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
3,120✔
672
                    if let Some(key) = key.take() {
3,120✔
673
                        dict.set_item(key, item)?;
1,560✔
674
                        if dict.len() == length {
1,560✔
675
                            let dict = replace(&mut dict, PyDict::new(py));
1,008✔
676
                            let transformed = maybe_call_object_hook(
1,008✔
677
                                py,
1,008✔
678
                                dict.into_any(),
1,008✔
679
                                object_hook.as_ref(),
1,008✔
680
                                immutable,
1,008✔
681
                            )?;
12✔
682
                            return Ok(CompleteFrame(transformed));
996✔
683
                        }
552✔
684
                        Ok(ContinueFrame(true))
552✔
685
                    } else {
686
                        key = Some(item);
1,560✔
687
                        Ok(ContinueFrame(false))
1,560✔
688
                    }
689
                })
3,120✔
690
            } else {
691
                Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
156✔
692
                    if item.is_exact_instance_of::<BreakMarkerType>() {
156✔
693
                        let dict = replace(&mut dict, PyDict::new(py));
36✔
694
                        let transformed = maybe_call_object_hook(
36✔
695
                            py,
36✔
696
                            dict.into_any(),
36✔
697
                            object_hook.as_ref(),
36✔
698
                            immutable,
36✔
NEW
699
                        )?;
×
700
                        return Ok(CompleteFrame(transformed));
36✔
701
                    } else if let Some(key) = key.take() {
120✔
702
                        dict.set_item(key, item)?;
60✔
703
                        Ok(ContinueFrame(true))
60✔
704
                    } else {
705
                        key = Some(item);
60✔
706
                        Ok(ContinueFrame(false))
60✔
707
                    }
708
                })
156✔
709
            };
710
            Ok(BeginFrame(callback, true, Some(container)))
1,068✔
711
        }
712
    }
1,740✔
713

714
    fn decode_semantic<'py>(
3,384✔
715
        &mut self,
3,384✔
716
        py: Python<'py>,
3,384✔
717
        subtype: u8,
3,384✔
718
        immutable: bool,
3,384✔
719
    ) -> PyResult<DecoderResult<'py>> {
3,384✔
720
        let tagnum = self.decode_length_finite(py, subtype)?;
3,384✔
721
        if let Some(semantic_decoders) = &self.semantic_decoders {
3,384✔
722
            match semantic_decoders.bind(py).get_item(&tagnum) {
96✔
723
                Ok(decoder) => {
72✔
724
                    let name = decoder.getattr_opt(intern!(py, NAME_ATTR))?;
72✔
725

726
                    // If these attributes are present, this callable was decorated with
727
                    // @shareable_decoder
728
                    #[allow(unused_variables)]
729
                    return if let Some(name) = name {
72✔
730
                        let require_immutable: bool = decoder
36✔
731
                            .getattr_opt(intern!(py, IMMUTABLE_ATTR))?
36✔
732
                            .map(|x| x.is_truthy())
36✔
733
                            .transpose()?
36✔
734
                            .unwrap_or(false);
36✔
735
                        let retval = decoder.call1((immutable,))?;
36✔
736
                        let tuple: Bound<'_, PyTuple> = retval.cast_into()?;
36✔
737
                        if tuple.len() != 2 {
36✔
NEW
738
                            return Err(CBORDecodeError::new_err(format!(
×
NEW
739
                                "{decoder} returned a tuple of {} items, expected 2",
×
NEW
740
                                tuple.len()
×
NEW
741
                            )));
×
742
                        }
36✔
743
                        let container: Bound<'_, PyAny> = tuple.get_item(0)?.cast_into()?;
36✔
744
                        let callback: Bound<'_, PyAny> = tuple.get_item(1)?.cast_into()?;
36✔
745
                        Ok(BeginFrame(
746
                            Box::new(
36✔
747
                                move |item, _immutable: bool| -> PyResult<DecoderResult<'py>> {
36✔
748
                                    callback.call1((item,)).map(CompleteFrame)
36✔
749
                                },
36✔
750
                            ),
751
                            require_immutable,
36✔
752
                            if container.is_none() {
36✔
753
                                None
24✔
754
                            } else {
755
                                Some(container)
12✔
756
                            },
757
                        ))
758
                    } else {
759
                        let callback =
36✔
760
                            move |item, new_immutable: bool| -> PyResult<DecoderResult<'py>> {
36✔
761
                                decoder.call1((item, new_immutable)).map(CompleteFrame)
36✔
762
                            };
36✔
763
                        Ok(BeginFrame(Box::new(callback), immutable, None))
36✔
764
                    };
765
                }
766
                Err(e) if e.is_instance_of::<PyLookupError>(py) => {}
24✔
NEW
767
                Err(e) => return Err(e),
×
768
            }
769
        };
3,288✔
770

771
        // No semantic decoder lookup map – fall back to the hard coded switchboard
772
        let callback: Box<DecoderCallback<'py>> = match tagnum {
3,312✔
773
            0 => Box::new(Self::decode_datetime_string),
456✔
774
            1 => Box::new(Self::decode_epoch_datetime),
60✔
775
            2 => Box::new(Self::decode_positive_bignum),
132✔
776
            3 => Box::new(Self::decode_negative_bignum),
36✔
777
            4 => Box::new(Self::decode_fraction),
264✔
778
            5 => Box::new(Self::decode_bigfloat),
24✔
779
            25 => Box::new(Self::decode_stringref),
96✔
780
            28 => return Ok(Shareable),
240✔
781
            29 => Box::new(Self::decode_sharedref),
180✔
782
            30 => Box::new(Self::decode_rational),
252✔
783
            35 => Box::new(Self::decode_regexp),
36✔
784
            36 => Box::new(Self::decode_mime),
36✔
785
            37 => Box::new(Self::decode_uuid),
300✔
786
            52 => Box::new(Self::decode_ipv4),
120✔
787
            54 => Box::new(Self::decode_ipv6),
72✔
788
            100 => Box::new(Self::decode_epoch_date),
12✔
789
            256 => return Ok(StringNamespace),
36✔
790
            258 => return self.decode_set(py, immutable),
408✔
791
            260 => Box::new(Self::decode_ipaddress),
84✔
792
            261 => Box::new(Self::decode_ipnetwork),
84✔
793
            1004 => Box::new(Self::decode_date_string),
12✔
794
            43000 => Box::new(Self::decode_complex),
252✔
795
            55799 => Box::new(Self::decode_self_describe_cbor),
24✔
796
            _ => {
797
                // For a tag with no designated decoder, check if we have a tag hook, and call
798
                // that with the tag object, using its return value as the decoded value.
799
                let tag = CBORTag::new(tagnum.into_bound_py_any(py)?, py.None().into_bound(py))?;
96✔
800
                let bound_tag = Bound::new(py, tag)?.into_any();
96✔
801
                let container = bound_tag.clone();
96✔
802
                let mut tag_hook = self
96✔
803
                    .tag_hook
96✔
804
                    .as_ref()
96✔
805
                    .map(|hook| hook.clone_ref(py).into_bound(py));
96✔
806
                let callback = Box::new(move |item: Bound<'py, PyAny>, _immutable: bool| {
96✔
807
                    let tag: &Bound<'py, CBORTag> = bound_tag.cast()?;
84✔
808
                    tag.borrow_mut().value = item.unbind();
84✔
809
                    if let Some(tag_hook) = tag_hook.take() {
84✔
810
                        tag_hook.call1((&bound_tag, immutable)).map(CompleteFrame)
60✔
811
                    } else {
812
                        Ok(CompleteFrame(bound_tag.clone()))
24✔
813
                    }
814
                });
84✔
815
                return Ok(BeginFrame(callback, true, Some(container)));
96✔
816
            }
817
        };
818
        Ok(BeginFrame(callback, true, None))
2,532✔
819
    }
3,384✔
820

821
    fn decode_special<'py>(
2,388✔
822
        &mut self,
2,388✔
823
        py: Python<'py>,
2,388✔
824
        subtype: u8,
2,388✔
825
    ) -> PyResult<DecoderResult<'py>> {
2,388✔
826
        // Major tag 7
827
        match subtype {
2,388✔
828
            0..20 => {
2,388✔
829
                let value = subtype.into_pyobject(py)?;
72✔
830
                CBORSimpleValue::new(value)?.into_bound_py_any(py)
72✔
831
            }
832
            20 => Ok(false.into_bound_py_any(py)?),
132✔
833
            21 => Ok(true.into_bound_py_any(py)?),
144✔
834
            22 => Ok(py.None().into_bound_py_any(py)?),
504✔
835
            23 => Ok(UNDEFINED.get(py).unwrap().into_bound_py_any(py)?),
24✔
836
            24 => {
837
                let value = self.read_exact::<1>(py)?[0];
84✔
838
                if value < 0x20 {
84✔
839
                    return Err(CBORDecodeValueError::new_err(
36✔
840
                        "invalid two-byte sequence for simple value",
36✔
841
                    ));
36✔
842
                }
48✔
843
                CBORSimpleValue::new(value.into_pyobject(py)?)?.into_bound_py_any(py)
48✔
844
            }
845
            25 => {
846
                let bytes = self.read_exact::<2>(py)?;
684✔
847
                f16::from_be_bytes(bytes).to_f32().into_bound_py_any(py)
684✔
848
            }
849
            26 => {
850
                let bytes = self.read_exact::<4>(py)?;
108✔
851
                f32::from_be_bytes(bytes).into_bound_py_any(py)
108✔
852
            }
853
            27 => {
854
                let bytes = self.read_exact::<8>(py)?;
444✔
855
                f64::from_be_bytes(bytes).into_bound_py_any(py)
444✔
856
            }
857
            31 => Ok(BREAK_MARKER.get(py).unwrap().into_bound_py_any(py)?),
156✔
858
            _ => Err(CBORDecodeValueError::new_err(format!(
36✔
859
                "undefined reserved major type 7 subtype 0x{subtype:x}"
36✔
860
            ))),
36✔
861
        }
862
        .map(Value)
2,352✔
863
    }
2,388✔
864

865
    //
866
    // Decoders for semantic tags (major tag 6)
867
    //
868

869
    fn decode_datetime_string<'py>(
456✔
870
        value: Bound<'py, PyAny>,
456✔
871
        _immutable: bool,
456✔
872
    ) -> PyResult<DecoderResult<'py>> {
456✔
873
        // Semantic tag 0
874
        let py = value.py();
456✔
875
        let value_type = value.get_type();
456✔
876
        let mut datetime_str: Bound<'py, PyString> = value.cast_into().map_err(|e| {
456✔
NEW
877
            create_exc_from(
×
NEW
878
                py,
×
NEW
879
                CBORDecodeValueError::new_err(format!(
×
880
                    "expected string for tag, got {} instead",
NEW
881
                    value_type.to_string()
×
882
                )),
NEW
883
                Some(PyErr::from(e)),
×
884
            )
NEW
885
        })?;
×
886

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

894
            // Pad any microseconds part with zeros
895
            if let Some((first, second)) = temp_str.split_once('.') {
114✔
896
                if let Some(index) = second.find(|c: char| !c.is_numeric()) {
576✔
897
                    let (mut micros, tz_part) = second.split_at(index);
78✔
898
                    // Cut off excess zeroes from the start of the microseconds part
899
                    if micros.len() >= 6 {
78✔
900
                        micros = &micros[..6];
63✔
901
                    }
63✔
902

903
                    // Reconstitute the datetime string, right-padding the microseconds part
904
                    // with zeroes
905
                    temp_str = format!("{first}.{micros:0<6}{tz_part}");
78✔
NEW
906
                }
×
907
            }
36✔
908

909
            datetime_str = temp_str.into_pyobject(py)?;
114✔
910
        }
342✔
911

912
        DATETIME_FROMISOFORMAT
456✔
913
            .get(py)?
456✔
914
            .call1((&datetime_str,))
456✔
915
            .map_err(|e| {
456✔
916
                create_exc_from(
12✔
917
                    py,
12✔
918
                    CBORDecodeValueError::new_err(format!(
12✔
919
                        "invalid datetime string: '{datetime_str}'"
920
                    )),
921
                    Some(e),
12✔
922
                )
923
            })
12✔
924
            .map(CompleteFrame)
456✔
925
    }
456✔
926

927
    fn decode_epoch_datetime(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
60✔
928
        // Semantic tag 1
929
        let py = value.py();
60✔
930
        let utc = UTC.get(py)?;
60✔
931
        DATETIME_FROMTIMESTAMP
60✔
932
            .get(py)?
60✔
933
            .call1((value, utc))
60✔
934
            .map(CompleteFrame)
60✔
935
    }
60✔
936

937
    fn decode_positive_bignum(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
108✔
938
        // Semantic tag 2
939
        let py = value.py();
108✔
940
        INT_FROMBYTES
108✔
941
            .get(py)?
108✔
942
            .call1((value, intern!(py, "big")))
108✔
943
            .map(CompleteFrame)
108✔
944
    }
108✔
945

946
    fn decode_negative_bignum(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
36✔
947
        // Semantic tag 3
948
        let py = value.py();
36✔
949
        let int = INT_FROMBYTES.get(py)?.call1((value, intern!(py, "big")))?;
36✔
950
        int.neg()?.add(-1).map(CompleteFrame)
36✔
951
    }
36✔
952

953
    fn decode_fraction(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
264✔
954
        // Semantic tag 4
955
        let py = value.py();
264✔
956
        let tuple = value.cast::<PyTuple>().map_err(|e| {
264✔
957
            create_exc_from(
12✔
958
                py,
12✔
959
                CBORDecodeValueError::new_err(
12✔
960
                    "error decoding decimal fraction: input value must be an array",
961
                ),
962
                Some(PyErr::from(e)),
12✔
963
            )
964
        })?;
12✔
965

966
        if tuple.len() != 2 {
252✔
NEW
967
            return Err(CBORDecodeValueError::new_err(
×
NEW
968
                "error decoding decimal fraction: array must have exactly two elements",
×
NEW
969
            ));
×
970
        }
252✔
971

972
        let decimal_class = DECIMAL_TYPE.get(py)?;
252✔
973
        {
974
            let exp = tuple.get_item(0)?;
252✔
975
            let sig_tuple = decimal_class
252✔
976
                .call1((tuple.get_item(1)?,))?
252✔
977
                .call_method0(intern!(py, "as_tuple"))?
252✔
978
                .cast_into::<PyTuple>()?;
252✔
979
            let sign = sig_tuple.get_item(0)?;
252✔
980
            let digits = sig_tuple.get_item(1)?;
252✔
981
            let args_tuple = PyTuple::new(py, [sign, digits, exp])?;
252✔
982
            decimal_class.call1((args_tuple,)).map(CompleteFrame)
252✔
983
        }
984
        .map_err(|e| {
252✔
NEW
985
            create_exc_from(
×
NEW
986
                py,
×
NEW
987
                CBORDecodeValueError::new_err("error decoding decimal fraction"),
×
NEW
988
                Some(e),
×
989
            )
NEW
990
        })
×
991
    }
264✔
992

993
    fn decode_bigfloat(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
24✔
994
        // Semantic tag 5
995
        let py = value.py();
24✔
996
        let tuple = value.cast::<PyTuple>().map_err(|e| {
24✔
997
            create_exc_from(
12✔
998
                py,
12✔
999
                CBORDecodeValueError::new_err(
12✔
1000
                    "error decoding bigfloat: input value must be an array",
1001
                ),
1002
                Some(PyErr::from(e)),
12✔
1003
            )
1004
        })?;
12✔
1005

1006
        if tuple.len() != 2 {
12✔
NEW
1007
            return Err(CBORDecodeValueError::new_err(
×
NEW
1008
                "error decoding bigfloat: array must have exactly two elements",
×
NEW
1009
            ));
×
1010
        }
12✔
1011

1012
        let decimal_class = DECIMAL_TYPE.get(py)?;
12✔
1013
        {
1014
            let exp = decimal_class.call1((tuple.get_item(0)?,))?;
12✔
1015
            let sig = decimal_class.call1((tuple.get_item(1)?,))?;
12✔
1016
            let exp = PyInt::new(py, 2).pow(exp, py.None())?;
12✔
1017
            sig.mul(exp).map(CompleteFrame)
12✔
1018
        }
1019
        .map_err(|e| {
12✔
NEW
1020
            create_exc_from(
×
NEW
1021
                py,
×
NEW
1022
                CBORDecodeValueError::new_err("error decoding bigfloat"),
×
NEW
1023
                Some(e),
×
1024
            )
NEW
1025
        })
×
1026
    }
24✔
1027

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

1034
    fn decode_sharedref(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
180✔
1035
        // Semantic tag 29
1036
        let index: usize = value.extract()?;
180✔
1037
        Ok(SharedReference(index))
180✔
1038
    }
180✔
1039

1040
    fn decode_rational(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
252✔
1041
        // Semantic tag 30
1042
        let py = value.py();
252✔
1043
        let tuple = value.cast_into::<PyTuple>().map_err(|e| {
252✔
1044
            create_exc_from(
12✔
1045
                py,
12✔
1046
                CBORDecodeValueError::new_err(
12✔
1047
                    "error decoding rational: input value must be an array",
1048
                ),
1049
                Some(PyErr::from(e)),
12✔
1050
            )
1051
        })?;
12✔
1052

1053
        if tuple.len() != 2 {
240✔
NEW
1054
            return Err(CBORDecodeValueError::new_err(
×
NEW
1055
                "error decoding rational: array must have exactly two elements",
×
NEW
1056
            ));
×
1057
        }
240✔
1058

1059
        match FRACTION_TYPE.get(py)?.call1(tuple) {
240✔
1060
            Ok(fraction) => Ok(CompleteFrame(fraction)),
228✔
1061
            Err(e) => raise_exc_from(
12✔
1062
                py,
12✔
1063
                CBORDecodeValueError::new_err("error decoding rational"),
12✔
1064
                Some(e),
12✔
1065
            ),
1066
        }
1067
    }
252✔
1068

1069
    fn decode_regexp(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
36✔
1070
        // Semantic tag 35
1071
        let py = value.py();
36✔
1072
        match RE_COMPILE.get(py)?.call1((value,)) {
36✔
1073
            Ok(regexp) => Ok(CompleteFrame(regexp)),
24✔
1074
            Err(e) => raise_exc_from(
12✔
1075
                py,
12✔
1076
                CBORDecodeValueError::new_err("error decoding regular expression"),
12✔
1077
                Some(e),
12✔
1078
            ),
1079
        }
1080
    }
36✔
1081

1082
    fn decode_mime(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
24✔
1083
        // Semantic tag 36
1084
        let py = value.py();
24✔
1085
        let parser = EMAIL_PARSER.get(py)?.call0()?;
24✔
1086
        match parser.call_method1(intern!(py, "parsestr"), (value,)) {
24✔
1087
            Ok(message) => Ok(CompleteFrame(message)),
12✔
1088
            Err(e) => raise_exc_from(
12✔
1089
                py,
12✔
1090
                CBORDecodeValueError::new_err("error decoding MIME message"),
12✔
1091
                Some(e),
12✔
1092
            ),
1093
        }
1094
    }
24✔
1095

1096
    fn decode_uuid(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
300✔
1097
        // Semantic tag 37
1098
        let py = value.py();
300✔
1099
        let kwargs = PyDict::new(py);
300✔
1100
        kwargs.set_item(intern!(py, "bytes"), value)?;
300✔
1101
        match UUID_TYPE.get(py)?.call((), Some(&kwargs)) {
300✔
1102
            Ok(uuid) => Ok(CompleteFrame(uuid)),
276✔
1103
            Err(e) => raise_exc_from(
24✔
1104
                py,
24✔
1105
                CBORDecodeValueError::new_err("error decoding UUID value"),
24✔
1106
                Some(e),
24✔
1107
            ),
1108
        }
1109
    }
300✔
1110

1111
    fn decode_ipv4(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
120✔
1112
        // Semantic tag 52
1113
        let py = value.py();
120✔
1114
        let addr = if let Ok(bytes) = value.cast::<PyBytes>() {
120✔
1115
            // The decoded value was a bytestring, so this is an IPv4 address
1116
            IPV4ADDRESS_TYPE.get(py)?.call1((bytes,))?
96✔
1117
        } else if let Ok(tuple) = value.cast_into::<PyTuple>()
24✔
1118
            && tuple.len() == 2
24✔
1119
        {
1120
            // The decoded value was a 2-item array. Check the types of the elements:
1121
            // (int, bytes) -> network
1122
            // (bytes, int) -> interface
1123
            let first_item = tuple.get_item(0)?;
24✔
1124
            let second_item = tuple.get_item(1)?;
24✔
1125
            if let Ok(prefix) = first_item.cast::<PyInt>()
24✔
1126
                && let Ok(address) = second_item.cast::<PyBytes>()
12✔
1127
            {
1128
                let mut address_vec: Vec<u8> = address.extract()?;
12✔
1129
                address_vec.resize(4, 0);
12✔
1130
                IPV4NETWORK_TYPE.get(py)?.call1(((address_vec, prefix),))?
12✔
1131
            } else if let Ok(address) = first_item.cast::<PyBytes>()
12✔
1132
                && let Ok(prefix) = second_item.cast::<PyInt>()
12✔
1133
            {
1134
                IPV4INTERFACE_TYPE.get(py)?.call1(((address, prefix),))?
12✔
1135
            } else {
NEW
1136
                return Err(CBORDecodeValueError::new_err(
×
NEW
1137
                    "error decoding IPv4: invalid types in input array",
×
NEW
1138
                ));
×
1139
            }
1140
        } else {
NEW
1141
            return Err(CBORDecodeValueError::new_err(
×
NEW
1142
                "error decoding IPv4: input value must be a bytestring or an array of 2 elements",
×
NEW
1143
            ));
×
1144
        };
1145
        Ok(CompleteFrame(addr))
120✔
1146
    }
120✔
1147

1148
    fn decode_ipv6(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
72✔
1149
        // Semantic tag 54
1150
        let py = value.py();
72✔
1151
        let ipv6addr_class = IPV6ADDRESS_TYPE.get(py)?;
72✔
1152
        let addr = if let Ok(bytes) = value.cast::<PyBytes>() {
72✔
1153
            // The decoded value was a bytestring, so this is an IPv6 address
1154
            ipv6addr_class.call1((bytes,))?
36✔
1155
        } else if let Ok(tuple) = value.cast_into::<PyTuple>()
36✔
1156
            && (2..=3).contains(&tuple.len())
36✔
1157
        {
1158
            // The decoded value was a 2-item (or 3 with zone ID) array.
1159
            // Check the types of the elements:
1160
            // (int, bytes) -> network
1161
            // (bytes, int) -> interface
1162
            let first_item = tuple.get_item(0)?;
36✔
1163
            let second_item = tuple.get_item(1)?;
36✔
1164
            let zone_id = tuple.get_item(2).ok();
36✔
1165
            let (class, addr_bytes, prefix) = if let Ok(prefix) = first_item.cast::<PyInt>()
36✔
1166
                && let Ok(address) = second_item.cast::<PyBytes>()
12✔
1167
            {
1168
                let mut address_vec: Vec<u8> = address.extract()?;
12✔
1169
                address_vec.resize(16, 0);
12✔
1170
                Ok((
1171
                    IPV6NETWORK_TYPE.get(py)?,
12✔
1172
                    PyBytes::new(py, address_vec.as_slice()),
12✔
1173
                    prefix,
12✔
1174
                ))
1175
            } else if let Ok(address) = first_item.cast_into::<PyBytes>()
24✔
1176
                && let Ok(prefix) = second_item.cast::<PyInt>()
24✔
1177
            {
1178
                Ok((IPV6INTERFACE_TYPE.get(py)?, address, prefix))
24✔
1179
            } else {
NEW
1180
                Err(CBORDecodeValueError::new_err(
×
NEW
1181
                    "error decoding IPv6: invalid types in input array",
×
NEW
1182
                ))
×
NEW
1183
            }?;
×
1184
            let addr_obj = ipv6addr_class.call1((addr_bytes,))?;
36✔
1185

1186
            // Format the zone ID suffix if a zone ID was included
1187
            // (bytes or integer as the last item of a 3-tuple)
1188
            let zone_id_suffix = if let Some(zone_id) = zone_id {
36✔
1189
                if let Ok(zone_id_bytes) = zone_id.cast::<PyBytes>() {
24✔
1190
                    let zone_id_str = String::from_utf8(zone_id_bytes.as_bytes().to_vec())?;
12✔
1191
                    format!("%{zone_id_str}")
12✔
1192
                } else if let Ok(zone_id_int) = zone_id.cast::<PyInt>() {
12✔
1193
                    format!("%{zone_id_int}")
12✔
1194
                } else {
NEW
1195
                    return Err(CBORDecodeValueError::new_err(
×
NEW
1196
                        "error decoding IPv6: zone ID must be an integer or a bytestring",
×
NEW
1197
                    ));
×
1198
                }
1199
            } else {
1200
                String::default()
12✔
1201
            };
1202

1203
            let formatted_addr = format!("{addr_obj}{zone_id_suffix}/{prefix}");
36✔
1204
            class.call1((formatted_addr,))?
36✔
1205
        } else {
NEW
1206
            return Err(CBORDecodeValueError::new_err(
×
NEW
1207
                "error decoding IPv6: input value must be a bytestring or an array of 2 elements",
×
NEW
1208
            ));
×
1209
        };
1210
        Ok(CompleteFrame(addr))
72✔
1211
    }
72✔
1212

1213
    fn decode_epoch_date(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
12✔
1214
        // Semantic tag 100
1215
        let py = value.py();
12✔
1216
        let value = value.extract::<i32>()? + 719163;
12✔
1217
        let date = DATE_FROMORDINAL.get(py)?.call1((value,))?;
12✔
1218
        Ok(CompleteFrame(date))
12✔
1219
    }
12✔
1220

1221
    fn decode_ipaddress(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
84✔
1222
        // Semantic tag 260 (deprecated)
1223
        let py = value.py();
84✔
1224
        let value = value.cast_into::<PyBytes>().map_err(|e| {
84✔
1225
            create_exc_from(
12✔
1226
                py,
12✔
1227
                CBORDecodeValueError::new_err("invalid IP address"),
12✔
1228
                Some(PyErr::from(e)),
12✔
1229
            )
1230
        })?;
12✔
1231
        let addr_obj = match value.len()? {
72✔
1232
            4 | 16 => IPADDRESS_FUNC.get(py)?.call1((value,)),
48✔
1233
            6 => Ok(Bound::new(py, CBORTag::new_internal(260, value.into_any()))?.into_any()), // MAC address
12✔
1234
            length => Err(CBORDecodeValueError::new_err(format!(
12✔
1235
                "invalid IP address length ({length})"
12✔
1236
            ))),
12✔
1237
        }?;
12✔
1238
        Ok(CompleteFrame(addr_obj))
60✔
1239
    }
84✔
1240

1241
    fn decode_ipnetwork<'py>(
84✔
1242
        value: Bound<'py, PyAny>,
84✔
1243
        _immutable: bool,
84✔
1244
    ) -> PyResult<DecoderResult<'py>> {
84✔
1245
        // Semantic tag 261 (deprecated)
1246
        let py = value.py();
84✔
1247
        let value: Bound<'py, PyMapping> = value.cast_into()?;
84✔
1248
        let length = value.len()?;
84✔
1249
        if length != 1 {
84✔
1250
            return Err(CBORDecodeValueError::new_err(format!(
12✔
1251
                "invalid input map length for IP network: {}",
12✔
1252
                length
12✔
1253
            )));
12✔
1254
        }
72✔
1255
        let first_item = value.items()?.get_item(0)?;
72✔
1256
        let mask_length = first_item.get_item(1)?;
72✔
1257
        if !mask_length.is_exact_instance_of::<PyInt>() {
72✔
1258
            return Err(CBORDecodeValueError::new_err(format!(
12✔
1259
                "invalid mask length for IP network: {mask_length}"
12✔
1260
            )));
12✔
1261
        }
60✔
1262

1263
        let addr_obj = match IPNETWORK_FUNC.get(py)?.call1((&first_item,)) {
60✔
1264
            Ok(ip_network) => Ok(ip_network),
48✔
1265
            Err(e) => {
12✔
1266
                // A CompleteFrameError may indicate that the bytestring has host bits set, so try parsing
1267
                // it as an IP interface instead
1268
                if e.is_instance_of::<PyValueError>(py) {
12✔
1269
                    IPINTERFACE_FUNC.get(py)?.call1((first_item,))
12✔
1270
                } else {
NEW
1271
                    Err(e)
×
1272
                }
1273
            }
NEW
1274
        }?;
×
1275
        Ok(CompleteFrame(addr_obj))
60✔
1276
    }
84✔
1277

1278
    fn decode_date_string(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
12✔
1279
        // Semantic tag 1004
1280
        let py = value.py();
12✔
1281
        let date = DATE_FROMISOFORMAT.get(py)?.call1((value,))?;
12✔
1282
        Ok(CompleteFrame(date))
12✔
1283
    }
12✔
1284

1285
    fn decode_complex(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
252✔
1286
        // Semantic tag 43000
1287
        let py = value.py();
252✔
1288
        let tuple = value.cast_into::<PyTuple>().map_err(|e| {
252✔
NEW
1289
            create_exc_from(
×
NEW
1290
                py,
×
NEW
1291
                CBORDecodeValueError::new_err(
×
1292
                    "error decoding complex: input value must be an array",
1293
                ),
NEW
1294
                Some(PyErr::from(e)),
×
1295
            )
NEW
1296
        })?;
×
1297

1298
        if tuple.len() != 2 {
252✔
NEW
1299
            return Err(CBORDecodeValueError::new_err(
×
NEW
1300
                "error decoding complex: array must have exactly two elements",
×
NEW
1301
            ));
×
1302
        }
252✔
1303

1304
        let real: f64 = tuple.get_item(0)?.extract()?;
252✔
1305
        let imag: f64 = tuple.get_item(1)?.extract()?;
252✔
1306
        Ok(CompleteFrame(
252✔
1307
            PyComplex::from_doubles(py, real, imag).into_any(),
252✔
1308
        ))
252✔
1309
    }
252✔
1310

1311
    fn decode_self_describe_cbor(value: Bound<PyAny>, _immutable: bool) -> PyResult<DecoderResult> {
24✔
1312
        // Semantic tag 55799
1313
        Ok(CompleteFrame(value))
24✔
1314
    }
24✔
1315

1316
    fn decode_set<'py>(
408✔
1317
        &mut self,
408✔
1318
        py: Python<'py>,
408✔
1319
        immutable: bool,
408✔
1320
    ) -> PyResult<DecoderResult<'py>> {
408✔
1321
        // Semantic tag 258
1322
        let mut set_or_none = if immutable {
408✔
1323
            None
96✔
1324
        } else {
1325
            Some(PySet::empty(py)?.into_any())
312✔
1326
        };
1327
        let container = set_or_none.as_ref().map(|set| set.clone());
408✔
1328
        let callback = move |item: Bound<'py, PyAny>, _immutable: bool| {
408✔
1329
            let container: Bound<'py, PyAny> = if let Some(set) = set_or_none.take() {
396✔
1330
                set.call_method1(intern!(py, "update"), (item,))?;
300✔
1331
                set.into_any()
300✔
1332
            } else {
1333
                let tuple = item.cast_into::<PyTuple>()?;
96✔
1334
                PyFrozenSet::new(py, tuple)?.into_any()
96✔
1335
            };
1336
            Ok(CompleteFrame(container))
396✔
1337
        };
396✔
1338
        Ok(BeginFrame(Box::new(callback), true, container))
408✔
1339
    }
408✔
1340
}
1341

NEW
1342
#[pymethods]
×
1343
impl CBORDecoder {
1344
    #[new]
1345
    #[pyo3(signature = (
1346
        fp,
1347
        *,
1348
        tag_hook = None,
1349
        object_hook = None,
1350
        semantic_decoders = None,
1351
        str_errors = "strict",
1352
        read_size = 4096,
1353
        max_depth = 1000,
1354
        allow_indefinite = true,
1355
    ))]
1356
    pub fn new(
360✔
1357
        py: Python<'_>,
360✔
1358
        fp: &Bound<'_, PyAny>,
360✔
1359
        tag_hook: Option<&Bound<'_, PyAny>>,
360✔
1360
        object_hook: Option<&Bound<'_, PyAny>>,
360✔
1361
        semantic_decoders: Option<&Bound<'_, PyMapping>>,
360✔
1362
        str_errors: &str,
360✔
1363
        read_size: usize,
360✔
1364
        max_depth: usize,
360✔
1365
        allow_indefinite: bool,
360✔
1366
    ) -> PyResult<Self> {
360✔
1367
        Self::new_internal(
360✔
1368
            py,
360✔
1369
            Some(fp),
360✔
1370
            None,
360✔
1371
            tag_hook,
360✔
1372
            object_hook,
360✔
1373
            semantic_decoders,
360✔
1374
            str_errors,
360✔
1375
            read_size,
360✔
1376
            max_depth,
360✔
1377
            allow_indefinite,
360✔
1378
        )
1379
    }
360✔
1380

1381
    #[getter]
NEW
1382
    fn fp(&self, py: Python<'_>) -> Option<Py<PyAny>> {
×
NEW
1383
        self.fp.as_ref().map(|fp| fp.clone_ref(py))
×
NEW
1384
    }
×
1385

1386
    #[setter]
1387
    fn set_fp(&mut self, fp: &Bound<'_, PyAny>) -> PyResult<()> {
372✔
1388
        let result = fp.call_method0("readable");
372✔
1389
        if let Ok(readable) = &result
372✔
1390
            && readable.is_truthy()?
360✔
1391
        {
1392
            self.fp_is_seekable = fp.call_method0("seekable")?.is_truthy()?;
348✔
1393
            let fp = fp.clone();
348✔
1394
            self.read_method = Some(fp.getattr("read")?.unbind());
348✔
1395
            self.fp = Some(fp.unbind());
348✔
1396
            self.available_bytes = 0;
348✔
1397
            self.read_position = 0;
348✔
1398
            self.buffer = None;
348✔
1399
            Ok(())
348✔
1400
        } else {
1401
            raise_exc_from(
24✔
1402
                fp.py(),
24✔
1403
                PyValueError::new_err("fp must be a readable file-like object"),
24✔
1404
                result.err(),
24✔
1405
            )
1406
        }
1407
    }
372✔
1408

1409
    #[getter]
1410
    fn tag_hook(&self, py: Python<'_>) -> Option<Py<PyAny>> {
12✔
1411
        self.tag_hook
12✔
1412
            .as_ref()
12✔
1413
            .map(|tag_hook| tag_hook.clone_ref(py))
12✔
1414
    }
12✔
1415

1416
    #[setter]
1417
    fn set_tag_hook(&mut self, tag_hook: Option<&Bound<'_, PyAny>>) -> PyResult<()> {
4,320✔
1418
        if let Some(tag_hook) = tag_hook {
4,320✔
1419
            if !tag_hook.is_callable() {
132✔
1420
                return Err(PyErr::new::<PyTypeError, _>(
12✔
1421
                    "tag_hook must be callable or None",
12✔
1422
                ));
12✔
1423
            }
120✔
1424

1425
            self.tag_hook = Some(tag_hook.clone().unbind());
120✔
1426
        } else {
4,188✔
1427
            self.tag_hook = None;
4,188✔
1428
        }
4,188✔
1429
        Ok(())
4,308✔
1430
    }
4,320✔
1431

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

1439
    #[setter]
1440
    fn set_object_hook(&mut self, object_hook: Option<&Bound<'_, PyAny>>) -> PyResult<()> {
4,308✔
1441
        if let Some(object_hook) = object_hook {
4,308✔
1442
            if !object_hook.is_callable() {
48✔
1443
                return Err(PyErr::new::<PyTypeError, _>(
12✔
1444
                    "object_hook must be callable or None",
12✔
1445
                ));
12✔
1446
            }
36✔
1447

1448
            self.object_hook = Some(object_hook.clone().unbind());
36✔
1449
        } else {
4,260✔
1450
            self.object_hook = None;
4,260✔
1451
        }
4,260✔
1452
        Ok(())
4,296✔
1453
    }
4,308✔
1454

1455
    #[getter]
1456
    fn str_errors(&self, py: Python<'_>) -> Py<PyString> {
36✔
1457
        if let Some(str_errors) = self.str_errors.as_ref() {
36✔
1458
            str_errors.clone_ref(py)
24✔
1459
        } else {
1460
            intern!(py, "strict").clone().unbind()
12✔
1461
        }
1462
    }
36✔
1463

1464
    #[setter]
1465
    fn set_str_errors(&mut self, str_errors: &Bound<'_, PyString>) -> PyResult<()> {
4,296✔
1466
        let as_string: &str = str_errors.extract()?;
4,296✔
1467
        self.str_errors = match as_string {
4,296✔
1468
            "strict" => None,
4,296✔
1469
            "ignore" | "replace" | "backslashreplace" | "surrogateescape" => {
48✔
1470
                Some(str_errors.clone().unbind())
36✔
1471
            }
1472
            _ => {
1473
                return Err(PyValueError::new_err(format!(
12✔
1474
                    "invalid str_errors value: '{str_errors}'"
12✔
1475
                )));
12✔
1476
            }
1477
        };
1478
        Ok(())
4,284✔
1479
    }
4,296✔
1480

1481
    /// Read bytes from the data stream.
1482
    ///
1483
    /// :param amount: the number of bytes to read
1484
    #[pyo3(signature = (amount, /))]
1485
    fn read(&mut self, py: Python<'_>, amount: usize) -> PyResult<Vec<u8>> {
3,348✔
1486
        if amount == 0 {
3,348✔
1487
            return Ok(Vec::default());
96✔
1488
        }
3,252✔
1489

1490
        if self.available_bytes == 0 {
3,252✔
1491
            // No buffer
1492
            let (new_bytes, amount_read) = self.read_from_fp(py, amount)?;
72✔
1493
            self.read_position = amount;
12✔
1494
            self.available_bytes = amount_read - amount;
12✔
1495
            let new_buffer = new_bytes.as_bytes()[..amount].to_vec();
12✔
1496
            self.buffer = Some(new_bytes.unbind());
12✔
1497
            Ok(new_buffer)
12✔
1498
        } else if self.available_bytes < amount {
3,180✔
1499
            // Combine the remnants of the partial buffer with new data read from the file
1500
            let needed_bytes = amount - self.available_bytes;
72✔
1501
            let mut concatenated_buffer: Vec<u8> =
72✔
1502
                self.buffer.take().unwrap().as_bytes(py).to_vec();
72✔
1503
            let (new_bytes, amount_read) = self.read_from_fp(py, needed_bytes)?;
72✔
NEW
1504
            concatenated_buffer.extend_from_slice(&new_bytes[..needed_bytes]);
×
NEW
1505
            self.buffer = Some(new_bytes.unbind());
×
NEW
1506
            self.available_bytes = amount_read - needed_bytes;
×
NEW
1507
            self.read_position = needed_bytes;
×
NEW
1508
            Ok(concatenated_buffer)
×
1509
        } else {
1510
            // Return a slice from the existing bytes object
1511
            let vec = self.buffer.as_ref().unwrap().as_bytes(py)
3,108✔
1512
                [self.read_position..self.read_position + amount]
3,108✔
1513
                .to_vec();
3,108✔
1514
            self.available_bytes -= amount;
3,108✔
1515
            self.read_position += amount;
3,108✔
1516
            Ok(vec)
3,108✔
1517
        }
1518
    }
3,348✔
1519

1520
    /// Decode the next value from the stream.
1521
    ///
1522
    /// :param immutable: if :data:`True`, decode the next item as an immutable type
1523
    ///     (e.g. :class:`tuple` instead of a :class:`list`), if possible
1524
    /// :return: the decoded object
1525
    /// :raises CBORDecodeError: if there is any problem decoding the stream
1526
    #[pyo3(signature = (*, immutable = false))]
1527
    pub fn decode<'py>(&mut self, py: Python<'py>, immutable: bool) -> PyResult<Bound<'py, PyAny>> {
4,212✔
1528
        let mut frames: Vec<StackFrame> = Vec::new();
4,212✔
1529

1530
        fn add_frame<'a>(
19,236✔
1531
            frames: &mut Vec<StackFrame<'a>>,
19,236✔
1532
            max_depth: usize,
19,236✔
1533
            frame: StackFrame<'a>,
19,236✔
1534
        ) -> PyResult<()> {
19,236✔
1535
            if frames.len() == max_depth {
19,236✔
1536
                return Err(CBORDecodeError::new_err(format!(
24✔
1537
                    "maximum container nesting depth ({max_depth}) exceeded",
24✔
1538
                )));
24✔
1539
            }
19,212✔
1540

1541
            frames.push(frame);
19,212✔
1542
            Ok(())
19,212✔
1543
        }
19,236✔
1544

1545
        let mut shareables: Vec<Option<Bound<'py, PyAny>>> = Vec::new();
4,212✔
1546
        let mut string_namespaces: Vec<Vec<Bound<'py, PyAny>>> = Vec::new();
4,212✔
1547
        let mut value: Option<Bound<'py, PyAny>> = None;
4,212✔
1548
        let mut current_immutable: bool = immutable;
4,212✔
1549
        loop {
1550
            let result: PyResult<DecoderResult<'py>> = if let Some(previous_value) = value.take() {
42,120✔
1551
                // Call the decoder callback of the last frame
1552
                let frame = frames.last_mut().unwrap();
12,648✔
1553
                if let Some(decoder_callback) = frame.decoder_callback.as_mut() {
12,648✔
1554
                    decoder_callback(previous_value, frame.immutable)
12,564✔
1555
                } else if frame.contains_string_namespace {
84✔
1556
                    string_namespaces
12✔
1557
                        .pop()
12✔
1558
                        .expect("no string namespaces to pop from");
12✔
1559
                    Ok(CompleteFrame(previous_value))
12✔
1560
                } else if let Some(shareable_index) = frame.shareable_index {
72✔
1561
                    shareables[shareable_index].get_or_insert_with(|| previous_value.clone());
72✔
1562
                    Ok(CompleteFrame(previous_value))
72✔
1563
                } else {
NEW
1564
                    panic!("no decoder callback, shareable index or string namespace");
×
1565
                }
1566
            } else {
1567
                let (major_type, subtype) = self.read_major_and_subtype(py)?;
29,472✔
1568
                match major_type {
29,448✔
1569
                    0 => self.decode_uint(py, subtype),
3,300✔
1570
                    1 => self.decode_negint(py, subtype),
768✔
1571
                    2 => self.decode_bytestring(py, subtype),
1,128✔
1572
                    3 => self.decode_string(py, subtype),
2,160✔
1573
                    4 => self.decode_array(py, subtype, current_immutable),
14,580✔
1574
                    5 => self.decode_map(py, subtype, current_immutable),
1,740✔
1575
                    6 => self.decode_semantic(py, subtype, current_immutable),
3,384✔
1576
                    7 => self.decode_special(py, subtype),
2,388✔
NEW
1577
                    _ => Err(CBORDecodeError::new_err(format!(
×
NEW
1578
                        "invalid major type: {major_type}"
×
NEW
1579
                    ))),
×
1580
                }
1581
            };
1582

1583
            match result {
41,544✔
1584
                Ok(BeginFrame(callback, requested_immutable, container)) => {
18,960✔
1585
                    if let Some(frame) = frames.last_mut()
18,960✔
1586
                        && let Some(container) = container
16,428✔
1587
                        && let Some(shareable_index) = frame.shareable_index
13,092✔
1588
                    {
156✔
1589
                        frames.pop();
156✔
1590
                        shareables[shareable_index] = Some(container.clone());
156✔
1591
                    }
18,804✔
1592
                    current_immutable = current_immutable || requested_immutable;
18,960✔
1593
                    add_frame(
18,960✔
1594
                        &mut frames,
18,960✔
1595
                        self.max_depth,
18,960✔
1596
                        StackFrame {
18,960✔
1597
                            immutable: current_immutable,
18,960✔
1598
                            decoder_callback: Some(callback),
18,960✔
1599
                            shareable_index: None,
18,960✔
1600
                            contains_string_namespace: false,
18,960✔
1601
                        },
18,960✔
1602
                    )?;
24✔
1603
                }
1604
                Ok(ContinueFrame(require_immutable)) => {
6,048✔
1605
                    // If require_immutable is true, the next value must be immutable
1606
                    // Otherwise, restore the immutable flag to the previous value
1607
                    current_immutable = if frames.len() >= 2 {
6,048✔
1608
                        frames.get(frames.len() - 2).unwrap().immutable
3,120✔
1609
                    } else {
1610
                        immutable
2,928✔
1611
                    } || require_immutable;
3,384✔
1612
                }
1613
                Ok(CompleteFrame(new_value)) => {
6,120✔
1614
                    frames
6,120✔
1615
                        .pop()
6,120✔
1616
                        .expect("received frame completion but there are no frames on the stack");
6,120✔
1617
                    current_immutable = frames.last().map_or(immutable, |frame| frame.immutable);
6,120✔
1618
                    value = Some(new_value);
6,120✔
1619
                }
1620
                Ok(Value(new_value)) => {
6,876✔
1621
                    value = Some(new_value);
6,876✔
1622
                }
6,876✔
1623
                Ok(StringNamespace) => {
1624
                    add_frame(
36✔
1625
                        &mut frames,
36✔
1626
                        self.max_depth,
36✔
1627
                        StackFrame {
36✔
1628
                            immutable: current_immutable,
36✔
1629
                            decoder_callback: None,
36✔
1630
                            shareable_index: None,
36✔
1631
                            contains_string_namespace: true,
36✔
1632
                        },
36✔
NEW
1633
                    )?;
×
1634
                    string_namespaces.push(Vec::new());
36✔
1635
                }
1636
                Ok(StringValue(string, length)) => {
2,988✔
1637
                    // Conditionally add the string to the innermost string namespace
1638
                    if let Some(namespace) = string_namespaces.last_mut() {
2,988✔
1639
                        if match namespace.len() {
48✔
1640
                            0..24 => length >= 3,
48✔
NEW
1641
                            24..256 => length >= 4,
×
NEW
1642
                            256..65536 => length >= 5,
×
NEW
1643
                            65536..4294967296 => length >= 6,
×
NEW
1644
                            _ => length >= 11,
×
1645
                        } {
48✔
1646
                            namespace.push(string.clone());
48✔
1647
                        }
48✔
1648
                    }
2,940✔
1649
                    value = Some(string);
2,988✔
1650
                }
1651
                Ok(StringReference(index)) => {
96✔
1652
                    frames
96✔
1653
                        .pop()
96✔
1654
                        .expect("  received string reference but there are no frames on the stack");
96✔
1655
                    if let Some(namespace) = string_namespaces.last() {
96✔
1656
                        if let Some(string) = namespace.get(index) {
84✔
1657
                            value = Some(string.clone());
72✔
1658
                        } else {
72✔
1659
                            return Err(CBORDecodeValueError::new_err(format!(
12✔
1660
                                "string reference {index} not found"
12✔
1661
                            )));
12✔
1662
                        }
1663
                    } else {
1664
                        return Err(CBORDecodeValueError::new_err(
12✔
1665
                            "string reference outside of namespace",
12✔
1666
                        ));
12✔
1667
                    }
1668
                    current_immutable = frames
72✔
1669
                        .last()
72✔
1670
                        .map_or(current_immutable, |frame| frame.immutable);
72✔
1671
                }
1672
                Ok(Shareable) => {
1673
                    add_frame(
240✔
1674
                        &mut frames,
240✔
1675
                        self.max_depth,
240✔
1676
                        StackFrame {
240✔
1677
                            immutable: current_immutable,
240✔
1678
                            decoder_callback: None,
240✔
1679
                            shareable_index: Some(shareables.len()),
240✔
1680
                            contains_string_namespace: false,
240✔
1681
                        },
240✔
NEW
1682
                    )?;
×
1683
                    shareables.push(None);
240✔
1684
                }
1685
                Ok(SharedReference(index)) => {
180✔
1686
                    frames
180✔
1687
                        .pop()
180✔
1688
                        .expect("received shared reference but there are no frames on the stack");
180✔
1689
                    value = match shareables.get(index) {
180✔
1690
                        Some(Some(value)) => Some(value.clone()),
144✔
1691
                        Some(None) => {
1692
                            return Err(CBORDecodeError::new_err(format!(
12✔
1693
                                "shared value {index} has not been initialized"
12✔
1694
                            )));
12✔
1695
                        }
1696
                        None => {
1697
                            return Err(CBORDecodeError::new_err(format!(
24✔
1698
                                "shared reference {index} not found"
24✔
1699
                            )));
24✔
1700
                        }
1701
                    };
1702
                    current_immutable = frames
144✔
1703
                        .last()
144✔
1704
                        .map_or(current_immutable, |frame| frame.immutable);
144✔
1705
                }
1706
                Err(err) => {
552✔
1707
                    // If an Exception was raised, wrap it in a CBORDecodeError
1708
                    // If a ValueError was raised, wrap it in a CBORDecodeValueError
1709
                    return if err.is_instance_of::<CBORDecodeError>(py) {
552✔
1710
                        Err(err)
504✔
1711
                    } else if err.is_instance_of::<PyValueError>(py) {
48✔
1712
                        Err(create_exc_from(
10✔
1713
                            py,
10✔
1714
                            CBORDecodeValueError::new_err(err.to_string()),
10✔
1715
                            Some(err),
10✔
1716
                        ))
10✔
1717
                    } else if err.is_instance_of::<PyException>(py) {
38✔
1718
                        Err(create_exc_from(
38✔
1719
                            py,
38✔
1720
                            CBORDecodeError::new_err(err.to_string()),
38✔
1721
                            Some(err),
38✔
1722
                        ))
38✔
1723
                    } else {
NEW
1724
                        Err(err)
×
1725
                    };
1726
                }
1727
            }
1728

1729
            if frames.is_empty() {
41,460✔
1730
                // If fp was seekable and excess data has been read, empty the buffer and
1731
                // rewind the file
1732
                if self.available_bytes > 0
3,552✔
1733
                    && let Some(fp) = &self.fp
24✔
1734
                {
1735
                    let offset = -(self.available_bytes as isize);
24✔
1736
                    fp.call_method1(py, intern!(py, "seek"), (offset, SEEK_CUR))?;
24✔
1737
                    self.buffer = None;
24✔
1738
                    self.available_bytes = 0;
24✔
1739
                    self.read_position = 0;
24✔
1740
                }
3,528✔
1741
                return Ok(value.expect("stack is empty but final return value is missing"));
3,552✔
1742
            }
37,908✔
1743
        }
1744
    }
4,212✔
1745
}
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