blob/
lib.rs

1//! Blob
2//!
3//! This crate provides a dedicated `Blob` structure for use in storing,
4//! encoding and decoding to/from base-64, with support for type-level encoding
5//! configurations suitable for url-safe base-64.
6//!
7//! When serializing, it will encode the binary data as base-64, and when deserializing it
8//! can either read and decode a base-64 encoded string or a raw sequence of bytes.
9//!
10//! Example using `FromStr::from_str`:
11//!
12//! ```
13//! extern crate blob;
14//!
15//! use std::str::FromStr;
16//!
17//! use blob::Blob;
18//!
19//! fn main() {
20//!     let my_blob: Blob = Blob::from_str("AQIDBAU=").unwrap();
21//!
22//!     assert_eq!(my_blob, [1, 2, 3, 4, 5]);
23//! }
24//! ```
25
26#![deny(missing_docs)]
27
28extern crate base64;
29extern crate serde;
30
31use std::borrow::{Borrow, BorrowMut};
32use std::fmt::{self, Display};
33use std::hash::{Hash, Hasher};
34use std::io::{self, Write};
35use std::iter::{Extend, FromIterator, IntoIterator};
36use std::marker::PhantomData;
37use std::ops::{Deref, DerefMut};
38use std::slice::{Iter, IterMut};
39use std::str::FromStr;
40use std::vec::IntoIter;
41
42/// Trait used for statically typed Blob encoding configs
43pub trait Config: Send + Sync {
44    /// Associated base-64 config
45    const CONFIG: base64::Config;
46}
47
48macro_rules! impl_configs {
49    ($($(#[$($attrs:tt)*])* $name:ident: $config:ident,)*) => {
50        $(
51            $(#[$($attrs)*])*
52            pub enum $name {}
53
54            impl Config for $name {
55                const CONFIG: base64::Config = base64::$config;
56            }
57        )*
58    }
59}
60
61impl_configs! {
62    /// As per `crypt(3)` requirements
63    Crypt: CRYPT,
64
65    /// Standard character set with padding.
66    Standard: STANDARD,
67
68    /// Standard character set without padding.
69    StandardNoPad: STANDARD_NO_PAD,
70
71    /// URL-safe character set with padding
72    UrlSafe: URL_SAFE,
73
74    /// URL-safe character set without padding
75    UrlSafeNoPad: URL_SAFE_NO_PAD,
76}
77
78/// Blob structure containing binary data
79///
80/// Interally, the blob is stored as a plain `Vec<u8>`, and some
81/// methods are exposed from that. If you need full access to the
82/// underlying `Vec`, use `borrow()` or `borrow_mut()`
83pub struct Blob<C: Config = Standard> {
84    data: Vec<u8>,
85    _config: PhantomData<C>,
86}
87
88impl<C: Config> Default for Blob<C> {
89    #[inline]
90    fn default() -> Self {
91        Blob {
92            data: Vec::new(),
93            _config: PhantomData,
94        }
95    }
96}
97
98impl<C: Config> Blob<C> {
99    /// Create a new empty `Blob`
100    #[inline]
101    pub fn new() -> Blob<C> {
102        Blob::default()
103    }
104
105    /// Create a `Blob` from an underlying `Vec`
106    #[inline]
107    pub fn from_vec(vec: Vec<u8>) -> Blob<C> {
108        Blob {
109            data: vec,
110            _config: PhantomData,
111        }
112    }
113
114    /// Create a new `Blob` with the given capacity
115    #[inline]
116    pub fn with_capacity(capacity: usize) -> Blob<C> {
117        Blob::from_vec(Vec::with_capacity(capacity))
118    }
119
120    /// Returns the number of bytes the `Blob` can hold without reallocating.
121    #[inline]
122    pub fn capacity(&self) -> usize {
123        self.data.capacity()
124    }
125
126    /// Reserves capacity for at least additional more bytes to be inserted in the given `Blob`
127    #[inline]
128    pub fn reserve(&mut self, additional: usize) {
129        self.data.reserve(additional)
130    }
131
132    /// Use a different encoding configuration for the `Blob`
133    #[inline(always)]
134    pub fn with_config<E: Config>(self) -> Blob<E> {
135        Blob {
136            data: self.data,
137            _config: PhantomData,
138        }
139    }
140
141    /// Encode the `Blob` to a base-64 string
142    #[inline]
143    pub fn encode_base64(&self) -> String {
144        base64::encode_config(&self.data, C::CONFIG)
145    }
146
147    /// Encodes the `Blob` as base-64 to an `io::Writer`, avoiding intermediate allocations
148    pub fn encode_to<W: io::Write>(&self, mut writer: W) -> io::Result<()> {
149        let mut encoder = base64::write::EncoderWriter::new(&mut writer, C::CONFIG);
150
151        encoder.write_all(&self.data)
152    }
153
154    /// Decode base-64 encoded data into a `Blob`
155    pub fn decode_base64<T>(encoded: T) -> Result<Blob<C>, base64::DecodeError>
156    where
157        T: AsRef<[u8]>,
158    {
159        // perform as_ref here to only monomorphize the decoder once
160        base64::decode_config(encoded.as_ref(), C::CONFIG).map(Blob::from_vec)
161    }
162
163    /// Decodes some base-64 data and appends it to the `Blob`
164    #[inline]
165    pub fn append_base64<T>(&mut self, encoded: T) -> Result<(), base64::DecodeError>
166    where
167        T: AsRef<[u8]>,
168    {
169        // perform as_ref here to only monomorphize the decoder once
170        base64::decode_config_buf(encoded.as_ref(), C::CONFIG, &mut self.data)
171    }
172
173    /// Consume self and return the inner `Vec<u8>`
174    #[inline]
175    pub fn into_vec(self) -> Vec<u8> {
176        self.data
177    }
178}
179
180impl<C: Config> FromStr for Blob<C> {
181    type Err = base64::DecodeError;
182
183    #[inline]
184    fn from_str(s: &str) -> Result<Self, Self::Err> {
185        Blob::decode_base64(s)
186    }
187}
188
189impl<C: Config> Clone for Blob<C> {
190    #[inline]
191    fn clone(&self) -> Blob<C> {
192        Blob {
193            data: self.data.clone(),
194            _config: PhantomData,
195        }
196    }
197}
198
199impl<C: Config> fmt::Debug for Blob<C> {
200    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
201        f.debug_tuple("Blob").field(&self.data).finish()
202    }
203}
204
205impl<C: Config> Display for Blob<C> {
206    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
207        base64::display::Base64Display::with_config(&self.data, C::CONFIG).fmt(f)
208    }
209}
210
211impl<C: Config> Hash for Blob<C> {
212    #[inline]
213    fn hash<H: Hasher>(&self, state: &mut H) {
214        self.data.hash(state);
215    }
216}
217
218impl<C: Config> Write for Blob<C> {
219    #[inline(always)]
220    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
221        self.data.write(buf)
222    }
223
224    #[inline(always)]
225    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
226        self.data.write_all(buf)
227    }
228
229    #[inline(always)]
230    fn flush(&mut self) -> io::Result<()> {
231        self.data.flush()
232    }
233}
234
235impl<C: Config> FromIterator<u8> for Blob<C> {
236    fn from_iter<I>(iter: I) -> Blob<C>
237    where
238        I: IntoIterator<Item = u8>,
239    {
240        Blob::from_vec(Vec::from_iter(iter))
241    }
242}
243
244impl<C: Config> Extend<u8> for Blob<C> {
245    #[inline]
246    fn extend<T>(&mut self, iter: T)
247    where
248        T: IntoIterator<Item = u8>,
249    {
250        self.data.extend(iter)
251    }
252}
253
254impl<'a, C: Config> Extend<&'a u8> for Blob<C> {
255    #[inline]
256    fn extend<T>(&mut self, iter: T)
257    where
258        T: IntoIterator<Item = &'a u8>,
259    {
260        self.data.extend(iter)
261    }
262}
263
264impl<C: Config> IntoIterator for Blob<C> {
265    type Item = u8;
266    type IntoIter = IntoIter<u8>;
267
268    #[inline]
269    fn into_iter(self) -> Self::IntoIter {
270        self.data.into_iter()
271    }
272}
273
274impl<'a, C: Config> IntoIterator for &'a Blob<C> {
275    type Item = &'a u8;
276    type IntoIter = Iter<'a, u8>;
277
278    #[inline]
279    fn into_iter(self) -> Self::IntoIter {
280        self.data.iter()
281    }
282}
283
284impl<'a, C: Config> IntoIterator for &'a mut Blob<C> {
285    type Item = &'a mut u8;
286    type IntoIter = IterMut<'a, u8>;
287
288    #[inline]
289    fn into_iter(self) -> Self::IntoIter {
290        self.data.iter_mut()
291    }
292}
293
294impl<C: Config> Deref for Blob<C> {
295    type Target = [u8];
296
297    #[inline(always)]
298    fn deref(&self) -> &Self::Target {
299        &self.data
300    }
301}
302
303impl<C: Config> DerefMut for Blob<C> {
304    #[inline(always)]
305    fn deref_mut(&mut self) -> &mut Self::Target {
306        &mut self.data
307    }
308}
309
310impl<T, C: Config> From<T> for Blob<C>
311where
312    T: Into<Vec<u8>>,
313{
314    #[inline(always)]
315    fn from(value: T) -> Blob<C> {
316        Blob::from_vec(value.into())
317    }
318}
319
320impl<C: Config> PartialEq<Self> for Blob<C> {
321    #[inline(always)]
322    fn eq(&self, other: &Self) -> bool {
323        self.data.eq(&other.data)
324    }
325}
326
327impl<C: Config> Eq for Blob<C> {}
328
329impl<T, C: Config> PartialEq<T> for Blob<C>
330where
331    Vec<u8>: PartialEq<T>,
332{
333    #[inline(always)]
334    fn eq(&self, other: &T) -> bool {
335        self.data == *other
336    }
337}
338
339impl<C: Config> AsRef<[u8]> for Blob<C> {
340    #[inline(always)]
341    fn as_ref(&self) -> &[u8] {
342        &self.data
343    }
344}
345
346impl<C: Config> AsRef<Vec<u8>> for Blob<C> {
347    #[inline(always)]
348    fn as_ref(&self) -> &Vec<u8> {
349        &self.data
350    }
351}
352
353impl<C: Config> AsMut<[u8]> for Blob<C> {
354    #[inline(always)]
355    fn as_mut(&mut self) -> &mut [u8] {
356        &mut self.data
357    }
358}
359
360impl<C: Config> AsMut<Vec<u8>> for Blob<C> {
361    #[inline(always)]
362    fn as_mut(&mut self) -> &mut Vec<u8> {
363        &mut self.data
364    }
365}
366
367impl<C: Config> Borrow<Vec<u8>> for Blob<C> {
368    fn borrow(&self) -> &Vec<u8> {
369        &self.data
370    }
371}
372
373impl<C: Config> BorrowMut<Vec<u8>> for Blob<C> {
374    fn borrow_mut(&mut self) -> &mut Vec<u8> {
375        &mut self.data
376    }
377}
378
379impl<C: Config> serde::Serialize for Blob<C> {
380    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
381    where
382        S: serde::Serializer,
383    {
384        let encoded = self.encode_base64();
385
386        serializer.serialize_str(encoded.as_str())
387    }
388}
389
390impl<'de, C: Config> serde::Deserialize<'de> for Blob<C> {
391    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
392    where
393        D: serde::Deserializer<'de>,
394    {
395        struct BlobVisitor<C: Config>(PhantomData<C>);
396
397        impl<'de, C: Config> serde::de::Visitor<'de> for BlobVisitor<C> {
398            type Value = Blob<C>;
399
400            fn expecting(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
401                f.write_str("base64 encoded string or byte sequence")
402            }
403
404            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
405            where
406                E: serde::de::Error,
407            {
408                FromStr::from_str(value).map_err(E::custom)
409            }
410
411            fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
412            where
413                E: serde::de::Error,
414            {
415                Ok(Blob::from_vec(value.to_owned()))
416            }
417
418            fn visit_byte_buf<E>(self, value: Vec<u8>) -> Result<Self::Value, E>
419            where
420                E: serde::de::Error,
421            {
422                Ok(Blob::from_vec(value))
423            }
424
425            fn visit_seq<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
426            where
427                V: serde::de::SeqAccess<'de>,
428            {
429                // Preallocate the bytes vec if possible, but remain conservative
430                let mut bytes = Vec::with_capacity(visitor.size_hint().unwrap_or(0).min(4096));
431
432                while let Some(byte) = visitor.next_element()? {
433                    bytes.push(byte);
434                }
435
436                Ok(Blob::from_vec(bytes))
437            }
438        }
439
440        deserializer.deserialize_any(BlobVisitor(PhantomData))
441    }
442}