xdr_codec/
lib.rs

1//! XDR runtime encoding/decoding
2//!
3//! This crate provides runtime support for encoding and decoding XDR
4//! data. It is intended to be used with code generated by the
5//! "xdrgen" crate, but it can also be used with hand-written code.
6//!
7//! It provides two key traits - `Pack` and `Unpack` - which all
8//! encodable types must implement. It also provides the helper
9//! functions `pack()` and `unpack()` to simplify the API.
10//!
11//! By default, this does not implement codecs for `i8` or `u8`. This is because
12//! encoding individual bytes is quite inefficient, as they're all padded up to
13//! 32 bits (4 bytes). This doesn't matter for individual items, but arrays of
14//! bytes should be represented by opaque arrays (static size) or flex arrays
15//! (dynamic size) (or strings for character data).
16//!
17//! However, some protocols are mis-specified to use byte arrays (I'm looking at
18//! you, gluster), so the option to support the exists. You can enable byte codec
19//! with the `bytecodec` feature.
20#![crate_type = "lib"]
21
22extern crate byteorder;
23#[macro_use]
24extern crate error_chain;
25
26pub use std::io::{Read, Write};
27use std::ops::Deref;
28use std::cmp::min;
29use std::borrow::{Borrow, Cow};
30use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
31
32pub mod record;
33
34mod error;
35pub use error::*;
36
37#[cfg(test)]
38mod test;
39
40static PADDING: [u8; 4] = [0; 4];
41
42/// Compute XDR padding.
43///
44/// Return slice of zero padding needed to bring `sz` up to a multiple of 4. If no padding is needed,
45/// it will be a zero-sized slice.
46#[inline]
47pub fn padding(sz: usize) -> &'static [u8] {
48    &PADDING[..(4 - (sz % 4)) % 4]
49}
50
51/// Wrapper for XDR opaque data.
52///
53/// In XDR terms, "opaque data" is a plain array of bytes, packed as tightly as possible, and then
54/// padded to a 4 byte offset. This is different from an array of bytes, where each byte would be
55/// padded to 4 bytes when emitted into the array.
56#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
57pub struct Opaque<'a>(pub Cow<'a, [u8]>);
58
59impl<'a> Opaque<'a> {
60    pub fn owned(v: Vec<u8>) -> Opaque<'a> {
61        Opaque(Cow::Owned(v))
62    }
63    pub fn borrowed(v: &'a [u8]) -> Opaque<'a> {
64        Opaque(Cow::Borrowed(v))
65    }
66}
67
68impl<'a> Deref for Opaque<'a> {
69    type Target = [u8];
70    fn deref(&self) -> &[u8] {
71        self.0.deref()
72    }
73}
74
75impl<'a> From<&'a [u8]> for Opaque<'a> {
76    fn from(v: &'a [u8]) -> Self {
77        Opaque::borrowed(v)
78    }
79}
80
81/// Serialization (packing) helper.
82///
83/// Helper to serialize any type implementing `Pack` into an implementation of `std::io::Write`.
84pub fn pack<Out: Write, T: Pack<Out>>(val: &T, out: &mut Out) -> Result<()> {
85    val.pack(out).map(|_| ())
86}
87
88/// Pack a fixed-size array.
89///
90/// As the size is fixed, it doesn't need to be encoded. `sz` is in units of array elements.
91/// If the `val` is too large, it is truncated; it is too small, then the array is padded out with
92/// default values (if provided). If the array is too small and there's no pad/default value, then it fails
93/// with `Error::InvalidLen`.
94pub fn pack_array<Out, T>(val: &[T], sz: usize, out: &mut Out, defl: Option<&T>) -> Result<usize>
95where
96    Out: Write,
97    T: Pack<Out>,
98{
99    let mut vsz = 0;
100    let val = &val[..min(sz, val.len())];
101
102    for v in val {
103        vsz += v.pack(out)?;
104    }
105    assert!(vsz % 4 == 0);
106
107    if val.len() < sz {
108        if let Some(defl) = defl {
109            for _ in val.len()..sz {
110                vsz += defl.pack(out)?;
111            }
112        } else {
113            bail!(ErrorKind::InvalidLen(sz));
114        }
115    }
116    Ok(vsz)
117}
118
119/// Pack a fixed-size byte array
120///
121/// As size is fixed, it doesn't need to be encoded. `sz` is in bytes (and array elements, which are u8)
122/// If the array is too large, it is truncated; if its too small its padded with `0x00`.
123pub fn pack_opaque_array<Out: Write>(val: &[u8], sz: usize, out: &mut Out) -> Result<usize> {
124    let mut vsz;
125    let val = &val[..min(sz, val.len())];
126
127    vsz = val.len();
128    out.write_all(val)?;
129
130    let p = padding(sz);
131    for _ in val.len()..(sz + p.len()) {
132        out.write_u8(0)?;
133        vsz += 1;
134    }
135
136    Ok(vsz)
137}
138
139/// Pack a dynamically sized array, with size limit check.
140///
141/// This packs an array of packable objects, and also applies an optional size limit.
142#[inline]
143pub fn pack_flex<Out: Write, T: Pack<Out>>(
144    val: &[T],
145    maxsz: Option<usize>,
146    out: &mut Out,
147) -> Result<usize> {
148    if maxsz.map_or(false, |m| val.len() > m) {
149        bail!(ErrorKind::InvalidLen(maxsz.unwrap()));
150    }
151
152    val.pack(out)
153}
154
155/// Pack a dynamically sized opaque array, with size limit check.
156///
157/// This packs an array of packable objects, and also applies an optional size limit.
158#[inline]
159pub fn pack_opaque_flex<Out: Write>(
160    val: &[u8],
161    maxsz: Option<usize>,
162    out: &mut Out,
163) -> Result<usize> {
164    if maxsz.map_or(false, |m| val.len() > m) {
165        bail!(ErrorKind::InvalidLen(maxsz.unwrap()));
166    }
167
168    Opaque::borrowed(val).pack(out)
169}
170
171/// Pack a string with size limit check.
172#[inline]
173pub fn pack_string<Out: Write>(val: &str, maxsz: Option<usize>, out: &mut Out) -> Result<usize> {
174    pack_opaque_flex(val.as_bytes(), maxsz, out)
175}
176
177/// Unpack a fixed-sized array
178///
179/// Unpack a fixed-size array of elements. The results are placed in `array`, but the actual wire-size of
180/// the array is `arraysz`. If the supplied `array` is too large, the remainer is filled in with the
181/// default value (if provided); if it is too small, the excess elements are discarded.
182///
183/// If the provided array is too large and there is no default, then decoding fails with an `InvalidLen` error.
184/// All the elements in `array` will be initialized after a successful return.
185pub fn unpack_array<In, T>(
186    input: &mut In,
187    array: &mut [T],
188    arraysz: usize,
189    defl: Option<&T>,
190) -> Result<usize>
191where
192    In: Read,
193    T: Unpack<In> + Clone,
194{
195    let mut rsz = 0;
196    let sz = min(arraysz, array.len());
197
198    for elem in &mut array[..sz] {
199        let (v, sz) = Unpack::unpack(input)?;
200        rsz += sz;
201        *elem = v;
202    }
203
204    // Fill in excess array entries with default values
205    if arraysz < array.len() {
206        if let Some(defl) = defl {
207            for elem in &mut array[arraysz..] {
208                *elem = defl.clone();
209            }
210        } else {
211            bail!(ErrorKind::InvalidLen(arraysz));
212        }
213    }
214
215    // Mop up unused array entries on the wire
216    if arraysz > array.len() {
217        for _ in array.len()..arraysz {
218            let (_, sz) = T::unpack(input)?;
219            rsz += sz;
220        }
221    }
222    assert!(rsz % 4 == 0);
223
224    Ok(rsz)
225}
226
227/// Unpack a fixed-sized opaque array
228///
229/// Unpack a fixed-size array of raw bytes. The results are placed in `bytes`, but the actual wire-size of
230/// the array is `bytesz`. If the supplied `bytes` is too large, the remainer is filled in with 0x00;
231/// if it is too small, the excess elements are discarded.
232///
233/// All the bytes in `bytes` will be initialized after a successful call.
234pub fn unpack_opaque_array<In: Read>(
235    input: &mut In,
236    bytes: &mut [u8],
237    bytesz: usize,
238) -> Result<usize> {
239    let sz = min(bytesz, bytes.len());
240    let mut rsz = 0;
241
242    while rsz < sz {
243        let r = input.read(&mut bytes[rsz..])?;
244        rsz += r;
245    }
246
247    // Fill in excess
248    if sz < bytes.len() {
249        for b in &mut bytes[sz..] {
250            *b = 0;
251        }
252    }
253
254    // Mop up unused data on the wire and padding
255    let p = padding(bytesz).len();
256    if bytes.len() < bytesz + p {
257        for _ in bytes.len()..(bytesz + p) {
258            let _ = input.read_u8()?;
259            rsz += 1;
260        }
261    }
262
263    Ok(rsz)
264}
265
266/// Unpack a (perhaps) length-limited array
267pub fn unpack_flex<In: Read, T: Unpack<In>>(
268    input: &mut In,
269    maxsz: Option<usize>,
270) -> Result<(Vec<T>, usize)> {
271    let (elems, mut sz) = Unpack::unpack(input)?;
272
273    if maxsz.map_or(false, |m| elems > m) {
274        bail!(ErrorKind::InvalidLen(maxsz.unwrap()));
275    }
276
277    let mut out = Vec::with_capacity(elems);
278
279    for _ in 0..elems {
280        let (e, esz) = Unpack::unpack(input)?;
281        out.push(e);
282        sz += esz;
283    }
284
285    let p = padding(sz);
286    for _ in 0..p.len() {
287        let _ = input.read_u8()?;
288    }
289    sz += p.len();
290
291    Ok((out, sz))
292}
293
294/// Unpack a (perhaps) length-limited opaque array
295///
296/// Unpack an XDR encoded array of bytes, with an optional maximum length.
297pub fn unpack_opaque_flex<In: Read>(
298    input: &mut In,
299    maxsz: Option<usize>,
300) -> Result<(Vec<u8>, usize)> {
301    let (elems, mut sz) = Unpack::unpack(input)?;
302
303    if maxsz.map_or(false, |m| elems > m) {
304        bail!(ErrorKind::InvalidLen(maxsz.unwrap()));
305    }
306
307    let mut out = Vec::with_capacity(elems);
308
309    sz += input.take(elems as u64).read_to_end(&mut out)?;
310
311    let p = padding(sz);
312    for _ in 0..p.len() {
313        let _ = input.read_u8()?;
314    }
315    sz += p.len();
316
317    Ok((out, sz))
318}
319
320/// Unpack (perhaps) length-limited string
321pub fn unpack_string<In: Read>(input: &mut In, maxsz: Option<usize>) -> Result<(String, usize)> {
322    let (v, sz) = unpack_opaque_flex(input, maxsz)?;
323
324    String::from_utf8(v).map_err(Error::from).map(|s| (s, sz))
325}
326
327/// Basic packing trait.
328///
329/// This trait is used to implement XDR packing any Rust type into a
330/// `Write` stream. It returns the number of bytes the encoding took.
331///
332/// This crate provides a number of implementations for all the basic
333/// XDR types, and generated code will generally compose them to pack
334/// structures, unions, etc.
335///
336/// Streams generated by `Pack` can be consumed by `Unpack`.
337pub trait Pack<Out: Write> {
338    fn pack(&self, out: &mut Out) -> Result<usize>;
339}
340
341#[cfg(feature = "bytecodec")]
342impl<Out: Write> Pack<Out> for u8 {
343    #[inline]
344    fn pack(&self, out: &mut Out) -> Result<usize> {
345        out.write_u32::<BigEndian>(*self as u32)
346            .map_err(Error::from)
347            .map(|_| 4)
348    }
349}
350
351#[cfg(feature = "bytecodec")]
352impl<Out: Write> Pack<Out> for i8 {
353    #[inline]
354    fn pack(&self, out: &mut Out) -> Result<usize> {
355        out.write_i32::<BigEndian>(*self as i32)
356            .map_err(Error::from)
357            .map(|_| 4)
358    }
359}
360
361impl<Out: Write> Pack<Out> for u32 {
362    #[inline]
363    fn pack(&self, out: &mut Out) -> Result<usize> {
364        out.write_u32::<BigEndian>(*self).map_err(Error::from).map(
365            |_| 4,
366        )
367    }
368}
369
370impl<Out: Write> Pack<Out> for i32 {
371    #[inline]
372    fn pack(&self, out: &mut Out) -> Result<usize> {
373        out.write_i32::<BigEndian>(*self).map_err(Error::from).map(
374            |_| 4,
375        )
376    }
377}
378
379impl<Out: Write> Pack<Out> for u64 {
380    #[inline]
381    fn pack(&self, out: &mut Out) -> Result<usize> {
382        out.write_u64::<BigEndian>(*self).map_err(Error::from).map(
383            |_| 8,
384        )
385    }
386}
387
388impl<Out: Write> Pack<Out> for i64 {
389    #[inline]
390    fn pack(&self, out: &mut Out) -> Result<usize> {
391        out.write_i64::<BigEndian>(*self).map_err(Error::from).map(
392            |_| 8,
393        )
394    }
395}
396
397impl<Out: Write> Pack<Out> for f32 {
398    #[inline]
399    fn pack(&self, out: &mut Out) -> Result<usize> {
400        out.write_f32::<BigEndian>(*self).map_err(Error::from).map(
401            |_| 4,
402        )
403    }
404}
405
406impl<Out: Write> Pack<Out> for f64 {
407    #[inline]
408    fn pack(&self, out: &mut Out) -> Result<usize> {
409        out.write_f64::<BigEndian>(*self).map_err(Error::from).map(
410            |_| 8,
411        )
412    }
413}
414
415impl<Out: Write> Pack<Out> for bool {
416    #[inline]
417    fn pack(&self, out: &mut Out) -> Result<usize> {
418        (*self as u32).pack(out)
419    }
420}
421
422impl<Out: Write> Pack<Out> for () {
423    #[inline]
424    fn pack(&self, _out: &mut Out) -> Result<usize> {
425        Ok(0)
426    }
427}
428
429impl<Out: Write> Pack<Out> for usize {
430    #[inline]
431    fn pack(&self, out: &mut Out) -> Result<usize> {
432        (*self as u32).pack(out)
433    }
434}
435
436impl<Out: Write, T: Pack<Out>> Pack<Out> for [T] {
437    fn pack(&self, out: &mut Out) -> Result<usize> {
438        let len = self.len();
439
440        let mut sz = len.pack(out)?;
441        for it in self {
442            sz += it.pack(out)?;
443        }
444
445        let p = padding(sz);
446        if p.len() > 0 {
447            out.write_all(p)?;
448            sz += p.len();
449        }
450
451        Ok(sz)
452    }
453}
454
455impl<Out: Write, T: Pack<Out>> Pack<Out> for Vec<T> {
456    #[inline]
457    fn pack(&self, out: &mut Out) -> Result<usize> {
458        if self.len() > u32::max_value() as usize {
459            return Err(ErrorKind::InvalidLen(self.len()).into());
460        }
461
462        (&self[..]).pack(out)
463    }
464}
465
466impl<'a, Out: Write> Pack<Out> for Opaque<'a> {
467    fn pack(&self, out: &mut Out) -> Result<usize> {
468        let mut sz;
469        let data: &[u8] = self.0.borrow();
470
471        if data.len() > u32::max_value() as usize {
472            return Err(ErrorKind::InvalidLen(data.len()).into());
473        }
474
475        sz = data.len().pack(out)?;
476
477        out.write_all(data)?;
478        sz += data.len();
479
480        let p = padding(sz);
481        if p.len() > 0 {
482            out.write_all(p)?;
483            sz += p.len();
484        }
485
486        Ok(sz)
487    }
488}
489
490impl<Out: Write> Pack<Out> for str {
491    #[inline]
492    fn pack(&self, out: &mut Out) -> Result<usize> {
493        Opaque::borrowed(self.as_bytes()).pack(out)
494    }
495}
496
497impl<Out: Write, T: Pack<Out>> Pack<Out> for Option<T> {
498    fn pack(&self, out: &mut Out) -> Result<usize> {
499        match self {
500            &None => false.pack(out),
501            &Some(ref v) => {
502                let sz = true.pack(out)? + v.pack(out)?;
503                Ok(sz)
504            }
505        }
506    }
507}
508
509impl<Out: Write, T: Pack<Out>> Pack<Out> for Box<T> {
510    fn pack(&self, out: &mut Out) -> Result<usize> {
511        let t: &T = self.borrow();
512        t.pack(out)
513    }
514}
515
516impl<'a, Out: Write, T> Pack<Out> for Cow<'a, T>
517where
518    T: 'a + Pack<Out> + ToOwned<Owned = T>,
519{
520    fn pack(&self, out: &mut Out) -> Result<usize> {
521        let t: &T = self.borrow();
522        t.pack(out)
523    }
524}
525
526/// Deserialization (unpacking) helper function
527///
528/// This function will read encoded bytes from `input` (a `Read`
529/// implementation) and return a fully constructed type (or an
530/// error). This relies on type inference to determine which type is
531/// to be unpacked, so its up to the calling envionment to clarify
532/// this. (Generally it falls out quite naturally.)
533pub fn unpack<In: Read, T: Unpack<In>>(input: &mut In) -> Result<T> {
534    T::unpack(input).map(|(v, _)| v)
535}
536
537/// Basic unpacking trait
538///
539/// This trait is used to unpack a type from an XDR encoded byte
540/// stream (encoded with `Pack`).  It returns the decoded instance and
541/// the number of bytes consumed from the input.
542///
543/// This crate provides implementations for all the basic XDR types,
544/// as well as for arrays.
545pub trait Unpack<In: Read>: Sized {
546    fn unpack(input: &mut In) -> Result<(Self, usize)>;
547}
548
549#[cfg(feature = "bytecodec")]
550impl<In: Read> Unpack<In> for u8 {
551    #[inline]
552    fn unpack(input: &mut In) -> Result<(Self, usize)> {
553        input.read_u32::<BigEndian>().map_err(Error::from).map(
554            |v| {
555                (v as u8, 4)
556            },
557        )
558    }
559}
560
561#[cfg(feature = "bytecodec")]
562impl<In: Read> Unpack<In> for i8 {
563    #[inline]
564    fn unpack(input: &mut In) -> Result<(Self, usize)> {
565        input.read_i32::<BigEndian>().map_err(Error::from).map(
566            |v| {
567                (v as i8, 4)
568            },
569        )
570    }
571}
572
573impl<In: Read> Unpack<In> for u32 {
574    #[inline]
575    fn unpack(input: &mut In) -> Result<(Self, usize)> {
576        input.read_u32::<BigEndian>().map_err(Error::from).map(
577            |v| (v, 4),
578        )
579    }
580}
581
582impl<In: Read> Unpack<In> for i32 {
583    #[inline]
584    fn unpack(input: &mut In) -> Result<(Self, usize)> {
585        input.read_i32::<BigEndian>().map_err(Error::from).map(
586            |v| (v, 4),
587        )
588    }
589}
590
591impl<In: Read> Unpack<In> for u64 {
592    #[inline]
593    fn unpack(input: &mut In) -> Result<(Self, usize)> {
594        input.read_u64::<BigEndian>().map_err(Error::from).map(
595            |v| (v, 8),
596        )
597    }
598}
599
600impl<In: Read> Unpack<In> for i64 {
601    #[inline]
602    fn unpack(input: &mut In) -> Result<(Self, usize)> {
603        input.read_i64::<BigEndian>().map_err(Error::from).map(
604            |v| (v, 8),
605        )
606    }
607}
608
609impl<In: Read> Unpack<In> for f32 {
610    fn unpack(input: &mut In) -> Result<(Self, usize)> {
611        input.read_f32::<BigEndian>().map_err(Error::from).map(
612            |v| (v, 4),
613        )
614    }
615}
616
617impl<In: Read> Unpack<In> for f64 {
618    fn unpack(input: &mut In) -> Result<(Self, usize)> {
619        input.read_f64::<BigEndian>().map_err(Error::from).map(
620            |v| (v, 8),
621        )
622    }
623}
624
625impl<In: Read> Unpack<In> for bool {
626    #[inline]
627    fn unpack(input: &mut In) -> Result<(Self, usize)> {
628        i32::unpack(input).and_then(|(v, sz)| match v {
629            0 => Ok((false, sz)),
630            1 => Ok((true, sz)),
631            v => Err(ErrorKind::InvalidEnum(v).into()),
632        })
633    }
634}
635
636impl<In: Read> Unpack<In> for () {
637    #[inline]
638    fn unpack(_input: &mut In) -> Result<(Self, usize)> {
639        Ok(((), 0))
640    }
641}
642
643impl<In: Read> Unpack<In> for usize {
644    #[inline]
645    fn unpack(input: &mut In) -> Result<(Self, usize)> {
646        u32::unpack(input).map(|(v, sz)| (v as usize, sz))
647    }
648}
649
650impl<In: Read, T: Unpack<In>> Unpack<In> for Vec<T> {
651    fn unpack(input: &mut In) -> Result<(Self, usize)> {
652        unpack_flex(input, None)
653    }
654}
655
656impl<In: Read> Unpack<In> for String {
657    fn unpack(input: &mut In) -> Result<(Self, usize)> {
658        let (v, sz) = unpack_opaque_flex(input, None)?;
659        String::from_utf8(v).map_err(Error::from).map(|s| (s, sz))
660    }
661}
662
663impl<'a, In: Read> Unpack<In> for Opaque<'a> {
664    fn unpack(input: &mut In) -> Result<(Self, usize)> {
665        let (len, mut sz) = usize::unpack(input)?;
666        let mut v = Vec::new();
667        sz += input.by_ref().take(len as u64).read_to_end(&mut v)?;
668
669        let p = padding(sz);
670        for _ in 0..p.len() {
671            let _ = input.read_u8()?;
672            sz += 1;
673        }
674
675        Ok((Opaque(Cow::Owned(v)), sz))
676    }
677}
678
679impl<In: Read, T: Unpack<In>> Unpack<In> for Option<T> {
680    fn unpack(input: &mut In) -> Result<(Self, usize)> {
681        let (have, mut sz) = Unpack::unpack(input)?;
682        let ret = if have {
683            let (v, osz) = Unpack::unpack(input)?;
684            sz += osz;
685            Some(v)
686        } else {
687            None
688        };
689        Ok((ret, sz))
690    }
691}
692
693impl<In: Read, T: Unpack<In>> Unpack<In> for Box<T> {
694    fn unpack(input: &mut In) -> Result<(Self, usize)> {
695        let (b, sz) = Unpack::unpack(input)?;
696        Ok((Box::new(b), sz))
697    }
698}
699
700impl<'a, In: Read, T> Unpack<In> for Cow<'a, T>
701where
702    T: 'a + Unpack<In> + ToOwned<Owned = T>,
703{
704    fn unpack(input: &mut In) -> Result<(Self, usize)> {
705        let (b, sz) = Unpack::unpack(input)?;
706        Ok((Cow::Owned(b), sz))
707    }
708}