pub struct DecodeOptions { /* private fields */ }Expand description
Configuration for CBOR decoding.
DecodeOptions controls the input format (Binary,
Hex, or Diagnostic) and the
limits the decoder enforces against hostile or malformed input.
Construct it with DecodeOptions::new (or Default), adjust
settings with the builder methods, and call decode
or read_from for a single item, or
sequence_decoder / sequence_reader
for a CBOR sequence.
The convenience methods on Value (decode,
decode_hex, read_from,
read_hex_from) all forward to a default
DecodeOptions. Use this type directly when you need to decode
diagnostic notation, iterate a sequence, relax a limit for a known
input, or tighten one for untrusted input.
§Options
| Option | Default | Purpose |
|---|---|---|
format | Binary | Input syntax: binary, hex text, or diagnostic notation. |
recursion_limit | 200 | Maximum nesting depth of arrays, maps, and tags. |
length_limit | 1,000,000,000 | Maximum declared element count of a single array, map, byte string, or text string. |
oom_mitigation | 100,000,000 | Byte budget for speculative pre-allocation. |
strictness | Strictness::STRICT | Which non-deterministic encodings the decoder accepts and normalizes. |
§recursion_limit
Each array, map, or tag consumes one unit of recursion budget for
its contents. Exceeding the limit returns Error::NestingTooDeep.
The limit protects against stack overflow on adversarial input and
should be well below the stack a thread has available.
§length_limit
Applies to the length field in the CBOR head of arrays, maps, byte
strings, and text strings. It caps the declared size before any
bytes are read, so a malicious header claiming a petabyte-long
string is rejected immediately with Error::LengthTooLarge. The
limit does not restrict total input size; a valid document may
contain many items each up to the limit.
§oom_mitigation
CBOR encodes lengths in the head, so a decoder is tempted to
pre-allocate a Vec of the declared capacity. On hostile input
that is a trivial amplification attack: a few bytes on the wire
reserve gigabytes of memory. oom_mitigation is a byte budget,
shared across the current decode, that caps the total amount of
speculative capacity the decoder may reserve for array backing
storage. Once the budget is exhausted, further arrays start empty
and grow on demand. Decoding still succeeds if the input is
well-formed; only the up-front reservation is bounded.
The budget is consumed, not refilled: a deeply nested structure with many small arrays can drain it early and decode the tail with zero pre-allocation. That is the intended behavior.
§Examples
Decode binary CBOR with default limits:
use cbor_core::DecodeOptions;
let v = DecodeOptions::new().decode(&[0x18, 42]).unwrap();
assert_eq!(v.to_u32().unwrap(), 42);Switch the input format to hex text or diagnostic notation:
use cbor_core::{DecodeOptions, Format};
let v = DecodeOptions::new().format(Format::Hex).decode("182a").unwrap();
assert_eq!(v.to_u32().unwrap(), 42);
let v = DecodeOptions::new().format(Format::Diagnostic).decode("42").unwrap();
assert_eq!(v.to_u32().unwrap(), 42);Tighten limits for input from an untrusted source:
use cbor_core::DecodeOptions;
let strict = DecodeOptions::new()
.recursion_limit(16)
.length_limit(4096)
.oom_mitigation(64 * 1024);
assert!(strict.decode(&[0x18, 42]).is_ok());Implementations§
Source§impl DecodeOptions
impl DecodeOptions
Sourcepub const fn new() -> Self
pub const fn new() -> Self
Create a new set of options with the crate defaults.
use cbor_core::DecodeOptions;
let opts = DecodeOptions::new();
let v = opts.decode(&[0x18, 42]).unwrap();
assert_eq!(v.to_u32().unwrap(), 42);Sourcepub const fn format(self, format: Format) -> Self
pub const fn format(self, format: Format) -> Self
Select the input format: Binary,
Hex, or Diagnostic.
Default: Format::Binary.
use cbor_core::{DecodeOptions, Format};
let hex = DecodeOptions::new().format(Format::Hex).decode("182a").unwrap();
let bin = DecodeOptions::new().decode(&[0x18, 0x2a]).unwrap();
assert_eq!(hex, bin);
let v = DecodeOptions::new().format(Format::Diagnostic).decode("42").unwrap();
assert_eq!(v.to_u32().unwrap(), 42);Sourcepub const fn recursion_limit(self, limit: u16) -> Self
pub const fn recursion_limit(self, limit: u16) -> Self
Set the maximum nesting depth of arrays, maps, and tags.
Default: 200. Input that exceeds the limit returns
Error::NestingTooDeep.
use cbor_core::{DecodeOptions, Error};
// Two nested one-element arrays: 0x81 0x81 0x00
let err = DecodeOptions::new()
.recursion_limit(1)
.decode(&[0x81, 0x81, 0x00])
.unwrap_err();
assert_eq!(err, Error::NestingTooDeep);Sourcepub const fn length_limit(self, limit: u64) -> Self
pub const fn length_limit(self, limit: u64) -> Self
Set the maximum declared length for byte strings, text strings, arrays, and maps.
Default: 1,000,000,000. Checked against the length field in the
CBOR head before any bytes are consumed; an oversized declaration
returns Error::LengthTooLarge.
use cbor_core::{DecodeOptions, Error};
// A five-byte text string: 0x65 'h' 'e' 'l' 'l' 'o'
let err = DecodeOptions::new()
.length_limit(4)
.decode(b"\x65hello")
.unwrap_err();
assert_eq!(err, Error::LengthTooLarge);Sourcepub const fn oom_mitigation(self, bytes: usize) -> Self
pub const fn oom_mitigation(self, bytes: usize) -> Self
Set the byte budget for speculative pre-allocation of array backing storage.
Default: 100,000,000. Lower values trade a small amount of decoding throughput for stronger resistance to memory-amplification attacks. Valid input decodes regardless; only the up-front reservation is bounded.
use cbor_core::DecodeOptions;
// A two-element array: 0x82 0x01 0x02
let v = DecodeOptions::new()
.oom_mitigation(0)
.decode(&[0x82, 0x01, 0x02])
.unwrap();
assert_eq!(v.len(), Some(2));Sourcepub const fn strictness(self, strictness: Strictness) -> Self
pub const fn strictness(self, strictness: Strictness) -> Self
Configure which non-deterministic encodings the decoder will
accept. Default: Strictness::STRICT, which rejects every
deviation with Error::NonDeterministic.
Pass Strictness::LENIENT to accept all known deviations, or
build a custom mix of allow_* fields. Tolerated input is
normalized while decoding, so the resulting Value is
canonical and re-encoding it produces CBOR::Core compliant
bytes.
use cbor_core::{DecodeOptions, Strictness, Value};
// 255 wrongly encoded with a two byte argument; normalized on read.
let v = DecodeOptions::new()
.strictness(Strictness::LENIENT)
.decode(&[0x19, 0x00, 0xff])
.unwrap();
assert_eq!(v, Value::from(255));
assert_eq!(v.encode(), vec![0x18, 0xff]);Sourcepub fn decode<'a, T>(&self, bytes: &'a T) -> Result<Value<'a>>
pub fn decode<'a, T>(&self, bytes: &'a T) -> Result<Value<'a>>
Decode exactly one CBOR data item from an in-memory buffer.
Takes the input by reference: &[u8], &[u8; N], &Vec<u8>,
&str, &String, etc. all work via T: AsRef<[u8]> + ?Sized.
In Format::Binary, decoded text and byte strings borrow
directly from the input slice and the returned Value
inherits that lifetime; in Format::Hex and
Format::Diagnostic the result is owned.
The input must contain exactly one value: any bytes
remaining after a successful decode cause
Error::InvalidFormat. In Format::Diagnostic mode
trailing whitespace and comments are accepted, but nothing
else. Use sequence_decoder when the input is a CBOR
sequence.
An empty buffer (and, for diagnostic notation, one containing
only whitespace and comments) returns Error::UnexpectedEof.
A partial value returns Error::UnexpectedEof too.
use cbor_core::{DecodeOptions, Format};
let v = DecodeOptions::new().decode(&[0x18, 42]).unwrap();
assert_eq!(v.to_u32().unwrap(), 42);
let v = DecodeOptions::new().format(Format::Hex).decode("182a").unwrap();
assert_eq!(v.to_u32().unwrap(), 42);
let v = DecodeOptions::new()
.format(Format::Diagnostic)
.decode("42 / trailing comment is fine /")
.unwrap();
assert_eq!(v.to_u32().unwrap(), 42);Sourcepub fn decode_owned<'a>(&self, bytes: impl AsRef<[u8]>) -> Result<Value<'a>>
pub fn decode_owned<'a>(&self, bytes: impl AsRef<[u8]>) -> Result<Value<'a>>
Decode exactly one CBOR data item into an owned Value.
Takes the input by value: Vec<u8>, &[u8], &str, and
anything else that implements AsRef<[u8]> all work. Unlike
decode, the result never borrows from the
input regardless of format: text and byte strings are always
copied into owned allocations. The returned value can be held
as Value<'static> and stored or sent across threads without
any lifetime constraint.
Use this when the input is short-lived (a temporary buffer, a
Vec returned from a function, etc.) and the decoded value
needs to outlive it. When the input already lives long enough,
decode avoids the copies.
The input must contain exactly one value: any bytes
remaining after a successful decode cause
Error::InvalidFormat. In Format::Diagnostic mode
trailing whitespace and comments are accepted, but nothing
else. Use sequence_decoder when
the input is a CBOR sequence.
An empty buffer (and, for diagnostic notation, one containing
only whitespace and comments) returns Error::UnexpectedEof.
A partial value returns Error::UnexpectedEof too.
use cbor_core::{DecodeOptions, Format, Value};
// Decode from a short-lived Vec without worrying about lifetimes.
let bytes: Vec<u8> = vec![0x18, 42];
let v: Value<'static> = DecodeOptions::new().decode_owned(bytes).unwrap();
assert_eq!(v.to_u32().unwrap(), 42);
// Hex and diagnostic formats work the same way.
let v: Value<'static> = DecodeOptions::new()
.format(Format::Hex)
.decode_owned("182a")
.unwrap();
assert_eq!(v.to_u32().unwrap(), 42);Sourcepub fn read_from<'a>(&self, reader: impl Read) -> IoResult<Value<'a>>
pub fn read_from<'a>(&self, reader: impl Read) -> IoResult<Value<'a>>
Read a single CBOR data item from a stream.
Designed to be called repeatedly to pull successive elements of a CBOR sequence:
- In
Format::BinaryandFormat::Hexthe reader is consumed only up to the end of the item; any bytes after remain in the stream. - In
Format::Diagnostictrailing whitespace and comments are consumed up to either end of stream or a top-level separator comma (the comma is also consumed). Anything else after the value fails withError::InvalidFormat.
Bytes are read into an internal buffer, so the result is
always owned and can be held as Value<'static>. For
zero-copy decoding from a byte slice, use
decode instead.
I/O failures are returned as IoError::Io;
malformed or oversized input as IoError::Data.
use cbor_core::{DecodeOptions, Format};
let mut bytes: &[u8] = &[0x18, 42];
let v = DecodeOptions::new().read_from(&mut bytes).unwrap();
assert_eq!(v.to_u32().unwrap(), 42);
let mut hex: &[u8] = b"182a";
let v = DecodeOptions::new().format(Format::Hex).read_from(&mut hex).unwrap();
assert_eq!(v.to_u32().unwrap(), 42);
// Diagnostic: repeated read_from pulls successive sequence items.
let mut diag: &[u8] = b"1, 2, 3";
let opts = DecodeOptions::new().format(Format::Diagnostic);
let a = opts.read_from(&mut diag).unwrap();
let b = opts.read_from(&mut diag).unwrap();
let c = opts.read_from(&mut diag).unwrap();
assert_eq!(a.to_u32().unwrap(), 1);
assert_eq!(b.to_u32().unwrap(), 2);
assert_eq!(c.to_u32().unwrap(), 3);Sourcepub fn sequence_decoder<'a, T>(&self, input: &'a T) -> SequenceDecoder<'a> ⓘ
pub fn sequence_decoder<'a, T>(&self, input: &'a T) -> SequenceDecoder<'a> ⓘ
Create an iterator over a CBOR sequence stored in memory.
The returned SequenceDecoder yields each successive item of the
sequence as Result<Value<'a>>, where 'a is the lifetime of
the input slice. In binary format, items borrow text and byte
strings from the input; in hex and diagnostic format the items
are owned. The iterator captures a snapshot of these options;
subsequent changes to self do not affect it.
use cbor_core::{DecodeOptions, Format};
let opts = DecodeOptions::new().format(Format::Diagnostic);
let items: Vec<_> = opts
.sequence_decoder(b"1, 2, 3,")
.collect::<Result<_, _>>()
.unwrap();
assert_eq!(items.len(), 3);Sourcepub fn sequence_reader<R: Read>(&self, reader: R) -> SequenceReader<R> ⓘ
pub fn sequence_reader<R: Read>(&self, reader: R) -> SequenceReader<R> ⓘ
Create an iterator over a CBOR sequence read from a stream.
The returned SequenceReader yields each successive item as
IoResult<Value<'static>>. None indicates a clean end
between items; a truncated item produces Some(Err(_)). Items
are always owned (the bytes are read into an internal
buffer); for zero-copy iteration use
sequence_decoder on a byte slice
instead.
use cbor_core::DecodeOptions;
// Binary CBOR sequence: three one-byte items 0x01 0x02 0x03.
let bytes: &[u8] = &[0x01, 0x02, 0x03];
let items: Vec<_> = DecodeOptions::new()
.sequence_reader(bytes)
.collect::<Result<_, _>>()
.unwrap();
assert_eq!(items.len(), 3);Trait Implementations§
Source§impl Clone for DecodeOptions
impl Clone for DecodeOptions
Source§fn clone(&self) -> DecodeOptions
fn clone(&self) -> DecodeOptions
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more