cbor_data/builder/
mod.rs

1use crate::CborOwned;
2use std::marker::PhantomData;
3
4mod encoder;
5mod low_level;
6mod writer;
7mod writers;
8
9pub use encoder::Encoder;
10use low_level::*;
11pub use writer::Writer;
12pub use writers::{ArrayWriter, DictWriter, KeyBuilder, SingleBuilder, SingleResult};
13
14/// Marker trait to distinguish a builder that emits an owned value from one that appends to a vector
15pub trait CborOutput {
16    type Output;
17    fn output(bytes: &[u8]) -> Self::Output;
18}
19/// Marker type for builders that emit an owned value
20pub struct WithOutput;
21impl CborOutput for WithOutput {
22    type Output = CborOwned;
23    fn output(bytes: &[u8]) -> Self::Output {
24        CborOwned::unchecked(bytes)
25    }
26}
27/// Marker type for builders that only append to a provided vector
28pub struct NoOutput;
29impl CborOutput for NoOutput {
30    type Output = ();
31    fn output(_bytes: &[u8]) -> Self::Output {}
32}
33
34/// Builder for a single CBOR value.
35///
36/// [`CborOwned::canonical`](struct.CborOwned.html#method.canonical) uses the default configuration,
37/// which implies writing bytes into a fresh `Vec<u8>` and finally moving them into a SmallVec. You
38/// can minimise allocations by reusing the build buffer, and you can influence whether definite or
39/// indefinite length encoding is used for arrays and dictionaries.
40///
41/// # Example
42///
43/// ```rust
44/// use cbor_data::{Cbor, CborBuilder, Writer};
45///
46/// // this could come from a thread-local in real code:
47/// let mut build_buffer = Vec::new();
48/// // buffer will be cleared before use
49/// build_buffer.extend_from_slice(b"some garbage");
50///
51/// let bytes_from_elsewhere = [0x82, 1, 2];
52/// assert_eq!(Cbor::checked(&bytes_from_elsewhere).unwrap().to_string(), "[1, 2]");
53///
54/// let cbor = CborBuilder::with_scratch_space(&mut build_buffer)
55///     .with_max_definite_size(Some(1))
56///     .write_canonical(bytes_from_elsewhere.as_ref())
57///     .unwrap();
58///
59/// // now it is using indefinite-length encoding, since the array has more than 1 item
60/// assert_eq!(cbor.to_string(), "[_ 1, 2]");
61/// assert_eq!(cbor.as_slice(), [0x9f, 1, 2, 0xff]);
62/// ```
63pub struct CborBuilder<'a, O: CborOutput> {
64    bytes: Bytes<'a>,
65    max_definite: Option<u64>,
66    ph: PhantomData<O>,
67}
68
69impl Default for CborBuilder<'static, WithOutput> {
70    fn default() -> Self {
71        Self::new()
72    }
73}
74
75impl<'a> CborBuilder<'a, WithOutput> {
76    /// Create a builder that writes into its own fresh vector.
77    pub fn new() -> Self {
78        Self {
79            bytes: Bytes::Owned(Vec::new()),
80            max_definite: Some(255),
81            ph: PhantomData,
82        }
83    }
84
85    /// Create a builder that clears the given vector and writes into it.
86    ///
87    /// You can use this to reuse a scratch space across multiple values being built, e.g. by
88    /// keeping the same vector in a thread-local variable.
89    pub fn with_scratch_space(v: &'a mut Vec<u8>) -> Self {
90        v.clear();
91        Self {
92            bytes: Bytes::Borrowed(v),
93            max_definite: Some(255),
94            ph: PhantomData,
95        }
96    }
97}
98
99impl<'a> CborBuilder<'a, NoOutput> {
100    /// Append the CBOR bytes to the given vector and do not return a separate output value.
101    ///
102    /// ```
103    /// # use cbor_data::{CborBuilder, Writer};
104    /// let mut v = Vec::new();
105    /// let result: () = CborBuilder::append_to(&mut v).write_pos(12, None);
106    ///
107    /// assert_eq!(v, vec![12u8])
108    /// ```
109    pub fn append_to(v: &'a mut Vec<u8>) -> Self {
110        Self {
111            bytes: Bytes::Borrowed(v),
112            max_definite: Some(255),
113            ph: PhantomData,
114        }
115    }
116}
117
118impl<'a, O: CborOutput> CborBuilder<'a, O> {
119    /// Configure the limit above which indefinite size encoding will be used.
120    ///
121    /// The default is 255, which is the largest size up to which definite size is at least as
122    /// compact as indefinite size. Set to 23 to avoid moving bytes around when finishing the array.
123    /// Set to `None` to always use indefinite size encoding.
124    pub fn with_max_definite_size(self, max_definite: Option<u64>) -> Self {
125        Self {
126            bytes: self.bytes,
127            max_definite,
128            ph: PhantomData,
129        }
130    }
131}
132
133impl<'a, O: CborOutput> Writer for CborBuilder<'a, O> {
134    type Output = O::Output;
135
136    fn bytes<T>(&mut self, f: impl FnOnce(&mut Vec<u8>) -> T) -> T {
137        f(self.bytes.as_mut())
138    }
139
140    fn into_output(self) -> Self::Output {
141        O::output(self.bytes.as_slice())
142    }
143
144    fn max_definite(&self) -> Option<u64> {
145        self.max_definite
146    }
147
148    fn set_max_definite(&mut self, max: Option<u64>) {
149        self.max_definite = max;
150    }
151}