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