├── .gitignore ├── .rustfmt.toml ├── .travis-update-gh-pages.sh ├── .travis.yml ├── Cargo.toml ├── README.md ├── xdr-codec ├── Cargo.toml ├── README.md ├── src │ ├── error.rs │ ├── lib.rs │ ├── record.rs │ └── test.rs └── tests │ ├── qc-record.rs │ ├── quickcheck.rs │ └── test-record.rs └── xdrgen ├── Cargo.toml ├── README.md ├── example ├── Cargo.toml ├── build.rs └── src │ ├── simple.rs │ └── simple.x ├── src ├── lib.rs ├── spec │ ├── mod.rs │ ├── test.rs │ └── xdr_nom.rs └── xdrgen.rs └── tests └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | *~ 4 | 5 | # IDE - vscode 6 | .vscode 7 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | reorder_imported_names = true 2 | reorder_imports_in_group = true 3 | use_try_shorthand = true 4 | -------------------------------------------------------------------------------- /.travis-update-gh-pages.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # based on: http://sleepycoders.blogspot.se/2013/03/sharing-travis-ci-generated-files.html 4 | # and: https://gist.github.com/domenic/ec8b0fc8ab45f39403dd 5 | 6 | set -e # exit with nonzero exit code if anything fails 7 | 8 | #env | sed -e 's,GH_TOKEN=.*,GH_TOKEN=XXX,' 9 | 10 | # Only do it if not acting on a pull request. 11 | if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then 12 | 13 | echo git setup 14 | # Go to home and setup git 15 | cd $HOME 16 | git config --global user.name "Travis CI" 17 | git config --global user.email "travis@travis-ci.org" 18 | 19 | echo git clone ${GH_REPO} 20 | # Using token clone gh-pages branch. Pipe to /dev/null to avoid printing the decrypted key. 21 | git clone --quiet --branch=gh-pages https://${GH_TOKEN}@${GH_REPO} gh-pages > /dev/null 2>&1 22 | 23 | echo pages mkdir 24 | # Go there, and overwrite everything with the freshly built contents. 25 | mkdir -p gh-pages/doc # in case it's the first time 26 | 27 | echo copy 28 | cd gh-pages/doc 29 | rm -rf * 30 | for docs in $DOCS_OUT; do 31 | cp -Rf $TRAVIS_BUILD_DIR/$docs/. . 32 | done 33 | 34 | echo add 35 | # add, commit and push files 36 | git add --all -f . 37 | 38 | echo commit 39 | git commit -m "Travis build $TRAVIS_BUILD_NUMBER docs pushed to gh-pages" 40 | 41 | echo push 42 | git push -fq origin gh-pages > /dev/null 2>&1 43 | fi 44 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | rust: 4 | - stable 5 | - beta 6 | - nightly 7 | 8 | before_script: 9 | - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH 10 | 11 | addons: 12 | apt: 13 | packages: 14 | - libcurl4-openssl-dev 15 | - libelf-dev 16 | - libdw-dev 17 | - binutils-dev # optional: only required for the --verify flag of coveralls 18 | 19 | script: 20 | - | 21 | cd $TRAVIS_BUILD_DIR/xdr-codec && 22 | travis-cargo build && 23 | travis-cargo test -- --features bytecodec && 24 | travis-cargo --only stable doc && 25 | : travis-cargo coveralls --no-sudo --verify 26 | - | 27 | cd $TRAVIS_BUILD_DIR/xdrgen && 28 | travis-cargo build && 29 | travis-cargo test && 30 | travis-cargo doc 31 | 32 | # upload coverage once work out how to deal with 2 crates 33 | # travis-cargo coveralls --no-sudo --verify 34 | 35 | after_success: 36 | - cd $TRAVIS_BUILD_DIR && ./.travis-update-gh-pages.sh 37 | 38 | env: 39 | global: 40 | - GH_REPO: github.com/jsgf/rust-xdr.git 41 | - DOCS_OUT="xdrgen/target/doc xdr-codec/target/doc" 42 | - secure: CUnvQ3QAJEBfahqtsWTb72dvSB4xyebRIK3j/wwRNRP2evxwY1765VGTfV2zoPUD1MuR/8olEuHhs/b34tBMxhyMYZd2RGn+xuX4jD6aav318oVN2B4Y1sBDTme9QDW5XVDetKQlfjzYZM98C3YyxktuE66/Bar9D7g894EhC4KDjFv1G0REWy0bUlBcYh3JBO37lSHXE5g6noh/LlCRgFcno51irCoH/8qq+tCI3avAzAPS9lPYEUwr0xNRUpu61qTyf9riggcp/xAn96YMH+icJgmv/Ctbf3mr7DgJlSwces6iog/vjSnut52jChp52ci+iwmyfo8WCfThMQYFkIZEPkdFbLX1JpdlvT2LgHcrKdVq2JtL/HvUb8Oma+T4DZYlJRMg2jTMJcstta4Ik2RSBhj2RtmuZ1Ktp6Ty+Bt/0JyLYiwx6uVRkBSdKCrOX1S5Y9y06vbVz92WU15tuuF8cR9aPT7ND/HKe9jYtND7T0Z1pSj7ZKRGi+EGy7LZHZ1Pc/35AV5rcChEEpr0zVVDY7VyZctLCFksYVg3qkYvOKpp3pUlQS4HH28U2bvsFH2vghk18LMvBjqutPIhsSLDyYUTOdJWKW1LL5SgfifI3mqqSjc06HygDw3WClmMD49fnaAkLXlhN4bW/sQ2TffDVgCfoGdh7j4Cvfs2pXo= 43 | - RUST_BACKTRACE=1 44 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ "xdr-codec", "xdrgen", "xdrgen/example" ] 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust XDR library 2 | 3 | [![Build Status](https://travis-ci.org/jsgf/rust-xdr.svg?branch=master)](https://travis-ci.org/jsgf/rust-xdr) 4 | [![Crates.io](https://img.shields.io/crates/v/xdr-codec.svg)](https://crates.io/crates/xdr-codec/) 5 | 6 | This repo contains two crates: 7 | * [xdr-codec](xdr-codec), a runtime library to encode and decode XDR types 8 | * [xdrgen](xdrgen), a code generator which parses XDR specs (RFC4506) and 9 | generates Rust type definitions, with code to serialize/deserialize 10 | them as XDR. 11 | 12 | ## License 13 | 14 | Licensed under either of 15 | 16 | * Apache License, Version 2.0, ([LICENSE-APACHE](http://www.apache.org/licenses/LICENSE-2.0)) 17 | * MIT license ([LICENSE-MIT](http://opensource.org/licenses/MIT)) 18 | 19 | at your option. 20 | 21 | ### Contribution 22 | 23 | Unless you explicitly state otherwise, any contribution intentionally submitted 24 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any 25 | additional terms or conditions. 26 | -------------------------------------------------------------------------------- /xdr-codec/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xdr-codec" 3 | version = "0.4.4" 4 | authors = ["Jeremy Fitzhardinge "] 5 | license = "MIT OR Apache-2.0" 6 | description = "XDR encode/decode runtime support. Pairs with xdrgen which generates code from specs." 7 | repository = "https://github.com/jsgf/rust-xdr/tree/master/xdr-codec" 8 | documentation = "https://docs.rs/xdr-codec" 9 | readme = "README.md" 10 | keywords = ["encoding", "protocol", "xdr", "rfc4506", "serialization"] 11 | include = [ "src/**/*.rs", "tests/**/*.rs", "*.md", "Cargo.toml" ] 12 | 13 | [features] 14 | # Enable use of `Pack`/`Unpack` traits for `i8`/`u8`. Normally this is disabled to 15 | # prevent unintended use of `char thing[]` arrays when then intent was `opaque thing[]`. 16 | bytecodec = [] 17 | # For travis 18 | unstable = [] 19 | 20 | [dependencies] 21 | byteorder = "1.0" 22 | error-chain = "0.10" 23 | 24 | [dev-dependencies] 25 | quickcheck = "0.4" 26 | -------------------------------------------------------------------------------- /xdr-codec/README.md: -------------------------------------------------------------------------------- 1 | # Rust XDR library 2 | 3 | [![Build Status](https://travis-ci.org/jsgf/rust-xdr.svg?branch=master)](https://travis-ci.org/jsgf/rust-xdr) 4 | [![Crates.io](https://img.shields.io/crates/v/xdr-codec.svg)]() 5 | [![Coverage Status](https://coveralls.io/repos/github/jsgf/rust-xdr/badge.svg?branch=master)](https://coveralls.io/github/jsgf/rust-xdr?branch=master) 6 | 7 | This crate provides a set of runtime routines to encode and decode 8 | basic XDR types, which can be used with 9 | [xdrgen's](https://github.com/jsgf/rust-xdrgen) automatically 10 | generated code, or with hand-written codecs. 11 | 12 | This crate also implements XDR-RPC record marking in the form of the 13 | `XdrRecordReader` and `XdrRecordWriter` IO filters. 14 | 15 | ## Usage 16 | 17 | The easiest way to use this library is with [xdrgen](https://crates.io/crates/xdrgen), 18 | which takes takes a specification in a `.x` file and generates all the necessary 19 | definitions for you. 20 | 21 | However, you can manually implement the `Pack` and `Unpack` traits for your own 22 | types: 23 | 24 | ``` 25 | struct MyType { 26 | a: u32, 27 | b: Vec, 28 | } 29 | 30 | impl Pack for MyType 31 | where W: Write 32 | { 33 | fn pack(&self, out: &mut W) -> xdr_codec::Result { 34 | let mut sz = 0; 35 | 36 | sz += try!(self.a.pack(out)); 37 | sz += try!(Opaque::borrowed(self.b).pack(out)); 38 | 39 | Ok(sz) 40 | } 41 | } 42 | 43 | impl Unpack for MyType 44 | where R: Read 45 | { 46 | fn unpack(input: &mut In) -> Result<(Self, usize)> { 47 | let mut rsz = 0; 48 | let ret = MyType { 49 | a: { let (v, sz) = try!(Unpack::unpack(input)); rsz += sz; v }, 50 | b: { let (v, sz) = try!(Opaque::unpack(input)); rsz += sz; v.into_owned() }, 51 | }; 52 | 53 | Ok((ret, rsz)) 54 | } 55 | } 56 | ``` 57 | 58 | or alternatively, put the following in src/mytype.x: 59 | 60 | ``` 61 | struct MyType { 62 | unsigned int a; 63 | opaque b<>; 64 | } 65 | ``` 66 | 67 | then add a build.rs to your Cargo.toml: 68 | 69 | ``` 70 | extern crate xdrgen; 71 | 72 | fn main() { 73 | xdrgen::compile("src/mytype.x").expect("xdrgen mytype.x failed"); 74 | } 75 | ``` 76 | 77 | then include the generated code in one of your modules: 78 | ``` 79 | extern crate xdr_codec; 80 | 81 | // ... 82 | 83 | include!(concat!(env!("OUT_DIR"), "/mytype_xdr.rs")); 84 | ``` 85 | 86 | ## Documentation 87 | 88 | Complete documentation is [here](https://docs.rs/xdr-codec/). 89 | 90 | ## Changes in 0.4.2 91 | 92 | Implement standard traits for `char`/`unsigned char` (`i8`/`u8` in Rust). 93 | 94 | Also handle `short`/`unsigned short` as an extension in .x files. They are still 95 | represented in memory as `i32`/`u32`. 96 | 97 | ## Changes in 0.4 98 | 99 | Version 0.4 added the `bytecodec` feature, which implements `Pack` and `Unpack` 100 | for byte types (`i8` and `u8`). This is normally unwanted, since bytes suffer from 101 | massive padding on the wire when used individually, or in an array of bytes (`opaque` 102 | is the preferred way to transport compact byte arrays). However, some protocols 103 | are mis-specified to use padded byte arrays, so `bytecodec` is available for them. 104 | 105 | ## Changes in 0.2 106 | 107 | Versions starting with 0.2 introduced a number of breaking changes: 108 | 109 | * `u8` no longer implements `Pack`/`Unpack` 110 | 111 | XDR doesn't directly support encoding individual bytes; if it did, it would 112 | require each one to be padded out to 4 bytes. xdr-codec 0.1 implemented 113 | `Pack` and `Unpack` for `u8` primarily to allow direct use of a `Vec` 114 | as an XDR `opaque<>`. However, this also allowed direct use of 115 | `u8::pack()` which makes it too easy to accidentally generate a malformed 116 | XDR stream without proper padding. 117 | 118 | In 0.2, u8 no longer implements `Pack` and `Unpack`. Instead, xdr-codec 119 | has a `Opaque<'a>(&'a [u8])` wrapper which does. This allows any `[u8]` 120 | slice to be packed and unpacked. 121 | 122 | It also has a set of helper functions for packing and unpacking both 123 | flexible and fixed-sized opaques, strings and general arrays. These make 124 | it straightforward to manage arrays in a way that is robust. This also allows 125 | xdrgen to generate code for fixed-sized arrays that's not completely unrolled 126 | unpack calls. 127 | 128 | (I'm not entirely happy with the proliferation of functions however, so 129 | I'm thinking about a trait-based approach that is more idiomatic Rust. That 130 | may have to be 0.3.) 131 | 132 | * Extensions to XDR record marking 133 | 134 | I added `XdrRecordReaderIter` which allows iteration over records. Previously 135 | all the records in the stream were flattened into a plain byte stream, which 136 | defeats the purpose of the records. `XdrRecordReader` still implements `Read` 137 | so that's still available, but it also implements `IntoIterator` so you can 138 | iterate records. 139 | 140 | The addition of more unit tests (see below) pointed out some poorly thought 141 | out corner cases, so now record generation and use of the EOR marker is more 142 | consistent. 143 | 144 | * More unit tests, including quickcheck generated ones 145 | 146 | I've increased the number of tests, and added quickcheck generated tests 147 | which cleared up a few corner cases. 148 | 149 | ## License 150 | 151 | Licensed under either of 152 | 153 | * Apache License, Version 2.0, ([LICENSE-APACHE](http://www.apache.org/licenses/LICENSE-2.0)) 154 | * MIT license ([LICENSE-MIT](http://opensource.org/licenses/MIT)) 155 | 156 | at your option. 157 | 158 | ### Contribution 159 | 160 | Unless you explicitly state otherwise, any contribution intentionally submitted 161 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any 162 | additional terms or conditions. 163 | -------------------------------------------------------------------------------- /xdr-codec/src/error.rs: -------------------------------------------------------------------------------- 1 | error_chain! { 2 | foreign_links { 3 | IOError(::std::io::Error); 4 | InvalidUtf8(::std::string::FromUtf8Error); 5 | } 6 | 7 | errors { 8 | InvalidCase(v: i32) { 9 | description("invalid union case") 10 | display("invalid union case: '{}'", v) 11 | } 12 | InvalidEnum(v: i32) { 13 | description("invalid enum value") 14 | display("invalid enum value: '{}'", v) 15 | } 16 | InvalidLen(v: usize) { 17 | description("invalid array len") 18 | display("invalid array len: '{}'", v) 19 | } 20 | } 21 | } 22 | 23 | unsafe impl Sync for Error {} 24 | 25 | impl Error { 26 | pub fn invalidcase(v: i32) -> Error { 27 | ErrorKind::InvalidCase(v).into() 28 | } 29 | 30 | pub fn invalidenum(v: i32) -> Error { 31 | ErrorKind::InvalidEnum(v).into() 32 | } 33 | 34 | pub fn invalidlen(v: usize) -> Error { 35 | ErrorKind::InvalidLen(v).into() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /xdr-codec/src/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 | 22 | extern crate byteorder; 23 | #[macro_use] 24 | extern crate error_chain; 25 | 26 | pub use std::io::{Read, Write}; 27 | use std::ops::Deref; 28 | use std::cmp::min; 29 | use std::borrow::{Borrow, Cow}; 30 | use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; 31 | 32 | pub mod record; 33 | 34 | mod error; 35 | pub use error::*; 36 | 37 | #[cfg(test)] 38 | mod test; 39 | 40 | static 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] 47 | pub 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)] 57 | pub struct Opaque<'a>(pub Cow<'a, [u8]>); 58 | 59 | impl<'a> Opaque<'a> { 60 | pub fn owned(v: Vec) -> 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 | 68 | impl<'a> Deref for Opaque<'a> { 69 | type Target = [u8]; 70 | fn deref(&self) -> &[u8] { 71 | self.0.deref() 72 | } 73 | } 74 | 75 | impl<'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`. 84 | pub fn pack>(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`. 94 | pub fn pack_array(val: &[T], sz: usize, out: &mut Out, defl: Option<&T>) -> Result 95 | where 96 | Out: Write, 97 | T: Pack, 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`. 123 | pub fn pack_opaque_array(val: &[u8], sz: usize, out: &mut Out) -> Result { 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] 143 | pub fn pack_flex>( 144 | val: &[T], 145 | maxsz: Option, 146 | out: &mut Out, 147 | ) -> Result { 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] 159 | pub fn pack_opaque_flex( 160 | val: &[u8], 161 | maxsz: Option, 162 | out: &mut Out, 163 | ) -> Result { 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] 173 | pub fn pack_string(val: &str, maxsz: Option, out: &mut Out) -> Result { 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. 185 | pub fn unpack_array( 186 | input: &mut In, 187 | array: &mut [T], 188 | arraysz: usize, 189 | defl: Option<&T>, 190 | ) -> Result 191 | where 192 | In: Read, 193 | T: Unpack + Clone, 194 | { 195 | #[inline] 196 | fn set(p: &mut T, v: T) { *p = v } 197 | #[inline] 198 | fn drop(_: &mut T) { } 199 | 200 | unpack_array_with(input, array, arraysz, set, drop, defl) 201 | } 202 | 203 | /// Specialized variant of `unpack_array` which initializes the element via a callback. This is primarily 204 | /// so that the array can be uninitialized, and we initialize it element at a time with `ptr::write()`. 205 | #[inline] 206 | pub fn unpack_array_with( 207 | input: &mut In, 208 | array: &mut [T], 209 | arraysz: usize, 210 | set: fn (&mut T, T), 211 | drop: fn(&mut T), 212 | defl: Option<&T>, 213 | ) -> Result 214 | where 215 | In: Read, 216 | T: Unpack + Clone, 217 | { 218 | let mut rsz = 0; 219 | let sz = min(arraysz, array.len()); 220 | 221 | // If we fail part way through then return the error and the index we got up to 222 | // so we can clean up the entries we did initialize. 223 | let res = (|| { 224 | for (idx, elem) in (&mut array[..sz]).into_iter().enumerate() { 225 | let (v, sz) = match Unpack::unpack(input) { 226 | Ok(v) => v, 227 | Err(e) => return Some((idx, e)), 228 | }; 229 | rsz += sz; 230 | set(elem, v); 231 | } 232 | None 233 | })(); 234 | if let Some((idx, err)) = res { 235 | for elem in &mut array[..idx] { 236 | drop(elem) 237 | }; 238 | return Err(err); 239 | } 240 | 241 | // Fill in excess array entries with default values 242 | if arraysz < array.len() { 243 | if let Some(defl) = defl { 244 | for elem in &mut array[arraysz..] { 245 | set(elem, defl.clone()); 246 | } 247 | } else { 248 | bail!(ErrorKind::InvalidLen(arraysz)); 249 | } 250 | } 251 | 252 | // Mop up unused array entries on the wire 253 | if arraysz > array.len() { 254 | for _ in array.len()..arraysz { 255 | let (_, sz) = T::unpack(input)?; 256 | rsz += sz; 257 | } 258 | } 259 | assert!(rsz % 4 == 0); 260 | 261 | Ok(rsz) 262 | } 263 | 264 | /// Unpack a fixed-sized opaque array 265 | /// 266 | /// Unpack a fixed-size array of raw bytes. The results are placed in `bytes`, but the actual wire-size of 267 | /// the array is `bytesz`. If the supplied `bytes` is too large, the remainer is filled in with 0x00; 268 | /// if it is too small, the excess elements are discarded. 269 | /// 270 | /// All the bytes in `bytes` will be initialized after a successful call. 271 | pub fn unpack_opaque_array( 272 | input: &mut In, 273 | bytes: &mut [u8], 274 | bytesz: usize, 275 | ) -> Result { 276 | let sz = min(bytesz, bytes.len()); 277 | let mut rsz = 0; 278 | 279 | while rsz < sz { 280 | let r = input.read(&mut bytes[rsz..])?; 281 | rsz += r; 282 | } 283 | 284 | // Fill in excess 285 | if sz < bytes.len() { 286 | for b in &mut bytes[sz..] { 287 | *b = 0; 288 | } 289 | } 290 | 291 | // Mop up unused data on the wire and padding 292 | let p = padding(bytesz).len(); 293 | if bytes.len() < bytesz + p { 294 | for _ in bytes.len()..(bytesz + p) { 295 | let _ = input.read_u8()?; 296 | rsz += 1; 297 | } 298 | } 299 | 300 | Ok(rsz) 301 | } 302 | 303 | /// Unpack a (perhaps) length-limited array 304 | pub fn unpack_flex>( 305 | input: &mut In, 306 | maxsz: Option, 307 | ) -> Result<(Vec, usize)> { 308 | let (elems, mut sz) = Unpack::unpack(input)?; 309 | 310 | if maxsz.map_or(false, |m| elems > m) { 311 | bail!(ErrorKind::InvalidLen(maxsz.unwrap())); 312 | } 313 | 314 | let mut out = Vec::with_capacity(elems); 315 | 316 | for _ in 0..elems { 317 | let (e, esz) = Unpack::unpack(input)?; 318 | out.push(e); 319 | sz += esz; 320 | } 321 | 322 | let p = padding(sz); 323 | for _ in 0..p.len() { 324 | let _ = input.read_u8()?; 325 | } 326 | sz += p.len(); 327 | 328 | Ok((out, sz)) 329 | } 330 | 331 | /// Unpack a (perhaps) length-limited opaque array 332 | /// 333 | /// Unpack an XDR encoded array of bytes, with an optional maximum length. 334 | pub fn unpack_opaque_flex( 335 | input: &mut In, 336 | maxsz: Option, 337 | ) -> Result<(Vec, usize)> { 338 | let (elems, mut sz) = Unpack::unpack(input)?; 339 | 340 | if maxsz.map_or(false, |m| elems > m) { 341 | bail!(ErrorKind::InvalidLen(maxsz.unwrap())); 342 | } 343 | 344 | let mut out = Vec::with_capacity(elems); 345 | 346 | sz += input.take(elems as u64).read_to_end(&mut out)?; 347 | 348 | let p = padding(sz); 349 | for _ in 0..p.len() { 350 | let _ = input.read_u8()?; 351 | } 352 | sz += p.len(); 353 | 354 | Ok((out, sz)) 355 | } 356 | 357 | /// Unpack (perhaps) length-limited string 358 | pub fn unpack_string(input: &mut In, maxsz: Option) -> Result<(String, usize)> { 359 | let (v, sz) = unpack_opaque_flex(input, maxsz)?; 360 | 361 | String::from_utf8(v).map_err(Error::from).map(|s| (s, sz)) 362 | } 363 | 364 | /// Basic packing trait. 365 | /// 366 | /// This trait is used to implement XDR packing any Rust type into a 367 | /// `Write` stream. It returns the number of bytes the encoding took. 368 | /// 369 | /// This crate provides a number of implementations for all the basic 370 | /// XDR types, and generated code will generally compose them to pack 371 | /// structures, unions, etc. 372 | /// 373 | /// Streams generated by `Pack` can be consumed by `Unpack`. 374 | pub trait Pack { 375 | fn pack(&self, out: &mut Out) -> Result; 376 | } 377 | 378 | #[cfg(feature = "bytecodec")] 379 | impl Pack for u8 { 380 | #[inline] 381 | fn pack(&self, out: &mut Out) -> Result { 382 | out.write_u32::(*self as u32) 383 | .map_err(Error::from) 384 | .map(|_| 4) 385 | } 386 | } 387 | 388 | #[cfg(feature = "bytecodec")] 389 | impl Pack for i8 { 390 | #[inline] 391 | fn pack(&self, out: &mut Out) -> Result { 392 | out.write_i32::(*self as i32) 393 | .map_err(Error::from) 394 | .map(|_| 4) 395 | } 396 | } 397 | 398 | impl Pack for u32 { 399 | #[inline] 400 | fn pack(&self, out: &mut Out) -> Result { 401 | out.write_u32::(*self).map_err(Error::from).map( 402 | |_| 4, 403 | ) 404 | } 405 | } 406 | 407 | impl Pack for i32 { 408 | #[inline] 409 | fn pack(&self, out: &mut Out) -> Result { 410 | out.write_i32::(*self).map_err(Error::from).map( 411 | |_| 4, 412 | ) 413 | } 414 | } 415 | 416 | impl Pack for u64 { 417 | #[inline] 418 | fn pack(&self, out: &mut Out) -> Result { 419 | out.write_u64::(*self).map_err(Error::from).map( 420 | |_| 8, 421 | ) 422 | } 423 | } 424 | 425 | impl Pack for i64 { 426 | #[inline] 427 | fn pack(&self, out: &mut Out) -> Result { 428 | out.write_i64::(*self).map_err(Error::from).map( 429 | |_| 8, 430 | ) 431 | } 432 | } 433 | 434 | impl Pack for f32 { 435 | #[inline] 436 | fn pack(&self, out: &mut Out) -> Result { 437 | out.write_f32::(*self).map_err(Error::from).map( 438 | |_| 4, 439 | ) 440 | } 441 | } 442 | 443 | impl Pack for f64 { 444 | #[inline] 445 | fn pack(&self, out: &mut Out) -> Result { 446 | out.write_f64::(*self).map_err(Error::from).map( 447 | |_| 8, 448 | ) 449 | } 450 | } 451 | 452 | impl Pack for bool { 453 | #[inline] 454 | fn pack(&self, out: &mut Out) -> Result { 455 | (*self as u32).pack(out) 456 | } 457 | } 458 | 459 | impl Pack for () { 460 | #[inline] 461 | fn pack(&self, _out: &mut Out) -> Result { 462 | Ok(0) 463 | } 464 | } 465 | 466 | impl Pack for usize { 467 | #[inline] 468 | fn pack(&self, out: &mut Out) -> Result { 469 | (*self as u32).pack(out) 470 | } 471 | } 472 | 473 | impl> Pack for [T] { 474 | fn pack(&self, out: &mut Out) -> Result { 475 | let len = self.len(); 476 | 477 | let mut sz = len.pack(out)?; 478 | for it in self { 479 | sz += it.pack(out)?; 480 | } 481 | 482 | let p = padding(sz); 483 | if p.len() > 0 { 484 | out.write_all(p)?; 485 | sz += p.len(); 486 | } 487 | 488 | Ok(sz) 489 | } 490 | } 491 | 492 | impl> Pack for Vec { 493 | #[inline] 494 | fn pack(&self, out: &mut Out) -> Result { 495 | if self.len() > u32::max_value() as usize { 496 | return Err(ErrorKind::InvalidLen(self.len()).into()); 497 | } 498 | 499 | (&self[..]).pack(out) 500 | } 501 | } 502 | 503 | impl<'a, Out: Write> Pack for Opaque<'a> { 504 | fn pack(&self, out: &mut Out) -> Result { 505 | let mut sz; 506 | let data: &[u8] = self.0.borrow(); 507 | 508 | if data.len() > u32::max_value() as usize { 509 | return Err(ErrorKind::InvalidLen(data.len()).into()); 510 | } 511 | 512 | sz = data.len().pack(out)?; 513 | 514 | out.write_all(data)?; 515 | sz += data.len(); 516 | 517 | let p = padding(sz); 518 | if p.len() > 0 { 519 | out.write_all(p)?; 520 | sz += p.len(); 521 | } 522 | 523 | Ok(sz) 524 | } 525 | } 526 | 527 | impl Pack for str { 528 | #[inline] 529 | fn pack(&self, out: &mut Out) -> Result { 530 | Opaque::borrowed(self.as_bytes()).pack(out) 531 | } 532 | } 533 | 534 | impl> Pack for Option { 535 | fn pack(&self, out: &mut Out) -> Result { 536 | match self { 537 | &None => false.pack(out), 538 | &Some(ref v) => { 539 | let sz = true.pack(out)? + v.pack(out)?; 540 | Ok(sz) 541 | } 542 | } 543 | } 544 | } 545 | 546 | impl> Pack for Box { 547 | fn pack(&self, out: &mut Out) -> Result { 548 | let t: &T = self.borrow(); 549 | t.pack(out) 550 | } 551 | } 552 | 553 | impl<'a, Out: Write, T> Pack for Cow<'a, T> 554 | where 555 | T: 'a + Pack + ToOwned, 556 | { 557 | fn pack(&self, out: &mut Out) -> Result { 558 | let t: &T = self.borrow(); 559 | t.pack(out) 560 | } 561 | } 562 | 563 | /// Deserialization (unpacking) helper function 564 | /// 565 | /// This function will read encoded bytes from `input` (a `Read` 566 | /// implementation) and return a fully constructed type (or an 567 | /// error). This relies on type inference to determine which type is 568 | /// to be unpacked, so its up to the calling envionment to clarify 569 | /// this. (Generally it falls out quite naturally.) 570 | pub fn unpack>(input: &mut In) -> Result { 571 | T::unpack(input).map(|(v, _)| v) 572 | } 573 | 574 | /// Basic unpacking trait 575 | /// 576 | /// This trait is used to unpack a type from an XDR encoded byte 577 | /// stream (encoded with `Pack`). It returns the decoded instance and 578 | /// the number of bytes consumed from the input. 579 | /// 580 | /// This crate provides implementations for all the basic XDR types, 581 | /// as well as for arrays. 582 | pub trait Unpack: Sized { 583 | fn unpack(input: &mut In) -> Result<(Self, usize)>; 584 | } 585 | 586 | #[cfg(feature = "bytecodec")] 587 | impl Unpack for u8 { 588 | #[inline] 589 | fn unpack(input: &mut In) -> Result<(Self, usize)> { 590 | input.read_u32::().map_err(Error::from).map( 591 | |v| { 592 | (v as u8, 4) 593 | }, 594 | ) 595 | } 596 | } 597 | 598 | #[cfg(feature = "bytecodec")] 599 | impl Unpack for i8 { 600 | #[inline] 601 | fn unpack(input: &mut In) -> Result<(Self, usize)> { 602 | input.read_i32::().map_err(Error::from).map( 603 | |v| { 604 | (v as i8, 4) 605 | }, 606 | ) 607 | } 608 | } 609 | 610 | impl Unpack for u32 { 611 | #[inline] 612 | fn unpack(input: &mut In) -> Result<(Self, usize)> { 613 | input.read_u32::().map_err(Error::from).map( 614 | |v| (v, 4), 615 | ) 616 | } 617 | } 618 | 619 | impl Unpack for i32 { 620 | #[inline] 621 | fn unpack(input: &mut In) -> Result<(Self, usize)> { 622 | input.read_i32::().map_err(Error::from).map( 623 | |v| (v, 4), 624 | ) 625 | } 626 | } 627 | 628 | impl Unpack for u64 { 629 | #[inline] 630 | fn unpack(input: &mut In) -> Result<(Self, usize)> { 631 | input.read_u64::().map_err(Error::from).map( 632 | |v| (v, 8), 633 | ) 634 | } 635 | } 636 | 637 | impl Unpack for i64 { 638 | #[inline] 639 | fn unpack(input: &mut In) -> Result<(Self, usize)> { 640 | input.read_i64::().map_err(Error::from).map( 641 | |v| (v, 8), 642 | ) 643 | } 644 | } 645 | 646 | impl Unpack for f32 { 647 | fn unpack(input: &mut In) -> Result<(Self, usize)> { 648 | input.read_f32::().map_err(Error::from).map( 649 | |v| (v, 4), 650 | ) 651 | } 652 | } 653 | 654 | impl Unpack for f64 { 655 | fn unpack(input: &mut In) -> Result<(Self, usize)> { 656 | input.read_f64::().map_err(Error::from).map( 657 | |v| (v, 8), 658 | ) 659 | } 660 | } 661 | 662 | impl Unpack for bool { 663 | #[inline] 664 | fn unpack(input: &mut In) -> Result<(Self, usize)> { 665 | i32::unpack(input).and_then(|(v, sz)| match v { 666 | 0 => Ok((false, sz)), 667 | 1 => Ok((true, sz)), 668 | v => Err(ErrorKind::InvalidEnum(v).into()), 669 | }) 670 | } 671 | } 672 | 673 | impl Unpack for () { 674 | #[inline] 675 | fn unpack(_input: &mut In) -> Result<(Self, usize)> { 676 | Ok(((), 0)) 677 | } 678 | } 679 | 680 | impl Unpack for usize { 681 | #[inline] 682 | fn unpack(input: &mut In) -> Result<(Self, usize)> { 683 | u32::unpack(input).map(|(v, sz)| (v as usize, sz)) 684 | } 685 | } 686 | 687 | impl> Unpack for Vec { 688 | fn unpack(input: &mut In) -> Result<(Self, usize)> { 689 | unpack_flex(input, None) 690 | } 691 | } 692 | 693 | impl Unpack for String { 694 | fn unpack(input: &mut In) -> Result<(Self, usize)> { 695 | let (v, sz) = unpack_opaque_flex(input, None)?; 696 | String::from_utf8(v).map_err(Error::from).map(|s| (s, sz)) 697 | } 698 | } 699 | 700 | impl<'a, In: Read> Unpack for Opaque<'a> { 701 | fn unpack(input: &mut In) -> Result<(Self, usize)> { 702 | let (len, mut sz) = usize::unpack(input)?; 703 | let mut v = Vec::new(); 704 | sz += input.by_ref().take(len as u64).read_to_end(&mut v)?; 705 | 706 | let p = padding(sz); 707 | for _ in 0..p.len() { 708 | let _ = input.read_u8()?; 709 | sz += 1; 710 | } 711 | 712 | Ok((Opaque(Cow::Owned(v)), sz)) 713 | } 714 | } 715 | 716 | impl> Unpack for Option { 717 | fn unpack(input: &mut In) -> Result<(Self, usize)> { 718 | let (have, mut sz) = Unpack::unpack(input)?; 719 | let ret = if have { 720 | let (v, osz) = Unpack::unpack(input)?; 721 | sz += osz; 722 | Some(v) 723 | } else { 724 | None 725 | }; 726 | Ok((ret, sz)) 727 | } 728 | } 729 | 730 | impl> Unpack for Box { 731 | fn unpack(input: &mut In) -> Result<(Self, usize)> { 732 | let (b, sz) = Unpack::unpack(input)?; 733 | Ok((Box::new(b), sz)) 734 | } 735 | } 736 | 737 | impl<'a, In: Read, T> Unpack for Cow<'a, T> 738 | where 739 | T: 'a + Unpack + ToOwned, 740 | { 741 | fn unpack(input: &mut In) -> Result<(Self, usize)> { 742 | let (b, sz) = Unpack::unpack(input)?; 743 | Ok((Cow::Owned(b), sz)) 744 | } 745 | } 746 | -------------------------------------------------------------------------------- /xdr-codec/src/record.rs: -------------------------------------------------------------------------------- 1 | //! XDR record marking 2 | //! 3 | //! This module implements wrappers for `Write` and `BufRead` which 4 | //! implement "Record Marking" from [RFC1831](https://tools.ietf.org/html/rfc1831.html#section-10), 5 | //! used for encoding XDR structures onto a bytestream such as TCP. 6 | //! 7 | //! The format is simple - each record is broken up into one or more 8 | //! record fragments. Each record fragment is prefixed with a 32-bit 9 | //! big-endian value. The low 31 bits is the fragment size, and the 10 | //! top bit is the "end of record" marker, indicating the last 11 | //! fragment of the record. 12 | //! 13 | //! There's no magic number or other way to determine whether a stream 14 | //! is using record marking; both ends must agree. 15 | use std::io::{self, BufRead, Read, Write}; 16 | use std::cmp::min; 17 | 18 | use error::*; 19 | 20 | use super::{Error, pack, unpack}; 21 | 22 | const LAST_REC: u32 = 1u32 << 31; 23 | 24 | fn mapioerr(xdrerr: Error) -> io::Error { 25 | match xdrerr { 26 | Error(ErrorKind::IOError(ioerr), _) => ioerr, 27 | other => io::Error::new(io::ErrorKind::Other, other), 28 | } 29 | } 30 | 31 | /// Read records from a bytestream. 32 | /// 33 | /// Reads will read up to the end of the current fragment, and not 34 | /// beyond. The `BufRead` trait doesn't otherwise allow for record 35 | /// boundaries to be deliniated. Callers can use the `eor` method to 36 | /// determine record ends. 37 | #[derive(Debug)] 38 | pub struct XdrRecordReader { 39 | size: usize, // record size 40 | consumed: usize, // bytes consumed 41 | eor: bool, // is last record 42 | 43 | reader: R, // reader 44 | } 45 | 46 | impl XdrRecordReader { 47 | /// Wrapper a record reader around an existing implementation of 48 | /// `BufRead`, such as `BufReader`. 49 | pub fn new(rd: R) -> XdrRecordReader { 50 | XdrRecordReader { 51 | size: 0, 52 | consumed: 0, 53 | eor: false, 54 | reader: rd, 55 | } 56 | } 57 | 58 | // read next record, returns true on EOF 59 | fn nextrec(&mut self) -> io::Result { 60 | assert_eq!(self.consumed, self.size); 61 | 62 | let rechdr: u32 = match unpack(&mut self.reader) { 63 | Ok(v) => v, 64 | Err(Error(ErrorKind::IOError(ref err), _)) 65 | if err.kind() == io::ErrorKind::UnexpectedEof => return Ok(true), 66 | Err(e) => return Err(mapioerr(e)), 67 | }; 68 | 69 | self.size = (rechdr & !LAST_REC) as usize; 70 | self.consumed = 0; 71 | self.eor = (rechdr & LAST_REC) != 0; 72 | 73 | Ok(false) 74 | } 75 | 76 | fn totremains(&self) -> usize { 77 | self.size - self.consumed 78 | } 79 | 80 | /// Current fragment is the end of the record. 81 | pub fn eor(&self) -> bool { 82 | self.eor 83 | } 84 | } 85 | 86 | impl Read for XdrRecordReader { 87 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 88 | let nread = { 89 | let data = self.fill_buf()?; 90 | let len = min(buf.len(), data.len()); 91 | 92 | (&data[..len]).read(buf)? 93 | }; 94 | 95 | self.consume(nread); 96 | Ok(nread) 97 | } 98 | } 99 | 100 | impl BufRead for XdrRecordReader { 101 | fn fill_buf(&mut self) -> io::Result<&[u8]> { 102 | while self.totremains() == 0 { 103 | if self.nextrec()? { 104 | return Ok(&[]); 105 | } 106 | } 107 | 108 | let remains = self.totremains(); 109 | let data = self.reader.fill_buf()?; 110 | Ok(&data[..min(data.len(), remains)]) 111 | } 112 | 113 | fn consume(&mut self, sz: usize) { 114 | assert!(sz <= self.totremains()); 115 | self.consumed += sz; 116 | self.reader.consume(sz); 117 | } 118 | } 119 | 120 | impl IntoIterator for XdrRecordReader { 121 | type Item = io::Result>; 122 | type IntoIter = XdrRecordReaderIter; 123 | 124 | fn into_iter(self) -> Self::IntoIter { 125 | XdrRecordReaderIter(Some(self)) 126 | } 127 | } 128 | 129 | /// Iterator over records in the stream. 130 | /// 131 | /// Each iterator result is either: 132 | /// 133 | /// * A complete record, or 134 | /// * an IO error. 135 | /// 136 | /// It will return an IO error once, and then end the iterator. 137 | /// A short read or an unterminated record will also end the iterator. It will not return a partial 138 | /// record. 139 | #[derive(Debug)] 140 | pub struct XdrRecordReaderIter(Option>); 141 | 142 | impl Iterator for XdrRecordReaderIter { 143 | type Item = io::Result>; 144 | 145 | fn next(&mut self) -> Option { 146 | if let Some(mut rr) = self.0.take() { 147 | let mut buf = Vec::new(); 148 | 149 | // loop over fragments until we get a complete record 150 | loop { 151 | // Do we need next fragment? 152 | if rr.totremains() == 0 { 153 | match rr.nextrec() { 154 | Err(e) => return Some(Err(e)), // IO error 155 | Ok(true) => return None, // EOF 156 | Ok(false) => (), // keep going 157 | } 158 | } 159 | 160 | let remains = rr.totremains(); 161 | let eor = rr.eor(); 162 | 163 | match rr.by_ref().take(remains as u64).read_to_end(&mut buf) { 164 | Ok(sz) if sz == remains => (), // OK, keep going 165 | Ok(_) => return None, // short read 166 | Err(e) => return Some(Err(e)), // error 167 | }; 168 | 169 | if eor { 170 | break; 171 | } 172 | } 173 | self.0 = Some(rr); 174 | Some(Ok(buf)) 175 | } else { 176 | None 177 | } 178 | } 179 | } 180 | 181 | const WRBUF: usize = 65536; 182 | 183 | /// Write records into a bytestream. 184 | /// 185 | /// Flushes the current buffer as end of record when destroyed. 186 | pub struct XdrRecordWriter { 187 | buf: Vec, // accumulated record fragment 188 | bufsz: usize, // max fragment size 189 | eor: bool, // last fragment was eor 190 | writer: W, // writer we're passing on to 191 | } 192 | 193 | impl XdrRecordWriter { 194 | /// Create a new `XdrRecordWriter` wrapped around a `Write` 195 | /// implementation, using a default buffer size (64k). 196 | pub fn new(w: W) -> XdrRecordWriter { 197 | XdrRecordWriter::with_buffer(w, WRBUF) 198 | } 199 | 200 | /// Create an instance with a specific buffer size. Panics if the 201 | /// size is zero. 202 | pub fn with_buffer(w: W, bufsz: usize) -> XdrRecordWriter { 203 | if bufsz == 0 { 204 | panic!("bufsz must be non-zero") 205 | } 206 | XdrRecordWriter { 207 | buf: Vec::with_capacity(bufsz), 208 | bufsz: bufsz, 209 | eor: false, 210 | writer: w, 211 | } 212 | } 213 | 214 | /// Flush the current buffer. If `eor` is true, the end of record 215 | /// marker is set. 216 | pub fn flush_eor(&mut self, eor: bool) -> io::Result<()> { 217 | if !eor && self.buf.len() == 0 { 218 | return Ok(()); 219 | } 220 | 221 | let rechdr = self.buf.len() as u32 | (if eor { LAST_REC } else { 0 }); 222 | 223 | pack(&rechdr, &mut self.writer).map_err(mapioerr)?; 224 | let _ = self.writer.write_all(&self.buf).map(|_| ())?; 225 | self.buf.truncate(0); 226 | 227 | self.eor = eor; 228 | self.writer.flush() 229 | } 230 | } 231 | 232 | impl Drop for XdrRecordWriter { 233 | fn drop(&mut self) { 234 | if self.buf.len() > 0 || !self.eor { 235 | let _ = self.flush_eor(true); 236 | } 237 | } 238 | } 239 | 240 | impl Write for XdrRecordWriter { 241 | fn write(&mut self, buf: &[u8]) -> io::Result { 242 | let mut off = 0; 243 | 244 | while off < buf.len() { 245 | let chunk = &buf[off..off + min(buf.len() - off, self.bufsz)]; 246 | if self.buf.len() + chunk.len() > self.bufsz { 247 | self.flush()?; 248 | } 249 | 250 | self.buf.extend(chunk); 251 | off += chunk.len(); 252 | } 253 | 254 | Ok(off) 255 | } 256 | 257 | fn flush(&mut self) -> io::Result<()> { 258 | self.flush_eor(false) 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /xdr-codec/src/test.rs: -------------------------------------------------------------------------------- 1 | // Don't rustfmt in here to avoid trashing vec![] formatting 2 | #![cfg_attr(rustfmt, rustfmt_skip)] 3 | 4 | use std::io::Cursor; 5 | use super::{Error, ErrorKind, Pack, Unpack, Opaque, 6 | pack_flex, pack_opaque_flex, pack_string, pack_array, pack_opaque_array, 7 | unpack_array, unpack_opaque_array, unpack_string, unpack_flex, unpack_opaque_flex}; 8 | 9 | 10 | #[cfg(feature = "bytecodec")] 11 | #[test] 12 | fn basic_8() { 13 | { 14 | let mut out = Cursor::new(Vec::new()); 15 | 16 | assert_eq!(0u8.pack(&mut out).unwrap(), 4); 17 | assert_eq!(100u8.pack(&mut out).unwrap(), 4); 18 | assert_eq!((-1i8).pack(&mut out).unwrap(), 4); 19 | 20 | let v = out.into_inner(); 21 | 22 | assert_eq!(v.len(), 12); 23 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x00, 24 | 0x00, 0x00, 0x00, 0x64, 25 | 0xff, 0xff, 0xff, 0xff, ]); 26 | 27 | let mut input = Cursor::new(v); 28 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (0u8, 4)); 29 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (100u8, 4)); 30 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (-1i8, 4)); 31 | } 32 | 33 | { 34 | let mut out = Cursor::new(Vec::new()); 35 | 36 | assert_eq!(0i8.pack(&mut out).unwrap(), 4); 37 | assert_eq!((-123i8).pack(&mut out).unwrap(), 4); 38 | assert_eq!((-128i8).pack(&mut out).unwrap(), 4); 39 | 40 | let v = out.into_inner(); 41 | 42 | assert_eq!(v.len(), 12); 43 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x00, 44 | 0xff, 0xff, 0xff, 0x85, 45 | 0xff, 0xff, 0xff, 0x80 ]); 46 | 47 | let mut input = Cursor::new(v); 48 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (0i8, 4)); 49 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (-123i8, 4)); 50 | assert_eq!(Unpack::unpack(&mut input).unwrap(), ((1<<7) as i8, 4)); 51 | } 52 | } 53 | 54 | #[test] 55 | fn basic_32() { 56 | { 57 | let mut out = Cursor::new(Vec::new()); 58 | 59 | assert_eq!(0u32.pack(&mut out).unwrap(), 4); 60 | assert_eq!(1000u32.pack(&mut out).unwrap(), 4); 61 | assert_eq!(823987423u32.pack(&mut out).unwrap(), 4); 62 | 63 | let v = out.into_inner(); 64 | 65 | assert_eq!(v.len(), 12); 66 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x00, 67 | 0x00, 0x00, 0x03, 0xe8, 68 | 0x31, 0x1d, 0x0c, 0xdf, ]); 69 | 70 | let mut input = Cursor::new(v); 71 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (0u32, 4)); 72 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (1000u32, 4)); 73 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (823987423u32, 4)); 74 | } 75 | 76 | { 77 | let mut out = Cursor::new(Vec::new()); 78 | 79 | assert_eq!(0i32.pack(&mut out).unwrap(), 4); 80 | assert_eq!((-1238i32).pack(&mut out).unwrap(), 4); 81 | assert_eq!(((1i32<<31) as i32).pack(&mut out).unwrap(), 4); 82 | 83 | let v = out.into_inner(); 84 | 85 | assert_eq!(v.len(), 12); 86 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x00, 87 | 0xff, 0xff, 0xfb, 0x2a, 88 | 0x80, 0x00, 0x00, 0x00 ]); 89 | 90 | let mut input = Cursor::new(v); 91 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (0i32, 4)); 92 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (-1238i32, 4)); 93 | assert_eq!(Unpack::unpack(&mut input).unwrap(), ((1<<31) as i32, 4)); 94 | } 95 | } 96 | 97 | #[test] 98 | fn basic_64() { 99 | { 100 | let mut out = Cursor::new(Vec::new()); 101 | 102 | assert_eq!(0u64.pack(&mut out).unwrap(), 8); 103 | assert_eq!(0x0011223344556677u64.pack(&mut out).unwrap(), 8); 104 | assert_eq!(0xff00ff00ff00ff00u64.pack(&mut out).unwrap(), 8); 105 | 106 | let v = out.into_inner(); 107 | 108 | assert_eq!(v.len(), 24); 109 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 110 | 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 111 | 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00 ]); 112 | 113 | let mut input = Cursor::new(v); 114 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (0u64, 8)); 115 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (4822678189205111u64, 8)); 116 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (18374966859414961920u64, 8)); 117 | } 118 | 119 | { 120 | let mut out = Cursor::new(Vec::new()); 121 | 122 | assert_eq!(0i64.pack(&mut out).unwrap(), 8); 123 | assert_eq!((-2938928374982749237i64).pack(&mut out).unwrap(), 8); 124 | assert_eq!(((1i64<<63) as i64).pack(&mut out).unwrap(), 8); 125 | 126 | let v = out.into_inner(); 127 | 128 | assert_eq!(v.len(), 24); 129 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 130 | 0xd7, 0x36, 0xd4, 0x36, 0xcc, 0xd6, 0x53, 0xcb, 131 | 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]); 132 | 133 | let mut input = Cursor::new(v); 134 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (0i64, 8)); 135 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (-2938928374982749237i64, 8)); 136 | assert_eq!(Unpack::unpack(&mut input).unwrap(), ((1i64<<63) as i64, 8)); 137 | } 138 | } 139 | 140 | #[test] 141 | fn basic_bool() { 142 | let mut out = Cursor::new(Vec::new()); 143 | 144 | assert_eq!(true.pack(&mut out).unwrap(), 4); 145 | assert_eq!(false.pack(&mut out).unwrap(), 4); 146 | 147 | let v = out.into_inner(); 148 | 149 | assert_eq!(v.len(), 8); 150 | assert_eq!(v, vec![0, 0, 0, 1, 0, 0, 0, 0]); 151 | 152 | let mut input = Cursor::new(v); 153 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (true, 4)); 154 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (false, 4)); 155 | 156 | let bad = vec![0, 0, 0, 2]; 157 | let mut input = Cursor::new(bad); 158 | match bool::unpack(&mut input) { 159 | Err(Error(ErrorKind::InvalidEnum(_), _)) => (), 160 | res => panic!("bad result {:?}", res), 161 | } 162 | } 163 | 164 | #[test] 165 | fn basic_string() { 166 | { 167 | let mut out = Cursor::new(Vec::new()); 168 | 169 | assert_eq!("foo!".pack(&mut out).unwrap(), 8); 170 | 171 | let v = out.into_inner(); 172 | 173 | assert_eq!(v.len(), 8); 174 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x04, 0x66, 0x6f, 0x6f, 0x21]); 175 | 176 | let mut input = Cursor::new(v); 177 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (String::from("foo!"), 8)); 178 | } 179 | 180 | { 181 | let mut out = Cursor::new(Vec::new()); 182 | 183 | assert_eq!("foo".pack(&mut out).unwrap(), 8); 184 | 185 | let v = out.into_inner(); 186 | 187 | assert_eq!(v.len(), 8); 188 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x03, 0x66, 0x6f, 0x6f, 0x00]); 189 | 190 | let mut input = Cursor::new(v); 191 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (String::from("foo"), 8)); 192 | } 193 | 194 | { 195 | let mut out = Cursor::new(Vec::new()); 196 | 197 | assert_eq!("foobar".pack(&mut out).unwrap(), 12); 198 | assert_eq!("piff".pack(&mut out).unwrap(), 8); 199 | 200 | let v = out.into_inner(); 201 | 202 | assert_eq!(v.len(), 20); 203 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x06, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x00, 0x00, 204 | 0x00, 0x00, 0x00, 0x04, 0x70, 0x69, 0x66, 0x66]); 205 | 206 | let mut input = Cursor::new(v); 207 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (String::from("foobar"), 12)); 208 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (String::from("piff"), 8)); 209 | } 210 | 211 | { 212 | let mut out = Cursor::new(Vec::new()); 213 | 214 | assert_eq!(pack_string("foo!", Some(10), &mut out).unwrap(), 8); 215 | 216 | let v = out.into_inner(); 217 | 218 | assert_eq!(v.len(), 8); 219 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x04, 0x66, 0x6f, 0x6f, 0x21]); 220 | 221 | let mut input = Cursor::new(v); 222 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (String::from("foo!"), 8)); 223 | } 224 | 225 | { 226 | let mut out = Cursor::new(Vec::new()); 227 | 228 | match pack_string("foo!", Some(2), &mut out) { 229 | Err(Error(ErrorKind::InvalidLen(_), _)) => (), 230 | e => panic!("bad result {:?}", e), 231 | } 232 | } 233 | } 234 | 235 | #[test] 236 | fn basic_flex() { 237 | { 238 | let mut out = Cursor::new(Vec::new()); 239 | 240 | assert_eq!(vec![0x11u32, 0x22, 0x33, 0x44].pack(&mut out).unwrap(), 4*4 + 4); 241 | 242 | let v = out.into_inner(); 243 | 244 | assert_eq!(v.len(), 4*4 + 4); 245 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x04, 246 | 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x22, 247 | 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x44]); 248 | 249 | let mut input = Cursor::new(v); 250 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (vec![0x11u32, 0x22, 0x33, 0x44], 4*4+4)); 251 | } 252 | 253 | { 254 | let mut out = Cursor::new(Vec::new()); 255 | 256 | assert_eq!(vec![0x11u32, 0x22].pack(&mut out).unwrap(), 2*4+4); 257 | 258 | let v = out.into_inner(); 259 | 260 | assert_eq!(v.len(), 2*4+4); 261 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x02, 262 | 0x00, 0x00, 0x00, 0x11, 263 | 0x00, 0x00, 0x00, 0x22]); 264 | 265 | let mut input = Cursor::new(v); 266 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (vec![0x11u32, 0x22], 4*2+4)); 267 | } 268 | 269 | { 270 | let mut out = Cursor::new(Vec::new()); 271 | 272 | assert_eq!(vec![0x11u32, 0x22, 0x00].pack(&mut out).unwrap(), 3*4+4); 273 | 274 | let v = out.into_inner(); 275 | 276 | assert_eq!(v.len(), 3*4+4); 277 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x03, 278 | 0x00, 0x00, 0x00, 0x11, 279 | 0x00, 0x00, 0x00, 0x22, 280 | 0x00, 0x00, 0x00, 0x00]); 281 | 282 | let mut input = Cursor::new(v); 283 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (vec![0x11u32, 0x22, 0x00], 3*4+4)); 284 | } 285 | 286 | { 287 | let mut out = Cursor::new(Vec::new()); 288 | 289 | assert_eq!(vec![0x11u32, 0x22, 0x33].pack(&mut out).unwrap(), 3*4+4); 290 | 291 | let v = out.into_inner(); 292 | 293 | assert_eq!(v.len(), 3*4+4); 294 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x03, 295 | 0x00, 0x00, 0x00, 0x11, 296 | 0x00, 0x00, 0x00, 0x22, 297 | 0x00, 0x00, 0x00, 0x33]); 298 | 299 | let mut input = Cursor::new(v); 300 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (vec![0x11u32, 0x22, 0x33], 3*4+4)); 301 | } 302 | 303 | { 304 | let mut out = Cursor::new(Vec::new()); 305 | 306 | assert_eq!(vec![0x11u32, 0x22, 0x33, 0x44, 0x55].pack(&mut out).unwrap(), 4*5+4); 307 | 308 | let v = out.into_inner(); 309 | 310 | assert_eq!(v.len(), 4*5+4); 311 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x05, 312 | 0x00, 0x00, 0x00, 0x11, 313 | 0x00, 0x00, 0x00, 0x22, 314 | 0x00, 0x00, 0x00, 0x33, 315 | 0x00, 0x00, 0x00, 0x44, 316 | 0x00, 0x00, 0x00, 0x55]); 317 | 318 | let mut input = Cursor::new(v); 319 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (vec![0x11u32, 0x22, 0x33, 0x44, 0x55], 5*4+4)); 320 | } 321 | 322 | { 323 | let mut out = Cursor::new(Vec::new()); 324 | 325 | assert_eq!(pack_flex(&vec![0x11u32, 0x22, 0x33, 0x44, 0x55], Some(10), &mut out).unwrap(), 4*5+4); 326 | 327 | let v = out.into_inner(); 328 | 329 | assert_eq!(v.len(), 4*5+4); 330 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x05, 331 | 0x00, 0x00, 0x00, 0x11, 332 | 0x00, 0x00, 0x00, 0x22, 333 | 0x00, 0x00, 0x00, 0x33, 334 | 0x00, 0x00, 0x00, 0x44, 335 | 0x00, 0x00, 0x00, 0x55]); 336 | 337 | let mut input = Cursor::new(v); 338 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (vec![0x11u32, 0x22, 0x33, 0x44, 0x55], 5*4+4)); 339 | } 340 | 341 | { 342 | let mut out = Cursor::new(Vec::new()); 343 | 344 | match pack_flex(&vec![0x11u32, 0x22, 0x33, 0x44, 0x55], Some(4), &mut out) { 345 | Err(Error(ErrorKind::InvalidLen(_), _)) => (), 346 | e => panic!("bad result {:?}", e) 347 | } 348 | } 349 | } 350 | 351 | #[test] 352 | fn basic_opaque_flex() { 353 | { 354 | let mut out = Cursor::new(Vec::new()); 355 | 356 | assert_eq!(Opaque::borrowed(&vec![0x11u8, 0x22, 0x33, 0x44]).pack(&mut out).unwrap(), 8); 357 | 358 | let v = out.into_inner(); 359 | 360 | assert_eq!(v.len(), 8); 361 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x04, 0x11, 0x22, 0x33, 0x44]); 362 | 363 | let mut input = Cursor::new(v); 364 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (Opaque::borrowed(&vec![0x11u8, 0x22, 0x33, 0x44]), 8)); 365 | } 366 | 367 | { 368 | let mut out = Cursor::new(Vec::new()); 369 | 370 | assert_eq!(Opaque::borrowed(&vec![0x11u8, 0x22]).pack(&mut out).unwrap(), 8); 371 | 372 | let v = out.into_inner(); 373 | 374 | assert_eq!(v.len(), 8); 375 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x02, 0x11, 0x22, 0x00, 0x00]); 376 | 377 | let mut input = Cursor::new(v); 378 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (Opaque::borrowed(&vec![0x11u8, 0x22]), 8)); 379 | } 380 | 381 | { 382 | let mut out = Cursor::new(Vec::new()); 383 | 384 | assert_eq!(Opaque::borrowed(&vec![0x11u8, 0x22, 0x00]).pack(&mut out).unwrap(), 8); 385 | 386 | let v = out.into_inner(); 387 | 388 | assert_eq!(v.len(), 8); 389 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x03, 0x11, 0x22, 0x00, 0x00]); 390 | 391 | let mut input = Cursor::new(v); 392 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (Opaque::borrowed(&vec![0x11u8, 0x22, 0x00]), 8)); 393 | } 394 | 395 | { 396 | let mut out = Cursor::new(Vec::new()); 397 | 398 | assert_eq!(Opaque::borrowed(&vec![0x11u8, 0x22, 0x33]).pack(&mut out).unwrap(), 8); 399 | 400 | let v = out.into_inner(); 401 | 402 | assert_eq!(v.len(), 8); 403 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x03, 0x11, 0x22, 0x33, 0x00]); 404 | 405 | let mut input = Cursor::new(v); 406 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (Opaque::borrowed(&vec![0x11u8, 0x22, 0x33]), 8)); 407 | } 408 | 409 | { 410 | let mut out = Cursor::new(Vec::new()); 411 | 412 | assert_eq!(Opaque::borrowed(&vec![0x11u8, 0x22, 0x33, 0x44, 0x55]).pack(&mut out).unwrap(), 12); 413 | 414 | let v = out.into_inner(); 415 | 416 | assert_eq!(v.len(), 12); 417 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x05, 0x11, 0x22, 0x33, 0x44, 0x55, 0x00, 0x00, 0x00]); 418 | 419 | let mut input = Cursor::new(v); 420 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (Opaque::borrowed(&vec![0x11u8, 0x22, 0x33, 0x44, 0x55]), 12)); 421 | } 422 | 423 | { 424 | let mut out = Cursor::new(Vec::new()); 425 | 426 | assert_eq!(pack_opaque_flex(&vec![0x11u8, 0x22, 0x33, 0x44, 0x55], Some(10), &mut out).unwrap(), 12); 427 | 428 | let v = out.into_inner(); 429 | 430 | assert_eq!(v.len(), 12); 431 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x05, 0x11, 0x22, 0x33, 0x44, 0x55, 0x00, 0x00, 0x00]); 432 | 433 | let mut input = Cursor::new(v); 434 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (Opaque::borrowed(&vec![0x11u8, 0x22, 0x33, 0x44, 0x55]), 12)); 435 | } 436 | 437 | { 438 | let mut out = Cursor::new(Vec::new()); 439 | 440 | match pack_opaque_flex(&vec![0x11u8, 0x22, 0x33, 0x44, 0x55], Some(3), &mut out) { 441 | Err(Error(ErrorKind::InvalidLen(_), _)) => (), 442 | e => panic!("bad result {:?}", e), 443 | } 444 | } 445 | } 446 | 447 | #[test] 448 | fn bounded_flex() { 449 | let mut out = Cursor::new(Vec::new()); 450 | 451 | assert_eq!(vec![0x11u32, 0x22, 0x33, 0x44, 0x55].pack(&mut out).unwrap(), 4*5+4); 452 | 453 | let v = out.into_inner(); 454 | 455 | { 456 | let mut input = Cursor::new(v.clone()); 457 | assert_eq!(unpack_flex(&mut input, Some(10)).unwrap(), (vec![0x11u32, 0x22, 0x33, 0x44, 0x55], 5*4+4)); 458 | } 459 | { 460 | let mut input = Cursor::new(v.clone()); 461 | match unpack_flex::<_, Vec>(&mut input, Some(4)) { 462 | Result::Err(Error(ErrorKind::InvalidLen(_), _)) => (), 463 | e => panic!("Unexpected {:?}", e), 464 | } 465 | } 466 | } 467 | 468 | #[test] 469 | fn bounded_opaque_flex() { 470 | let mut out = Cursor::new(Vec::new()); 471 | 472 | assert_eq!(Opaque::borrowed(&vec![0x11u8, 0x22, 0x33, 0x44, 0x55]).pack(&mut out).unwrap(), 12); 473 | 474 | let v = out.into_inner(); 475 | 476 | { 477 | let mut input = Cursor::new(v.clone()); 478 | assert_eq!(unpack_opaque_flex(&mut input, Some(10)).unwrap(), (vec![0x11u8, 0x22, 0x33, 0x44, 0x55], 12)); 479 | } 480 | { 481 | let mut input = Cursor::new(v.clone()); 482 | match unpack_opaque_flex(&mut input, Some(4)) { 483 | Result::Err(Error(ErrorKind::InvalidLen(_), _)) => (), 484 | e => panic!("Unexpected {:?}", e), 485 | } 486 | } 487 | } 488 | 489 | #[test] 490 | fn bounded_string() { 491 | let mut out = Cursor::new(Vec::new()); 492 | 493 | assert_eq!(String::from("hello, world").pack(&mut out).unwrap(), 16); 494 | 495 | let v = out.into_inner(); 496 | 497 | { 498 | let mut input = Cursor::new(v.clone()); 499 | assert_eq!(unpack_string(&mut input, Some(16)).expect("unpack_string failed"), 500 | (String::from("hello, world"), 16)); 501 | } 502 | { 503 | let mut input = Cursor::new(v.clone()); 504 | match unpack_string(&mut input, Some(5)) { 505 | Result::Err(Error(ErrorKind::InvalidLen(_), _)) => (), 506 | e => panic!("Unexpected {:?}", e), 507 | } 508 | } 509 | } 510 | 511 | #[test] 512 | fn basic_array() { 513 | { 514 | let mut out = Cursor::new(Vec::new()); 515 | let a = [0x11u32, 0x22, 0x33]; 516 | 517 | 518 | assert_eq!(pack_array(&a, a.len(), &mut out, Some(&0)).unwrap(), 3*4); 519 | 520 | let v = out.into_inner(); 521 | 522 | assert_eq!(v.len(), 3*4); 523 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x11, 524 | 0x00, 0x00, 0x00, 0x22, 525 | 0x00, 0x00, 0x00, 0x33]); 526 | 527 | let mut input = Cursor::new(v); 528 | let mut b = [0u32; 3]; 529 | let bsz = unpack_array(&mut input, &mut b[..], 3, Some(&0)).expect("unpack failed"); 530 | assert_eq!(bsz, 4*3); 531 | assert_eq!(&a[..], &b[..]); 532 | } 533 | 534 | { 535 | let mut out = Cursor::new(Vec::new()); 536 | let a = [0x11u32, 0x22, 0x33, 0x44]; 537 | 538 | assert_eq!(pack_array(&a, a.len(), &mut out, Some(&0)).unwrap(), 4*4); 539 | 540 | let v = out.into_inner(); 541 | 542 | assert_eq!(v.len(), 4*4); 543 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x22, 544 | 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x44]); 545 | 546 | let mut input = Cursor::new(v); 547 | let mut b = [0u32; 3]; 548 | let bsz = unpack_array(&mut input, &mut b[..], 4, Some(&0)).expect("unpack_array"); 549 | assert_eq!(bsz, 4*4); 550 | assert_eq!(&a[..3], &b[..]); 551 | } 552 | 553 | { 554 | let mut out = Cursor::new(Vec::new()); 555 | let a = [0x11u32, 0x22, 0x33, 0x44, 0x55]; 556 | 557 | assert_eq!(pack_array(&a, a.len(), &mut out, Some(&0)).unwrap(), 5*4); 558 | 559 | let v = out.into_inner(); 560 | 561 | assert_eq!(v.len(), 4*5); 562 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x11, 563 | 0x00, 0x00, 0x00, 0x22, 564 | 0x00, 0x00, 0x00, 0x33, 565 | 0x00, 0x00, 0x00, 0x44, 566 | 0x00, 0x00, 0x00, 0x55]); 567 | 568 | let mut input = Cursor::new(v); 569 | let mut b = [0u32; 5]; 570 | let bsz = unpack_array(&mut input, &mut b[..], a.len(), Some(&0)).expect("unpack_array"); 571 | assert_eq!(bsz, 5*4); 572 | assert_eq!(&a[..], &b[..]); 573 | } 574 | 575 | { 576 | let mut out = Cursor::new(Vec::new()); 577 | let a = [0x11u32, 0x22, 0x33, 0x44, 0x55]; 578 | 579 | assert_eq!(pack_array(&a, 4, &mut out, Some(&0)).unwrap(), 4*4); 580 | 581 | let v = out.into_inner(); 582 | 583 | assert_eq!(v.len(), 4*4); 584 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x11, 585 | 0x00, 0x00, 0x00, 0x22, 586 | 0x00, 0x00, 0x00, 0x33, 587 | 0x00, 0x00, 0x00, 0x44]); 588 | 589 | let mut input = Cursor::new(v); 590 | let mut b = [0u32; 4]; 591 | let bsz = unpack_array(&mut input, &mut b[..], 4, Some(&0)).expect("unpack_array"); 592 | assert_eq!(bsz, 4*4); 593 | assert_eq!(&a[..4], &b[..]); 594 | } 595 | 596 | { 597 | let mut out = Cursor::new(Vec::new()); 598 | let a = [0x11u32, 0x22, 0x33]; 599 | 600 | assert_eq!(pack_array(&a, 4, &mut out, Some(&0)).unwrap(), 4*4); 601 | 602 | let v = out.into_inner(); 603 | 604 | assert_eq!(v.len(), 4*4); 605 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x11, 606 | 0x00, 0x00, 0x00, 0x22, 607 | 0x00, 0x00, 0x00, 0x33, 608 | 0x00, 0x00, 0x00, 0x00]); 609 | 610 | let mut input = Cursor::new(v); 611 | let mut b = [0u32; 4]; 612 | let bsz = unpack_array(&mut input, &mut b[..], 4, Some(&0)).expect("unpack_array"); 613 | assert_eq!(bsz, 4*4); 614 | assert_eq!(vec![0x11,0x22,0x33,0x00], b); 615 | } 616 | } 617 | 618 | #[test] 619 | fn basic_opaque_array() { 620 | { 621 | let mut out = Cursor::new(Vec::new()); 622 | let a = [0x11u8, 0x22, 0x33]; 623 | 624 | 625 | assert_eq!(pack_opaque_array(&a, a.len(), &mut out).unwrap(), 4); 626 | 627 | let v = out.into_inner(); 628 | 629 | assert_eq!(v.len(), 4); 630 | assert_eq!(v, vec![0x11, 0x22, 0x33, 0x00]); 631 | 632 | let mut input = Cursor::new(v); 633 | let mut b = [0u8; 3]; 634 | let bsz = unpack_opaque_array(&mut input, &mut b[..], 3).expect("unpack opaque failed"); 635 | assert_eq!(bsz, 4); 636 | assert_eq!(&a[..], &b[..]); 637 | } 638 | 639 | { 640 | let mut out = Cursor::new(Vec::new()); 641 | let a = [0x11u8, 0x22, 0x33, 0x44]; 642 | 643 | assert_eq!(pack_opaque_array(&a, a.len(), &mut out).unwrap(), 4); 644 | 645 | let v = out.into_inner(); 646 | 647 | assert_eq!(v.len(), 4); 648 | assert_eq!(v, vec![0x11, 0x22, 0x33, 0x44]); 649 | 650 | let mut input = Cursor::new(v); 651 | let mut b = [0u8; 4]; 652 | let bsz = unpack_opaque_array(&mut input, &mut b[..], 4).expect("unpack_opaque_array"); 653 | assert_eq!(bsz, 4); 654 | assert_eq!(&a[..], &b[..]); 655 | } 656 | 657 | { 658 | let mut out = Cursor::new(Vec::new()); 659 | let a = [0x11u8, 0x22, 0x33, 0x44, 0x55]; 660 | 661 | assert_eq!(pack_opaque_array(&a, a.len(), &mut out).unwrap(), 8); 662 | 663 | let v = out.into_inner(); 664 | 665 | assert_eq!(v.len(), 8); 666 | assert_eq!(v, vec![0x11, 0x22, 0x33, 0x44, 0x55, 0x00, 0x00, 0x00]); 667 | 668 | let mut input = Cursor::new(v); 669 | let mut b = [0u8; 5]; 670 | let bsz = unpack_opaque_array(&mut input, &mut b[..], a.len()).expect("unpack_opaque_array"); 671 | assert_eq!(bsz, 8); 672 | assert_eq!(&a[..], &b[..]); 673 | } 674 | 675 | { 676 | let mut out = Cursor::new(Vec::new()); 677 | let a = [0x11u8, 0x22, 0x33, 0x44, 0x55]; 678 | 679 | assert_eq!(pack_opaque_array(&a, 4, &mut out).unwrap(), 4); 680 | 681 | let v = out.into_inner(); 682 | 683 | assert_eq!(v.len(), 4); 684 | assert_eq!(v, vec![0x11, 0x22, 0x33, 0x44]); 685 | 686 | let mut input = Cursor::new(v); 687 | let mut b = [0u8; 5]; 688 | let bsz = unpack_opaque_array(&mut input, &mut b[..], 4).expect("unpack_opaque_array"); 689 | assert_eq!(bsz, 4); 690 | assert_eq!(&a[..4], &b[..4]); 691 | assert_eq!(b[4], 0); 692 | } 693 | 694 | { 695 | let mut out = Cursor::new(Vec::new()); 696 | let a = [0x11u8, 0x22, 0x33]; 697 | 698 | assert_eq!(pack_opaque_array(&a, 4, &mut out).unwrap(), 4); 699 | 700 | let v = out.into_inner(); 701 | 702 | assert_eq!(v.len(), 4); 703 | assert_eq!(v, vec![0x11, 0x22, 0x33, 0x00]); 704 | 705 | let mut input = Cursor::new(v); 706 | let mut b = [0u8; 4]; 707 | let bsz = unpack_opaque_array(&mut input, &mut b[..], 4).expect("unpack_opaque_array"); 708 | assert_eq!(bsz, 4); 709 | assert_eq!(vec![0x11, 0x22, 0x33, 0x00], b); 710 | } 711 | } 712 | 713 | #[test] 714 | fn basic_option() { 715 | let mut out = Cursor::new(Vec::new()); 716 | let none: Option = None; 717 | let some: Option = Some(0x11223344_u32); 718 | 719 | assert_eq!(none.pack(&mut out).unwrap(), 4); 720 | assert_eq!(some.pack(&mut out).unwrap(), 8); 721 | 722 | let v = out.into_inner(); 723 | 724 | assert_eq!(v.len(), 12); 725 | assert_eq!(v, vec![0x00, 0x00, 0x00, 0x00, 726 | 0x00, 0x00, 0x00, 0x01, 0x11, 0x22, 0x33, 0x44,]); 727 | 728 | let mut input = Cursor::new(v); 729 | assert_eq!(Option::::unpack(&mut input).unwrap(), (None, 4)); 730 | assert_eq!(Unpack::unpack(&mut input).unwrap(), (Some(0x11223344_u32), 8)); 731 | 732 | let bad = vec![0, 0, 0, 2]; 733 | let mut input = Cursor::new(bad); 734 | 735 | match Option::::unpack(&mut input) { 736 | Err(Error(ErrorKind::InvalidEnum(_), _)) => (), 737 | res => panic!("bad result {:?}", res), 738 | } 739 | } 740 | -------------------------------------------------------------------------------- /xdr-codec/tests/qc-record.rs: -------------------------------------------------------------------------------- 1 | extern crate quickcheck; 2 | extern crate xdr_codec; 3 | 4 | use std::io::{Cursor, Write}; 5 | 6 | use quickcheck::{TestResult, quickcheck}; 7 | 8 | use xdr_codec::Pack; 9 | use xdr_codec::record::{XdrRecordReader, XdrRecordWriter}; 10 | 11 | // Make sure XdrRecordWriter writes the right stuff 12 | fn check_writerec(bufsz: usize, eor: bool, ref bytes: Vec) -> TestResult { 13 | const EOR: u32 = 1 << 31; 14 | 15 | if bufsz == 0 { 16 | return TestResult::discard(); 17 | } 18 | 19 | // Make an expected serialization into fragments 20 | let mut expected = Vec::new(); 21 | let nchunks = (bytes.len() + bufsz - 1) / bufsz; 22 | 23 | for (idx, c) in bytes.chunks(bufsz).enumerate() { 24 | let mut len = c.len() as u32; 25 | if nchunks - 1 == idx && eor { 26 | len |= EOR; 27 | } 28 | 29 | if let Err(e) = len.pack(&mut expected) { 30 | return TestResult::error(format!("pack failed: {:?}", e)); 31 | } 32 | expected.extend(c); 33 | } 34 | if !eor || nchunks == 0 { 35 | if let Err(e) = EOR.pack(&mut expected) { 36 | return TestResult::error(format!("eor pack failed: {:?}", e)); 37 | } 38 | } 39 | 40 | // Write the same data with XdrRecordWriter 41 | let mut buf = Vec::new(); 42 | { 43 | let mut xw = XdrRecordWriter::with_buffer(&mut buf, bufsz); 44 | if let Err(e) = xw.write(bytes) { 45 | return TestResult::error(format!("xw write failed: {:?}", e)); 46 | } 47 | if let Err(e) = xw.flush_eor(eor) { 48 | return TestResult::error(format!("xw flush_eor failed: {:?}", e)); 49 | } 50 | } 51 | 52 | if buf != expected { 53 | println!( 54 | "eor {} bufsz {} bytes {:?} len {}", 55 | eor, 56 | bufsz, 57 | bytes, 58 | bytes.len() 59 | ); 60 | println!("expected {:?} len {}", expected, expected.len()); 61 | println!(" buf {:?} len {}", buf, buf.len()); 62 | } 63 | 64 | TestResult::from_bool(buf == expected) 65 | } 66 | 67 | #[test] 68 | fn record_writerec() { 69 | quickcheck(check_writerec as fn(usize, bool, Vec) -> TestResult); 70 | } 71 | 72 | // Make sure record structure survives a round trip 73 | fn check_codec(bufsz: usize, ref records: Vec>) -> TestResult { 74 | if bufsz == 0 { 75 | return TestResult::discard(); 76 | } 77 | 78 | let mut buf = Vec::new(); 79 | 80 | for rec in records { 81 | let mut xw = XdrRecordWriter::with_buffer(&mut buf, bufsz); 82 | 83 | if let Err(e) = xw.write(rec) { 84 | return TestResult::error(format!("xw write failed: {:?}", e)); 85 | } 86 | } 87 | 88 | { 89 | let cur = Cursor::new(buf); 90 | let xr = XdrRecordReader::new(cur); 91 | 92 | for (res, orig) in xr.into_iter().zip(records) { 93 | match res { 94 | Err(e) => return TestResult::error(format!("xr failed {:?}", e)), 95 | Ok(ref rx) => { 96 | if rx != orig { 97 | println!( 98 | "bufsz {} mismatch orig {:?}, len {}", 99 | bufsz, 100 | orig, 101 | orig.len() 102 | ); 103 | println!(" rx {:?}, len {}", rx, rx.len()); 104 | return TestResult::failed(); 105 | } 106 | } 107 | } 108 | } 109 | } 110 | 111 | TestResult::passed() 112 | } 113 | 114 | #[test] 115 | fn record_codec() { 116 | quickcheck(check_codec as fn(usize, Vec>) -> TestResult); 117 | } 118 | -------------------------------------------------------------------------------- /xdr-codec/tests/quickcheck.rs: -------------------------------------------------------------------------------- 1 | extern crate xdr_codec; 2 | extern crate quickcheck; 3 | 4 | use std::io::Cursor; 5 | use std::fmt::Debug; 6 | use std::iter; 7 | 8 | use xdr_codec::{Error, ErrorKind, Pack, Unpack, pack_array, pack_opaque_array, padding, 9 | unpack_array, unpack_opaque_array}; 10 | use quickcheck::{Arbitrary, quickcheck}; 11 | 12 | // Output of packing is a multiple of 4 13 | fn pack(v: T) -> bool 14 | where 15 | T: PartialEq + Pack>>, 16 | { 17 | let mut data = Cursor::new(Vec::new()); 18 | 19 | let sz = v.pack(&mut data).expect("pack failed"); 20 | sz % 4 == 0 21 | } 22 | 23 | // Packing something then unpacking returns the same value 24 | fn codec(v: T) -> bool 25 | where 26 | T: PartialEq + Pack>> + Unpack>>, 27 | { 28 | let mut data = Cursor::new(Vec::new()); 29 | 30 | let psz = v.pack(&mut data).expect("pack failed"); 31 | 32 | let mut data = Cursor::new(data.into_inner()); 33 | let (uv, usz) = T::unpack(&mut data).expect("unpack failed"); 34 | 35 | psz == usz && v == uv 36 | } 37 | 38 | // Packing something then unpacking returns the same value 39 | fn short_unpack(v: T) -> bool 40 | where 41 | T: PartialEq + Pack>> + Unpack>>, 42 | { 43 | let mut data = Cursor::new(Vec::new()); 44 | 45 | let psz = v.pack(&mut data).expect("pack failed"); 46 | 47 | // truncate data to make sure unpacking fails 48 | let data = data.into_inner(); 49 | assert_eq!(psz, data.len()); 50 | let data = Vec::from(&data[..data.len() - 1]); 51 | 52 | let mut data = Cursor::new(data); 53 | match T::unpack(&mut data) { 54 | Err(Error(ErrorKind::IOError(_), _)) => true, 55 | _ => false, 56 | } 57 | } 58 | 59 | fn quickcheck_pack_t() 60 | where 61 | T: PartialEq + Pack>> + Unpack>> + Arbitrary + Debug, 62 | { 63 | quickcheck(pack as fn(T) -> bool); 64 | quickcheck(pack as fn(Vec) -> bool); 65 | quickcheck(pack as fn(Option) -> bool); 66 | quickcheck(pack as fn(Vec>) -> bool); 67 | quickcheck(pack as fn(Option>) -> bool); 68 | } 69 | 70 | fn quickcheck_codec_t() 71 | where 72 | T: PartialEq + Pack>> + Unpack>> + Arbitrary + Debug, 73 | { 74 | quickcheck(codec as fn(T) -> bool); 75 | quickcheck(codec as fn(Vec) -> bool); 76 | quickcheck(codec as fn(Option) -> bool); 77 | quickcheck(codec as fn(Vec>) -> bool); 78 | quickcheck(codec as fn(Option>) -> bool); 79 | } 80 | 81 | fn quickcheck_short_unpack_t() 82 | where 83 | T: PartialEq + Pack>> + Unpack>> + Arbitrary + Debug, 84 | { 85 | quickcheck(short_unpack as fn(T) -> bool); 86 | quickcheck(short_unpack as fn(Vec) -> bool); 87 | quickcheck(short_unpack as fn(Option) -> bool); 88 | quickcheck(short_unpack as fn(Vec>) -> bool); 89 | quickcheck(short_unpack as fn(Option>) -> bool); 90 | } 91 | 92 | #[test] 93 | fn quickcheck_pack_ui32() { 94 | quickcheck_pack_t::(); 95 | quickcheck_pack_t::(); 96 | quickcheck_pack_t::(); 97 | } 98 | 99 | #[test] 100 | fn quickcheck_pack_iu64() { 101 | quickcheck_pack_t::(); 102 | quickcheck_pack_t::(); 103 | } 104 | 105 | #[test] 106 | fn quickcheck_pack_float() { 107 | quickcheck_pack_t::(); 108 | quickcheck_pack_t::(); 109 | } 110 | 111 | #[test] 112 | fn quickcheck_codec_ui32() { 113 | quickcheck_codec_t::(); 114 | quickcheck_codec_t::(); 115 | quickcheck_codec_t::(); 116 | } 117 | 118 | #[test] 119 | fn quickcheck_codec_iu64() { 120 | quickcheck_codec_t::(); 121 | quickcheck_codec_t::(); 122 | } 123 | 124 | #[test] 125 | fn quickcheck_codec_float() { 126 | quickcheck_codec_t::(); 127 | quickcheck_codec_t::(); 128 | } 129 | 130 | #[test] 131 | fn quickcheck_short_unpack_ui32() { 132 | quickcheck_short_unpack_t::(); 133 | quickcheck_short_unpack_t::(); 134 | quickcheck_short_unpack_t::(); 135 | } 136 | 137 | #[test] 138 | fn quickcheck_short_unpack_iu64() { 139 | quickcheck_short_unpack_t::(); 140 | quickcheck_short_unpack_t::(); 141 | } 142 | 143 | #[test] 144 | fn quickcheck_short_unpack_float() { 145 | quickcheck_short_unpack_t::(); 146 | quickcheck_short_unpack_t::(); 147 | } 148 | 149 | fn check_array(arraysz: usize, rxsize: usize, data: Vec, defl: Option) -> bool { 150 | let mut buf = Vec::new(); 151 | 152 | // pack data we have into the array 153 | let tsz = match pack_array(&data[..], arraysz, &mut buf, defl.as_ref()) { 154 | Ok(tsz) if data.len() >= arraysz || defl.is_some() => tsz, 155 | e @ Err(Error(ErrorKind::InvalidLen(_), _)) => { 156 | let pass = defl.is_none() && data.len() < arraysz; 157 | if !pass { 158 | println!( 159 | "pack_array failed {:?}, defl {:?} data.len {} arraysz {}", 160 | e, 161 | defl, 162 | data.len(), 163 | arraysz 164 | ) 165 | } 166 | return pass; 167 | } 168 | Err(e) => { 169 | println!("pack_array failed {:?}", e); 170 | return false; 171 | } 172 | Ok(tsz) => { 173 | println!( 174 | "pack_array unexpected success tsz {} data.len {} arraysz {} defl {:?}", 175 | tsz, 176 | data.len(), 177 | arraysz, 178 | defl 179 | ); 180 | return false; 181 | } 182 | }; 183 | if tsz != arraysz * 4 { 184 | println!("tsz {} arraysz*4 {}", tsz, arraysz * 4); 185 | return false; 186 | } 187 | if buf.len() != tsz { 188 | println!("buf.len {} tsz {}", buf.len(), tsz); 189 | return false; 190 | } 191 | 192 | // if data is shorter than array, then serialized is padded with zero 193 | // XXX padding isn't necessarily zero 194 | //if data.len() < arraysz { 195 | // assert!(defl.is_some()); 196 | // if buf[data.len()*4..].iter().any(|b| *b != defl.unwrap()) { println!("nonzero pad"); return false } 197 | //} 198 | 199 | let mut recv: Vec = iter::repeat(0xffff_ffff_u32).take(rxsize).collect(); 200 | let mut cur = Cursor::new(buf); 201 | 202 | // unpack rxsize elements 203 | let rsz = match unpack_array(&mut cur, &mut recv[..], arraysz, defl.as_ref()) { 204 | Ok(rsz) if recv.len() <= arraysz || defl.is_some() => rsz, // normal success 205 | Err(Error(ErrorKind::InvalidLen(_), _)) => return defl.is_none() && recv.len() > arraysz, // expected if recv is too big and there's no default 206 | Err(e) => { 207 | println!("unpack_array failed {:?}", e); 208 | return false; 209 | } 210 | Ok(rsz) => { 211 | println!( 212 | "unpack_array unexpected success rsz {} recv.len {} arraysz {} defl {:?}", 213 | rsz, 214 | recv.len(), 215 | arraysz, 216 | defl 217 | ); 218 | return false; 219 | } 220 | }; 221 | if rsz != arraysz * 4 { 222 | println!("rsz {} arraysz*4 {}", rsz, arraysz * 4); 223 | return false; 224 | } 225 | 226 | // data and recv must match their common prefix up to arraysz 227 | if data.iter().zip(recv.iter().take(arraysz)).any( 228 | |(d, r)| *d != *r, 229 | ) 230 | { 231 | println!("nonmatching\ndata {:?}\nrecv {:?}", data, recv); 232 | return false; 233 | } 234 | 235 | // if recv is larger than array, then tail is defaulted 236 | if rxsize > arraysz { 237 | assert!(defl.is_some()); 238 | if recv[arraysz..].iter().any(|v| *v != defl.unwrap()) { 239 | println!("nondefault tail"); 240 | return false; 241 | } 242 | } 243 | 244 | true 245 | } 246 | 247 | #[test] 248 | fn quickcheck_array() { 249 | quickcheck( 250 | check_array as fn(usize, usize, Vec, Option) -> bool, 251 | ); 252 | } 253 | 254 | fn check_opaque(arraysz: usize, rxsize: usize, data: Vec) -> bool { 255 | let mut buf = Vec::new(); 256 | 257 | // pack data we have into the array 258 | let tsz = pack_opaque_array(&data[..], arraysz, &mut buf).expect("pack_array failed"); 259 | if tsz != arraysz + padding(arraysz).len() { 260 | println!( 261 | "tsz {} arraysz+pad {}", 262 | tsz, 263 | arraysz + padding(arraysz).len() 264 | ); 265 | return false; 266 | } 267 | if buf.len() != tsz { 268 | println!("buf.len {} tsz {}", buf.len(), tsz); 269 | return false; 270 | } 271 | 272 | // if data is shorter than array, then serialized is padded with zero 273 | if data.len() < arraysz { 274 | if buf[data.len()..].iter().any(|b| *b != 0) { 275 | println!("nonzero pad"); 276 | return false; 277 | } 278 | } 279 | 280 | let mut recv: Vec = iter::repeat(0xff).take(rxsize).collect(); 281 | let mut cur = Cursor::new(buf); 282 | 283 | // unpack rxsize elements 284 | let rsz = unpack_opaque_array(&mut cur, &mut recv[..], arraysz).expect("unpack_array failed"); 285 | if rsz != arraysz + padding(arraysz).len() { 286 | println!( 287 | "rsz {} arraysz+pad {}", 288 | rsz, 289 | arraysz + padding(arraysz).len() 290 | ); 291 | return false; 292 | } 293 | 294 | // data and recv must match their common prefix up to arraysz 295 | if data.iter().zip(recv.iter().take(arraysz)).any( 296 | |(d, r)| *d != *r, 297 | ) 298 | { 299 | println!("nonmatching\ndata {:?}\nrecv {:?}", data, recv); 300 | return false; 301 | } 302 | 303 | // if recv is larger than array, then tail is zero 304 | if rxsize > arraysz { 305 | if recv[arraysz..].iter().any(|v| *v != 0) { 306 | println!("nondefault tail"); 307 | return false; 308 | } 309 | } 310 | 311 | true 312 | } 313 | 314 | #[test] 315 | fn quickcheck_opaque() { 316 | quickcheck(check_opaque as fn(usize, usize, Vec) -> bool); 317 | } 318 | -------------------------------------------------------------------------------- /xdr-codec/tests/test-record.rs: -------------------------------------------------------------------------------- 1 | // Don't rustfmt in here to avoid trashing vec![] formatting 2 | #![cfg_attr(rustfmt, rustfmt_skip)] 3 | 4 | extern crate xdr_codec; 5 | 6 | use std::io::{Cursor, Read, Write}; 7 | 8 | use xdr_codec::record::{XdrRecordReader, XdrRecordWriter}; 9 | 10 | #[test] 11 | fn recread_full() { 12 | let inbuf = vec![128, 0, 0, 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 13 | let cur = Cursor::new(inbuf); 14 | 15 | let mut recread = XdrRecordReader::new(cur); 16 | let mut buf = vec![0; 20]; 17 | 18 | assert_eq!(recread.read(&mut buf[..]).unwrap(), 10); 19 | assert_eq!( 20 | buf, 21 | vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 22 | ); 23 | assert!(recread.eor()); 24 | } 25 | 26 | #[test] 27 | fn recread_short() { 28 | let inbuf = vec![128, 0, 0, 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 29 | let cur = Cursor::new(inbuf); 30 | 31 | let mut recread = XdrRecordReader::new(cur); 32 | let mut buf = vec![0; 5]; 33 | 34 | assert_eq!(recread.read(&mut buf[..]).unwrap(), 5); 35 | assert!(recread.eor()); 36 | assert_eq!(buf, vec![0, 1, 2, 3, 4]); 37 | 38 | assert_eq!(recread.read(&mut buf[..]).unwrap(), 5); 39 | assert!(recread.eor()); 40 | assert_eq!(buf, vec![5, 6, 7, 8, 9]); 41 | } 42 | 43 | #[test] 44 | fn recread_half() { 45 | let inbuf = vec![0, 0, 0, 5, 0, 1, 2, 3, 4, 128, 0, 0, 5, 5, 6, 7, 8, 9]; 46 | let cur = Cursor::new(inbuf); 47 | 48 | let mut recread = XdrRecordReader::new(cur); 49 | let mut buf = vec![0; 10]; 50 | 51 | assert_eq!(recread.read(&mut buf[..]).unwrap(), 5); 52 | assert_eq!(buf, vec![0, 1, 2, 3, 4, 0, 0, 0, 0, 0]); 53 | assert!(!recread.eor()); 54 | 55 | assert_eq!(recread.read(&mut buf[..]).unwrap(), 5); 56 | assert_eq!(buf, vec![5, 6, 7, 8, 9, 0, 0, 0, 0, 0]); 57 | assert!(recread.eor()); 58 | } 59 | 60 | #[test] 61 | fn recread_iter() { 62 | let inbuf = vec![ 63 | 0, 64 | 0, 65 | 0, 66 | 5, 67 | 0, 68 | 1, 69 | 2, 70 | 3, 71 | 4, 72 | 128, 73 | 0, 74 | 0, 75 | 5, 76 | 5, 77 | 6, 78 | 7, 79 | 8, 80 | 9, 81 | 128, 82 | 0, 83 | 0, 84 | 1, 85 | 99, 86 | ]; 87 | let cur = Cursor::new(inbuf); 88 | let recread = XdrRecordReader::new(cur); 89 | 90 | let expected = vec![vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], vec![99]]; 91 | let got: Vec<_> = recread.into_iter().map(|r| r.expect("IO error")).collect(); 92 | 93 | assert_eq!(expected, got); 94 | } 95 | 96 | #[test] 97 | fn read_zerorec() { 98 | let inbuf = vec![0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0]; 99 | 100 | let cur = Cursor::new(inbuf); 101 | let mut recread = XdrRecordReader::new(cur); 102 | 103 | let mut buf = [0; 100]; 104 | assert_eq!(recread.read(&mut buf).unwrap(), 0); 105 | assert!(recread.eor()); 106 | } 107 | 108 | #[test] 109 | #[should_panic(expected = "must be non-zero")] 110 | fn zerosz() { 111 | let buf = Vec::new(); 112 | let _ = XdrRecordWriter::with_buffer(buf, 0); 113 | } 114 | 115 | #[test] 116 | fn smallrec() { 117 | let mut buf = Vec::new(); 118 | 119 | { 120 | let mut xw = XdrRecordWriter::new(&mut buf); 121 | 122 | assert_eq!(write!(xw, "hello").unwrap(), ()); 123 | } 124 | 125 | assert_eq!(buf, vec![128, 0, 0, 5, 104, 101, 108, 108, 111]) 126 | } 127 | 128 | #[test] 129 | fn largerec() { 130 | let mut buf = Vec::new(); 131 | 132 | { 133 | let mut xw = XdrRecordWriter::with_buffer(&mut buf, 3); 134 | 135 | assert_eq!(write!(xw, "hello").unwrap(), ()); 136 | } 137 | 138 | assert_eq!(buf, vec![0, 0, 0, 3, 104, 101, 108, 128, 0, 0, 2, 108, 111]) 139 | } 140 | 141 | #[test] 142 | fn largerec_flush() { 143 | let mut buf = Vec::new(); 144 | 145 | { 146 | let mut xw = XdrRecordWriter::with_buffer(&mut buf, 10); 147 | 148 | assert_eq!(write!(xw, "hel").unwrap(), ()); 149 | xw.flush().unwrap(); 150 | assert_eq!(write!(xw, "lo").unwrap(), ()); 151 | xw.flush().unwrap(); 152 | } 153 | 154 | assert_eq!( 155 | buf, 156 | vec![ 157 | 0, 158 | 0, 159 | 0, 160 | 3, 161 | 104, 162 | 101, 163 | 108, 164 | 0, 165 | 0, 166 | 0, 167 | 2, 168 | 108, 169 | 111, 170 | 128, 171 | 0, 172 | 0, 173 | 0, 174 | ] 175 | ) 176 | } 177 | -------------------------------------------------------------------------------- /xdrgen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xdrgen" 3 | version = "0.4.4" 4 | authors = ["Jeremy Fitzhardinge "] 5 | license = "MIT/Apache-2.0" 6 | description = "XDR codec generator from specification. Designed for use with xdr-codec." 7 | repository = "https://github.com/jsgf/rust-xdr/tree/master/xdrgen" 8 | documentation = "https://docs.rs/xdrgen/" 9 | readme = "README.md" 10 | keywords = ["encoding", "protocol", "xdr", "rfc4506", "serialization"] 11 | include = [ "src/**/*.rs", "tests/**/*.rs", "*.md", "Cargo.toml" ] 12 | 13 | [[bin]] 14 | name = "xdrgen" 15 | path = "src/xdrgen.rs" 16 | test = false 17 | bench = false 18 | doc = false 19 | 20 | [features] 21 | unstable = [] 22 | 23 | [dependencies] 24 | log = "0.3" 25 | env_logger = "0.4" 26 | nom = { version="3.1", features=["verbose-errors"] } 27 | quote = "0.3" 28 | clap = "2.24" 29 | lazy_static = "0.2" 30 | bitflags = "0.9" 31 | 32 | [dependencies.xdr-codec] 33 | path = "../xdr-codec" 34 | version = "0.4" 35 | 36 | [dev-dependencies] 37 | tempdir = "0.3" 38 | error-chain = "0.10" 39 | -------------------------------------------------------------------------------- /xdrgen/README.md: -------------------------------------------------------------------------------- 1 | # Rust XDR library 2 | 3 | [![Build Status](https://travis-ci.org/jsgf/rust-xdr.svg?branch=master)](https://travis-ci.org/jsgf/rust-xdr) 4 | [![Crates.io](https://img.shields.io/crates/v/xdrgen.svg)](https://crates.io/crates/xdrgen) 5 | [![Coverage Status](https://coveralls.io/repos/github/jsgf/promising-future/badge.svg?branch=master)](https://coveralls.io/github/jsgf/promising-future?branch=master) 6 | 7 | This crate provides xdrgen, which takes an XDR specification in a .x 8 | file, and produces Rust code to serialize and deserialize the 9 | specified types. It is intended to be used in conjunction with 10 | [xdr-codec](https://github.com/jsgf/rust-xdr-codec). 11 | 12 | The syntax of the .x file follows 13 | [RFC4506](https://tools.ietf.org/html/rfc4506.html). This has type definitions 14 | for XDR but does not include RPC protocol specifications. Correspondingly, 15 | xdrgen does not support auto-generation of RPC clients/servers. 16 | 17 | ## Changes in 0.4.0 18 | 19 | - Now uses the `quote` package, so it will work on stable Rust 20 | - Detects the use of Rust keywords in XDR specifications, and appends a `_` to them. 21 | 22 | ## Usage 23 | 24 | Usage is straightforward. You can generate the Rust code from a spec a build.rs: 25 | 26 | ``` 27 | extern crate xdrgen; 28 | 29 | fn main() { 30 | xdrgen::compile("src/simple.x").expect("xdrgen simple.x failed"); 31 | } 32 | ``` 33 | 34 | This code can then be included into a module: 35 | 36 | ``` 37 | mod simple { 38 | use xdr_codec; 39 | 40 | #[allow(dead_code)] 41 | include!(concat!(env!("OUT_DIR"), "/simple_xdr.rs")); 42 | } 43 | ``` 44 | 45 | Once you have this, you can call `mytype.pack(&mut output)`, and 46 | `let mything: MyThing = xdr_codec::unpack(&mut input)?;`. 47 | 48 | The serializers require your types to implement the `Pack` and `Unpack` 49 | traits, and generate code to write to `std::io::Write` implementation, and 50 | read from `std::io::Read`. 51 | 52 | All types and fields are generated public, so you can control their access 53 | outside your module or crate. If your spec references other types which are 54 | not defined within the spec, then you can define them within the module 55 | as well, either by aliasing them with other defined types, or implementing 56 | the `Pack` and `Unpack` traits yourself. 57 | 58 | Use can use xdr-codec's `XdrRecordReader` and `XdrRecordWriter` types as IO 59 | filters that implement XDR-RPC record marking. 60 | 61 | More [documentation for xdrgen 62 | here](https://docs.rs/xdrgen/). See the 63 | [documentation for 64 | xdr-codec](https://docs.rs/xdr-codec/) for more 65 | details about using the generated types and code. 66 | 67 | ## Limitations 68 | 69 | There are currently a few limitations: 70 | * The generated code uses identifiers as specified in the .x file, so the 71 | Rust code will not use normal formatting conventions. 72 | * Generated code follows no formatting convention - use rustfmt if desired. 73 | * XDR has discriminated unions, which are a good match for Rust enums. 74 | However, it also supports a `default` case if an unknown discriminator 75 | is encountered. This crate supports this for unpacking, but not for 76 | packing, as Rust does not allow enums to have unknown values. 77 | * The generated code uses `#[derive(Debug, Clone, ...)]` to generate 78 | implementations for common traits. However, rustc only supports `#[derive]` 79 | on fixed-size arrays with 0..32 elements; if you have an array larger than 80 | this, the generated code will fail to compile. Right now, the only workaround 81 | is to manually implement `Pack` and `Unpack` for such types. 82 | (TODO: add an option to omit derived traits.) 83 | 84 | ## License 85 | 86 | Licensed under either of 87 | 88 | * Apache License, Version 2.0, ([LICENSE-APACHE](http://www.apache.org/licenses/LICENSE-2.0)) 89 | * MIT license ([LICENSE-MIT](http://opensource.org/licenses/MIT)) 90 | 91 | at your option. 92 | 93 | ### Contribution 94 | 95 | Unless you explicitly state otherwise, any contribution intentionally submitted 96 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any 97 | additional terms or conditions. 98 | -------------------------------------------------------------------------------- /xdrgen/example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example" 3 | version = "0.1.0" 4 | authors = ["Jeremy Fitzhardinge "] 5 | build = "build.rs" 6 | 7 | [[bin]] 8 | name = "simple" 9 | path = "src/simple.rs" 10 | 11 | [dependencies.xdr-codec] 12 | path = "../../xdr-codec" 13 | 14 | [build-dependencies.xdrgen] 15 | path = ".." 16 | -------------------------------------------------------------------------------- /xdrgen/example/build.rs: -------------------------------------------------------------------------------- 1 | extern crate xdrgen; 2 | 3 | fn main() { 4 | xdrgen::compile("src/simple.x").unwrap(); 5 | } 6 | -------------------------------------------------------------------------------- /xdrgen/example/src/simple.rs: -------------------------------------------------------------------------------- 1 | extern crate xdr_codec; 2 | 3 | use std::io::Cursor; 4 | use xdr_codec::{unpack,pack}; 5 | 6 | mod simple { 7 | use xdr_codec; 8 | 9 | #[allow(dead_code)] 10 | include!(concat!(env!("OUT_DIR"), "/simple_xdr.rs")); 11 | } 12 | 13 | fn main() { 14 | let foo = simple::Foo { 15 | a: 1, b: 2, c: 3, 16 | bar: vec![simple::Bar { data: vec![1,2,3] }], 17 | barish: None, 18 | name: String::from("foox"), 19 | thing: simple::Things::C, 20 | type_: 123, 21 | }; 22 | 23 | let mut buf = Vec::new(); 24 | 25 | pack(&foo, &mut buf).unwrap(); 26 | println!("foo={:?}", foo); 27 | println!("buf={:?} len={}", buf, buf.len()); 28 | 29 | let mut cur = Cursor::new(buf); 30 | 31 | let foo2 = unpack(&mut cur).unwrap(); 32 | 33 | println!("foo={:?}", foo); 34 | println!("foo2={:?}", foo2); 35 | assert_eq!(foo, foo2); 36 | } 37 | -------------------------------------------------------------------------------- /xdrgen/example/src/simple.x: -------------------------------------------------------------------------------- 1 | enum Things { A, B, C }; 2 | struct Bar { 3 | opaque data<>; 4 | }; 5 | struct Foo { 6 | int a; 7 | int b; 8 | int c; 9 | Bar bar<>; 10 | Bar *barish; 11 | string name<>; 12 | Things thing; 13 | unsigned type; 14 | }; 15 | -------------------------------------------------------------------------------- /xdrgen/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! XDR codec generation 2 | //! 3 | //! This crate provides library interfaces for programatically generating Rust code to implement 4 | //! RFC4506 XDR encoding/decoding, as well as a command line tool "xdrgen". 5 | //! 6 | //! It is intended to be used with the "xdr-codec" crate, which provides the runtime library for 7 | //! encoding/decoding primitive types, strings, opaque data and arrays. 8 | 9 | #![recursion_limit="128"] 10 | 11 | extern crate xdr_codec as xdr; 12 | 13 | #[macro_use] 14 | extern crate quote; 15 | 16 | #[macro_use] 17 | extern crate lazy_static; 18 | 19 | #[macro_use] 20 | extern crate log; 21 | 22 | #[macro_use] 23 | extern crate nom; 24 | 25 | #[macro_use] 26 | extern crate bitflags; 27 | 28 | use std::fs::File; 29 | use std::path::{Path, PathBuf}; 30 | use std::io::{Read, Write}; 31 | use std::fmt::Display; 32 | use std::env; 33 | use std::result; 34 | 35 | use xdr::Result; 36 | 37 | mod spec; 38 | use spec::{Emit, Emitpack, Symtab}; 39 | 40 | fn result_option(resopt: result::Result, E>) -> Option> { 41 | match resopt { 42 | Ok(None) => None, 43 | Ok(Some(v)) => Some(Ok(v)), 44 | Err(e) => Some(Err(e)), 45 | } 46 | } 47 | 48 | /// Generate Rust code from an RFC4506 XDR specification 49 | /// 50 | /// `infile` is simply a string used in error messages; it may be empty. `input` is a read stream of 51 | /// the specification, and `output` is where the generated code is sent. 52 | pub fn generate(infile: &str, mut input: In, mut output: Out) -> Result<()> 53 | where 54 | In: Read, 55 | Out: Write, 56 | { 57 | let mut source = String::new(); 58 | 59 | input.read_to_string(&mut source)?; 60 | 61 | let xdr = match spec::specification(&source) { 62 | Ok(defns) => Symtab::new(&defns), 63 | Err(e) => return Err(xdr::Error::from(format!("parse error: {}", e))), 64 | }; 65 | 66 | let xdr = xdr; 67 | 68 | let res: Vec<_> = { 69 | let consts = xdr.constants() 70 | .filter_map(|(c, &(v, ref scope))| if scope.is_none() { 71 | Some(spec::Const(c.clone(), v)) 72 | } else { 73 | None 74 | }) 75 | .map(|c| c.define(&xdr)); 76 | 77 | let typespecs = xdr.typespecs() 78 | .map(|(n, ty)| spec::Typespec(n.clone(), ty.clone())) 79 | .map(|c| c.define(&xdr)); 80 | 81 | let typesyns = xdr.typesyns() 82 | .map(|(n, ty)| spec::Typesyn(n.clone(), ty.clone())) 83 | .map(|c| c.define(&xdr)); 84 | 85 | let packers = xdr.typespecs() 86 | .map(|(n, ty)| spec::Typespec(n.clone(), ty.clone())) 87 | .filter_map(|c| result_option(c.pack(&xdr))); 88 | 89 | let unpackers = xdr.typespecs() 90 | .map(|(n, ty)| spec::Typespec(n.clone(), ty.clone())) 91 | .filter_map(|c| result_option(c.unpack(&xdr))); 92 | 93 | consts 94 | .chain(typespecs) 95 | .chain(typesyns) 96 | .chain(packers) 97 | .chain(unpackers) 98 | .collect::>>()? 99 | }; 100 | 101 | let _ = writeln!( 102 | output, 103 | r#" 104 | // GENERATED CODE 105 | // 106 | // Generated from {} by xdrgen. 107 | // 108 | // DO NOT EDIT 109 | 110 | "#, 111 | infile 112 | ); 113 | 114 | for it in res { 115 | let _ = writeln!(output, "{}\n", it.as_str()); 116 | } 117 | 118 | Ok(()) 119 | } 120 | 121 | /// Simplest possible way to generate Rust code from an XDR specification. 122 | /// 123 | /// It is intended for use in a build.rs script: 124 | /// 125 | /// ```ignore 126 | /// extern crate xdrgen; 127 | /// 128 | /// fn main() { 129 | /// xdrgen::compile("src/simple.x").unwrap(); 130 | /// } 131 | /// ``` 132 | /// 133 | /// Output is put into OUT_DIR, and can be included: 134 | /// 135 | /// ```ignore 136 | /// mod simple { 137 | /// use xdr_codec; 138 | /// 139 | /// include!(concat!(env!("OUT_DIR"), "/simple_xdr.rs")); 140 | /// } 141 | /// ``` 142 | /// 143 | /// If your specification uses types which are not within the specification, you can provide your 144 | /// own implementations of `Pack` and `Unpack` for them. 145 | pub fn compile

(infile: P) -> Result<()> 146 | where 147 | P: AsRef + Display, 148 | { 149 | let input = File::open(&infile)?; 150 | 151 | let mut outdir = PathBuf::from(env::var("OUT_DIR").unwrap_or(String::from("."))); 152 | let outfile = PathBuf::from(infile.as_ref()) 153 | .file_stem() 154 | .unwrap() 155 | .to_owned() 156 | .into_string() 157 | .unwrap() 158 | .replace("-", "_"); 159 | 160 | outdir.push(&format!("{}_xdr.rs", outfile)); 161 | 162 | let output = File::create(outdir)?; 163 | 164 | generate( 165 | infile.as_ref().as_os_str().to_str().unwrap_or(""), 166 | input, 167 | output, 168 | ) 169 | } 170 | -------------------------------------------------------------------------------- /xdrgen/src/spec/mod.rs: -------------------------------------------------------------------------------- 1 | use std::collections::btree_map::{BTreeMap, Iter}; 2 | use std::collections::{HashMap, HashSet}; 3 | use std::io::{Write, stderr}; 4 | 5 | use std::result; 6 | 7 | use quote::{self, ToTokens, Tokens}; 8 | 9 | mod xdr_nom; 10 | 11 | use xdr::Error; 12 | 13 | pub type Result = result::Result; 14 | 15 | pub use self::xdr_nom::specification; 16 | 17 | use super::result_option; 18 | 19 | bitflags! { 20 | pub struct Derives: u32 { 21 | const COPY = 1 << 0; 22 | const CLONE = 1 << 1; 23 | const DEBUG = 1 << 2; 24 | const EQ = 1 << 3; 25 | const PARTIALEQ = 1 << 4; 26 | } 27 | } 28 | 29 | impl ToTokens for Derives { 30 | fn to_tokens(&self, toks: &mut Tokens) { 31 | if self.is_empty() { 32 | return; 33 | } 34 | 35 | toks.append("#[derive("); 36 | 37 | let mut der = Vec::new(); 38 | 39 | if self.contains(COPY) { 40 | der.push(quote!(Copy)) 41 | } 42 | if self.contains(CLONE) { 43 | der.push(quote!(Clone)) 44 | } 45 | if self.contains(DEBUG) { 46 | der.push(quote!(Debug)) 47 | } 48 | if self.contains(EQ) { 49 | der.push(quote!(Eq)) 50 | } 51 | if self.contains(PARTIALEQ) { 52 | der.push(quote!(PartialEq)) 53 | } 54 | 55 | toks.append_separated(der, ","); 56 | toks.append(")]"); 57 | } 58 | } 59 | 60 | lazy_static! { 61 | static ref KEYWORDS: HashSet<&'static str> = { 62 | let kws = [ 63 | "abstract", "alignof", "as", "become", "box", 64 | "break", "const", "continue", "crate", "do", 65 | "else", "enum", "extern", "false", "final", 66 | "fn", "for", "if", "impl", "in", 67 | "let", "loop", "macro", "match", "mod", 68 | "move", "mut", "offsetof", "override", "priv", 69 | "proc", "pub", "pure", "ref", "return", 70 | "Self", "self", "sizeof", "static", "struct", 71 | "super", "trait", "true", "type", "typeof", 72 | "unsafe", "unsized", "use", "virtual", "where", 73 | "while", "yield", 74 | ]; 75 | 76 | kws.into_iter().map(|x| *x).collect() 77 | }; 78 | } 79 | 80 | fn quote_ident>(id: S) -> quote::Ident { 81 | let id = id.as_ref(); 82 | 83 | if (*KEYWORDS).contains(id) { 84 | quote::Ident::new(format!("{}_", id)) 85 | } else { 86 | quote::Ident::new(id) 87 | } 88 | } 89 | 90 | #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone)] 91 | pub enum Value { 92 | Ident(String), 93 | Const(i64), 94 | } 95 | 96 | impl Value { 97 | fn ident>(id: S) -> Value { 98 | Value::Ident(id.as_ref().to_string()) 99 | } 100 | 101 | fn as_ident(&self) -> quote::Ident { 102 | match self { 103 | &Value::Ident(ref id) => quote_ident(id), 104 | &Value::Const(val) => { 105 | quote::Ident::new(format!( 106 | "Const{}{}", 107 | (if val < 0 { "_" } else { "" }), 108 | val.abs() 109 | )) 110 | } 111 | } 112 | } 113 | 114 | fn as_i64(&self, symtab: &Symtab) -> Option { 115 | symtab.value(self) 116 | } 117 | 118 | fn as_token(&self, symtab: &Symtab) -> Tokens { 119 | match self { 120 | &Value::Const(c) => quote!(#c), 121 | &Value::Ident(ref id) => { 122 | let tok = quote_ident(id.as_str()); 123 | if let Some((_, Some(ref scope))) = symtab.getconst(id) { 124 | let scope = quote_ident(scope); 125 | quote!(#scope :: #tok) 126 | } else { 127 | quote!(#tok) 128 | } 129 | } 130 | } 131 | } 132 | } 133 | 134 | #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone)] 135 | pub enum Type { 136 | UInt, 137 | Int, 138 | UHyper, 139 | Hyper, 140 | Float, 141 | Double, 142 | Quadruple, 143 | Bool, 144 | 145 | // Special array elements 146 | Opaque, // binary 147 | String, // text 148 | 149 | // Compound types 150 | Enum(Vec), 151 | Struct(Vec), 152 | Union(Box, Vec, Option>), 153 | 154 | Option(Box), 155 | Array(Box, Value), 156 | Flex(Box, Option), 157 | 158 | // Type reference (may be external) 159 | Ident(String, Option), 160 | } 161 | 162 | impl Type { 163 | fn array(ty: Type, sz: Value) -> Type { 164 | Type::Array(Box::new(ty), sz) 165 | } 166 | 167 | fn flex(ty: Type, sz: Option) -> Type { 168 | Type::Flex(Box::new(ty), sz) 169 | } 170 | 171 | fn option(ty: Type) -> Type { 172 | Type::Option(Box::new(ty)) 173 | } 174 | 175 | fn union((d, c, dfl): (Decl, Vec, Option)) -> Type { 176 | Type::Union(Box::new(d), c, dfl.map(Box::new)) 177 | } 178 | 179 | fn ident>(id: S) -> Type { 180 | Type::Ident(id.as_ref().to_string(), None) 181 | } 182 | 183 | fn ident_with_derives>(id: S, derives: Derives) -> Type { 184 | Type::Ident(id.as_ref().to_string(), Some(derives)) 185 | } 186 | 187 | fn is_boxed(&self, symtab: &Symtab) -> bool { 188 | use self::Type::*; 189 | 190 | match self { 191 | _ if self.is_prim(symtab) => false, 192 | &Array(_, _) | &Flex(_, _) | &Option(_) => false, 193 | &Ident(ref name, _) => { 194 | if let Some(ty) = symtab.typespec(name) { 195 | ty.is_boxed(symtab) 196 | } else { 197 | true 198 | } 199 | } 200 | _ => true, 201 | } 202 | } 203 | 204 | fn is_prim(&self, symtab: &Symtab) -> bool { 205 | use self::Type::*; 206 | 207 | match self { 208 | &Int | &UInt | &Hyper | &UHyper | &Float | &Double | &Quadruple | &Bool => true, 209 | 210 | &Ident(ref id, _) => { 211 | match symtab.typespec(id) { 212 | None => false, 213 | Some(ref ty) => ty.is_prim(symtab), 214 | } 215 | } 216 | 217 | _ => false, 218 | } 219 | } 220 | 221 | fn derivable(&self, symtab: &Symtab, memo: Option<&mut HashMap>) -> Derives { 222 | use self::Type::*; 223 | let mut memoset = HashMap::new(); 224 | 225 | let mut memo = match memo { 226 | None => &mut memoset, 227 | Some(m) => m, 228 | }; 229 | 230 | if let Some(res) = memo.get(self) { 231 | return *res; 232 | } 233 | 234 | // No derives unless we can prove we have some 235 | memo.insert(self.clone(), Derives::empty()); 236 | 237 | let set = match self { 238 | &Array(ref ty, ref len) => { 239 | let ty = ty.as_ref(); 240 | let set = match ty { 241 | &Opaque | &String => EQ | PARTIALEQ | COPY | CLONE | DEBUG, 242 | ref ty => ty.derivable(symtab, Some(memo)), 243 | }; 244 | match len.as_i64(symtab) { 245 | Some(v) if v <= 32 => set, 246 | _ => Derives::empty(), // no #[derive] for arrays > 32 247 | } 248 | } 249 | &Flex(ref ty, ..) => { 250 | let set = ty.derivable(symtab, Some(memo)); 251 | set & !COPY // no Copy, everything else OK 252 | } 253 | &Enum(_) => EQ | PARTIALEQ | COPY | CLONE | DEBUG, 254 | &Option(ref ty) => ty.derivable(symtab, Some(memo)), 255 | &Struct(ref fields) => { 256 | fields.iter().fold(Derives::all(), |a, f| { 257 | a & f.derivable(symtab, memo) 258 | }) 259 | } 260 | 261 | &Union(_, ref cases, ref defl) => { 262 | cases.iter().map(|c| &c.1).fold(Derives::all(), |a, c| { 263 | a & c.derivable(symtab, memo) 264 | }) & 265 | defl.as_ref().map_or( 266 | Derives::all(), 267 | |d| d.derivable(symtab, memo), 268 | ) 269 | } 270 | 271 | &Ident(_, Some(derives)) => derives, 272 | 273 | &Ident(ref id, None) => { 274 | match symtab.typespec(id) { 275 | None => Derives::empty(), // unknown, really 276 | Some(ref ty) => ty.derivable(symtab, Some(memo)), 277 | } 278 | } 279 | 280 | &Float | &Double => PARTIALEQ | COPY | CLONE | DEBUG, 281 | ty if ty.is_prim(symtab) => Derives::all(), 282 | 283 | _ => Derives::all() & !COPY, 284 | }; 285 | 286 | memo.insert(self.clone(), set); 287 | set 288 | } 289 | 290 | 291 | fn packer(&self, val: Tokens, symtab: &Symtab) -> Result { 292 | use self::Type::*; 293 | 294 | let res = match self { 295 | &Enum(_) => quote!((*#val as i32).pack(out)?), 296 | 297 | &Flex(ref ty, ref maxsz) => { 298 | let ty = ty.as_ref(); 299 | let maxsz = match maxsz { 300 | &None => quote!(None), 301 | &Some(ref mx) => { 302 | let mx = mx.as_token(symtab); 303 | quote!(Some(#mx as usize)) 304 | } 305 | }; 306 | match ty { 307 | &Opaque => quote!(xdr_codec::pack_opaque_flex(&#val, #maxsz, out)?), 308 | &String => quote!(xdr_codec::pack_string(&#val, #maxsz, out)?), 309 | _ => quote!(xdr_codec::pack_flex(&#val, #maxsz, out)?), 310 | } 311 | } 312 | 313 | &Array(ref ty, _) => { 314 | let ty = ty.as_ref(); 315 | match ty { 316 | &Opaque | &String => { 317 | quote!(xdr_codec::pack_opaque_array(&#val[..], #val.len(), out)?) 318 | } 319 | _ => quote!(xdr_codec::pack_array(&#val[..], #val.len(), out, None)?), 320 | } 321 | } 322 | 323 | _ => quote!(#val.pack(out)?), 324 | }; 325 | 326 | trace!("packed {:?} val {:?} => {:?}", self, val, res); 327 | Ok(res) 328 | } 329 | 330 | fn is_syn(&self) -> bool { 331 | use self::Type::*; 332 | 333 | match self { 334 | &Opaque | &String | &Option(_) | &Ident(..) | &Int | &UInt | &Hyper | &UHyper | 335 | &Float | &Double | &Quadruple | &Bool => true, 336 | _ => false, 337 | } 338 | } 339 | 340 | fn unpacker(&self, symtab: &Symtab) -> Tokens { 341 | use self::Type::*; 342 | 343 | match self { 344 | &Array(ref ty, ref value) => { 345 | let ty = ty.as_ref(); 346 | let value = value.as_token(symtab); 347 | 348 | match ty { 349 | &Opaque | &String => { 350 | quote!({ 351 | let mut buf: [u8; #value as usize] = unsafe { ::std::mem::uninitialized() }; 352 | let sz = xdr_codec::unpack_opaque_array(input, &mut buf[..], #value as usize)?; 353 | (buf, sz) 354 | }) 355 | } 356 | ty => { 357 | let ty = ty.as_token(symtab).unwrap(); 358 | // Create the return array as uninitialized, since we don't know what to initialize it until 359 | // we can deserialize values. We don't even have a guaranteed value we can populate it with, since 360 | // the type may not implement Default (and it would be a waste anyway, since they're going to be 361 | // replaced). 362 | // 363 | // However, having an uninitialized array makes for lots of awkward corner cases. 364 | // Even in the common case, we can't simply use `unpack_array`, as it will replace each element 365 | // by assignment, but that will Drop any existing value - but in this case that will be undefined 366 | // as they're uninitialized. So we need to use `unpack_array_with` that allows us to specify a function 367 | // which does the initializing assignment. In this case we use `ptr::write` which overwrites memory 368 | // without Dropping the current contents. 369 | // 370 | // With that solved, we also need to deal with the error cases, where the array could be partially 371 | // initialized. For this case, `unpack_array_with` also takes a drop function which deinitializes 372 | // the partially initialized elements, so the array is left uninitialized in the failure case. 373 | // We can then just use `mem::forget` to dispose of the whole thing. 374 | // 375 | // We also need to catch panics to make sure the buf is forgotten. It may be partially initialized then 376 | // it may leak, but that's better than calling Drop on uninitialized elements. 377 | quote!({ 378 | #[inline] 379 | fn uninit_ptr_setter(p: &mut T, v: T) { 380 | unsafe { ::std::ptr::write(p, v) } 381 | } 382 | #[inline] 383 | fn uninit_ptr_dropper(p: &mut T) { 384 | unsafe { ::std::ptr::drop_in_place(p) } 385 | } 386 | let mut buf: [#ty; #value as usize] = unsafe { ::std::mem::uninitialized() }; 387 | let res = ::std::panic::catch_unwind( 388 | ::std::panic::AssertUnwindSafe(|| 389 | xdr_codec::unpack_array_with( 390 | input, &mut buf[..], #value as usize, uninit_ptr_setter, uninit_ptr_dropper, None))); 391 | 392 | let sz = match res { 393 | Ok(Ok(sz)) => sz, 394 | Ok(Err(err)) => { ::std::mem::forget(buf); return Err(err); } 395 | Err(panic) => { ::std::mem::forget(buf); ::std::panic::resume_unwind(panic); } 396 | }; 397 | (buf, sz) 398 | }) 399 | } 400 | } 401 | } 402 | 403 | &Flex(ref ty, ref maxsz) => { 404 | let ty = ty.as_ref(); 405 | let maxsz = match maxsz { 406 | &None => quote!(None), 407 | &Some(ref mx) => { 408 | let mx = mx.as_token(symtab); 409 | quote!(Some(#mx as usize)) 410 | } 411 | }; 412 | 413 | match ty { 414 | &String => quote!(xdr_codec::unpack_string(input, #maxsz)?), 415 | &Opaque => quote!(xdr_codec::unpack_opaque_flex(input, #maxsz)?), 416 | _ => quote!(xdr_codec::unpack_flex(input, #maxsz)?), 417 | } 418 | } 419 | 420 | _ => quote!(xdr_codec::Unpack::unpack(input)?), 421 | } 422 | } 423 | 424 | fn as_token(&self, symtab: &Symtab) -> Result { 425 | use self::Type::*; 426 | 427 | let ret = match self { 428 | &Int => quote!(i32), 429 | &UInt => quote!(u32), 430 | &Hyper => quote!(i64), 431 | &UHyper => quote!(u64), 432 | &Float => quote!(f32), 433 | &Double => quote!(f64), 434 | &Quadruple => quote!(f128), 435 | &Bool => quote!(bool), 436 | 437 | &String => quote!(String), 438 | &Opaque => quote!(Vec), 439 | 440 | &Option(ref ty) => { 441 | let ty = ty.as_ref(); 442 | let tok = ty.as_token(symtab)?; 443 | if ty.is_boxed(symtab) { 444 | quote!(Option>) 445 | } else { 446 | quote!(Option<#tok>) 447 | } 448 | } 449 | 450 | &Array(ref ty, ref sz) => { 451 | let ty = ty.as_ref(); 452 | match ty { 453 | &String | &Opaque => { 454 | let sztok = sz.as_token(symtab); 455 | quote!([u8; #sztok as usize]) 456 | } 457 | ref ty => { 458 | let tytok = ty.as_token(symtab)?; 459 | let sztok = sz.as_token(symtab); 460 | quote!([#tytok; #sztok as usize]) 461 | } 462 | } 463 | } 464 | 465 | &Flex(ref ty, _) => { 466 | let ty = ty.as_ref(); 467 | match ty { 468 | &String => quote!(String), 469 | &Opaque => quote!(Vec), 470 | ref ty => { 471 | let tok = ty.as_token(symtab)?; 472 | quote!(Vec<#tok>) 473 | } 474 | } 475 | } 476 | 477 | &Ident(ref name, _) => { 478 | let id = quote_ident(name.as_str()); 479 | quote!(#id) 480 | } 481 | 482 | _ => return Err(format!("can't have unnamed type {:?}", self).into()), 483 | }; 484 | Ok(ret) 485 | } 486 | } 487 | 488 | #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone)] 489 | pub struct EnumDefn(pub String, pub Option); 490 | 491 | impl EnumDefn { 492 | fn new>(id: S, val: Option) -> EnumDefn { 493 | EnumDefn(id.as_ref().to_string(), val) 494 | } 495 | } 496 | 497 | #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone)] 498 | pub struct UnionCase(Value, Decl); 499 | 500 | #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone)] 501 | pub enum Decl { 502 | Void, 503 | Named(String, Type), 504 | } 505 | 506 | impl Decl { 507 | fn named>(id: S, ty: Type) -> Decl { 508 | Decl::Named(id.as_ref().to_string(), ty) 509 | } 510 | 511 | fn name_as_ident(&self) -> Option<(quote::Ident, &Type)> { 512 | use self::Decl::*; 513 | match self { 514 | &Void => None, 515 | &Named(ref name, ref ty) => Some((quote_ident(name), ty)), 516 | } 517 | } 518 | 519 | fn as_token(&self, symtab: &Symtab) -> Result> { 520 | use self::Decl::*; 521 | match self { 522 | &Void => Ok(None), 523 | &Named(ref name, ref ty) => { 524 | let nametok = quote_ident(name.as_str()); 525 | let mut tok = ty.as_token(symtab)?; 526 | if false && ty.is_boxed(symtab) { 527 | tok = quote!(Box<#tok>) 528 | }; 529 | Ok(Some((nametok, tok))) 530 | } 531 | } 532 | } 533 | 534 | fn derivable(&self, symtab: &Symtab, memo: &mut HashMap) -> Derives { 535 | use self::Decl::*; 536 | match self { 537 | &Void => Derives::all(), 538 | &Named(_, ref ty) => ty.derivable(symtab, Some(memo)), 539 | } 540 | } 541 | } 542 | 543 | // Specification of a named type 544 | #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone)] 545 | pub struct Typespec(pub String, pub Type); 546 | 547 | // Named synonym for a type 548 | #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone)] 549 | pub struct Typesyn(pub String, pub Type); 550 | 551 | #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone)] 552 | pub struct Const(pub String, pub i64); 553 | 554 | #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone)] 555 | pub enum Defn { 556 | Typespec(String, Type), 557 | Typesyn(String, Type), 558 | Const(String, i64), 559 | } 560 | 561 | impl Defn { 562 | fn typespec>(id: S, ty: Type) -> Defn { 563 | Defn::Typespec(id.as_ref().to_string(), ty) 564 | } 565 | 566 | fn typesyn>(id: S, ty: Type) -> Defn { 567 | Defn::Typesyn(id.as_ref().to_string(), ty) 568 | } 569 | 570 | fn constant>(id: S, v: i64) -> Defn { 571 | Defn::Const(id.as_ref().to_string(), v) 572 | } 573 | } 574 | 575 | pub trait Emit { 576 | fn define(&self, symtab: &Symtab) -> Result; 577 | } 578 | 579 | pub trait Emitpack: Emit { 580 | fn pack(&self, symtab: &Symtab) -> Result>; 581 | fn unpack(&self, symtab: &Symtab) -> Result>; 582 | } 583 | 584 | impl Emit for Const { 585 | fn define(&self, _: &Symtab) -> Result { 586 | let name = quote_ident(&self.0); 587 | let val = &self.1; 588 | 589 | Ok(quote!(pub const #name: i64 = #val;)) 590 | } 591 | } 592 | 593 | impl Emit for Typesyn { 594 | fn define(&self, symtab: &Symtab) -> Result { 595 | let ty = &self.1; 596 | let name = quote_ident(&self.0); 597 | let tok = ty.as_token(symtab)?; 598 | Ok(quote!(pub type #name = #tok;)) 599 | } 600 | } 601 | 602 | impl Emit for Typespec { 603 | fn define(&self, symtab: &Symtab) -> Result { 604 | use self::Type::*; 605 | 606 | let name = quote_ident(&self.0); 607 | let ty = &self.1; 608 | 609 | let ret = match ty { 610 | &Enum(ref edefs) => { 611 | let defs: Vec<_> = edefs 612 | .iter() 613 | .filter_map(|&EnumDefn(ref field, _)| if let Some((val, Some(_))) = 614 | symtab.getconst(field) 615 | { 616 | Some((quote_ident(field), val as isize)) 617 | } else { 618 | None 619 | }) 620 | .map(|(field, val)| quote!(#field = #val,)) 621 | .collect(); 622 | 623 | let derive = ty.derivable(symtab, None); 624 | quote!(#derive pub enum #name { #(#defs)* }) 625 | } 626 | 627 | &Struct(ref decls) => { 628 | let decls: Vec<_> = decls 629 | .iter() 630 | .filter_map(|decl| result_option(decl.as_token(symtab))) 631 | .map(|res| res.map(|(field, ty)| quote!(pub #field: #ty,))) 632 | .collect::>>()?; 633 | 634 | let derive = ty.derivable(symtab, None); 635 | quote! { 636 | #derive 637 | pub struct #name { #(#decls)* } 638 | } 639 | } 640 | 641 | &Union(ref selector, ref cases, ref defl) => { 642 | let selector = selector.as_ref(); 643 | use self::Decl::*; 644 | use self::Value::*; 645 | 646 | let labelfields = false; // true - include label in enum branch 647 | 648 | // return true if case is compatible with the selector 649 | let compatcase = |case: &Value| { 650 | let seltype = match selector { 651 | &Void => return false, 652 | &Named(_, ref ty) => ty, 653 | }; 654 | 655 | match case { 656 | &Const(val) if val < 0 => { 657 | match seltype { 658 | &Int | &Hyper => true, 659 | _ => false, 660 | } 661 | } 662 | 663 | &Const(_) => { 664 | match seltype { 665 | &Int | &Hyper | &UInt | &UHyper => true, 666 | _ => false, 667 | } 668 | } 669 | 670 | &Ident(ref id) => { 671 | if *seltype == Bool { 672 | id == "TRUE" || id == "FALSE" 673 | } else { 674 | if let &Type::Ident(ref selname, _) = seltype { 675 | match symtab.getconst(id) { 676 | Some((_, Some(ref scope))) => scope == selname, 677 | _ => false, 678 | } 679 | } else { 680 | false 681 | } 682 | } 683 | } 684 | } 685 | }; 686 | 687 | let mut cases: Vec<_> = cases 688 | .iter() 689 | .map(|&UnionCase(ref val, ref decl)| { 690 | if !compatcase(val) { 691 | return Err(Error::from( 692 | format!("incompat selector {:?} case {:?}", selector, val), 693 | )); 694 | } 695 | 696 | let label = val.as_ident(); 697 | 698 | match decl { 699 | &Void => Ok(quote!(#label,)), 700 | &Named(ref name, ref ty) => { 701 | let mut tok = ty.as_token(symtab)?; 702 | if false && ty.is_boxed(symtab) { 703 | tok = quote!(Box<#tok>) 704 | }; 705 | if labelfields { 706 | let name = quote_ident(name); 707 | Ok(quote!(#label { #name : #tok },)) 708 | } else { 709 | Ok(quote!(#label(#tok),)) 710 | } 711 | } 712 | } 713 | }) 714 | .collect::>>()?; 715 | 716 | if let &Some(ref def_val) = defl { 717 | let def_val = def_val.as_ref(); 718 | match def_val { 719 | &Named(ref name, ref ty) => { 720 | let mut tok = ty.as_token(symtab)?; 721 | if ty.is_boxed(symtab) { 722 | tok = quote!(Box<#tok>) 723 | }; 724 | if labelfields { 725 | let name = quote_ident(name); 726 | cases.push(quote!(default { #name: #tok },)) 727 | } else { 728 | cases.push(quote!(default(#tok),)) 729 | } 730 | } 731 | &Void => cases.push(quote!(default,)), 732 | } 733 | } 734 | 735 | let derive = ty.derivable(symtab, None); 736 | quote! { 737 | #derive 738 | pub enum #name { #(#cases)* } 739 | } 740 | } 741 | 742 | &Flex(..) | &Array(..) => { 743 | let tok = ty.as_token(symtab)?; 744 | let derive = ty.derivable(symtab, None); 745 | quote! { 746 | #derive 747 | pub struct #name(pub #tok); 748 | } 749 | } 750 | 751 | _ => { 752 | let tok = ty.as_token(symtab)?; 753 | quote!(pub type #name = #tok;) 754 | } 755 | }; 756 | Ok(ret) 757 | } 758 | } 759 | 760 | impl Emitpack for Typespec { 761 | fn pack(&self, symtab: &Symtab) -> Result> { 762 | use self::Type::*; 763 | use self::Decl::*; 764 | 765 | let name = quote_ident(&self.0); 766 | let ty = &self.1; 767 | let mut directive = quote!(); 768 | 769 | let body: Tokens = match ty { 770 | &Enum(_) => { 771 | directive = quote!(#[inline]); 772 | ty.packer(quote!(self), symtab)? 773 | } 774 | 775 | &Struct(ref decl) => { 776 | let decls: Vec<_> = decl.iter() 777 | .filter_map(|d| match d { 778 | &Void => None, 779 | &Named(ref name, ref ty) => Some((quote_ident(name), ty)), 780 | }) 781 | .map(|(field, ty)| { 782 | let p = ty.packer(quote!(self.#field), symtab).unwrap(); 783 | quote!(#p + ) 784 | }) 785 | .collect(); 786 | quote!(#(#decls)* 0) 787 | } 788 | 789 | &Union(_, ref cases, ref defl) => { 790 | let mut matches: Vec<_> = cases 791 | .iter() 792 | .filter_map(|&UnionCase(ref val, ref decl)| { 793 | let label = val.as_ident(); 794 | let disc = val.as_token(symtab); 795 | 796 | let ret = match decl { 797 | &Void => quote!(&#name::#label => (#disc as i32).pack(out)?,), 798 | &Named(_, ref ty) => { 799 | let pack = match ty.packer(quote!(val), symtab) { 800 | Err(_) => return None, 801 | Ok(p) => p, 802 | }; 803 | quote!(&#name::#label(ref val) => (#disc as i32).pack(out)? + #pack,) 804 | } 805 | }; 806 | Some(ret) 807 | }) 808 | .collect(); 809 | 810 | if let &Some(ref decl) = defl { 811 | let decl = decl.as_ref(); 812 | // Can't cast a value-carrying enum to i32 813 | let default = match decl { 814 | &Void => { 815 | quote! { 816 | &#name::default => return Err(xdr_codec::Error::invalidcase(-1)), 817 | } 818 | } 819 | &Named(_, _) => { 820 | quote! { 821 | &#name::default(_) => return Err(xdr_codec::Error::invalidcase(-1)), 822 | } 823 | } 824 | }; 825 | 826 | matches.push(default) 827 | } 828 | 829 | quote!(match self { #(#matches)* }) 830 | } 831 | 832 | // Array and Flex types are wrapped in tuple structs 833 | &Flex(..) | &Array(..) => ty.packer(quote!(self.0), symtab)?, 834 | 835 | &Ident(_, _) => return Ok(None), 836 | 837 | _ => { 838 | if ty.is_prim(symtab) { 839 | return Ok(None); 840 | } else { 841 | ty.packer(quote!(self), symtab)? 842 | } 843 | } 844 | }; 845 | 846 | trace!("body {:?}", body); 847 | 848 | Ok(Some(quote! { 849 | impl xdr_codec::Pack for #name { 850 | #directive 851 | fn pack(&self, out: &mut Out) -> xdr_codec::Result { 852 | Ok(#body) 853 | } 854 | } 855 | })) 856 | } 857 | 858 | fn unpack(&self, symtab: &Symtab) -> Result> { 859 | use self::Type::*; 860 | use self::Decl::*; 861 | 862 | let name = quote_ident(&self.0); 863 | let ty = &self.1; 864 | let mut directive = quote!(); 865 | 866 | let body = match ty { 867 | &Enum(ref defs) => { 868 | directive = quote!(#[inline]); 869 | let matchdefs: Vec<_> = defs.iter() 870 | .filter_map(|&EnumDefn(ref name, _)| { 871 | let tok = quote_ident(name); 872 | if let Some((ref _val, ref scope)) = symtab.getconst(name) { 873 | // let val = *val as i32; 874 | if let &Some(ref scope) = scope { 875 | let scope = quote_ident(scope); 876 | // Some(quote!(#val => #scope :: #tok,)) 877 | Some(quote!(x if x == #scope :: #tok as i32 => #scope :: #tok,)) 878 | } else { 879 | // Some(quote!(#val => #tok,)) 880 | Some(quote!(x if x == #tok as i32 => #tok,)) 881 | } 882 | } else { 883 | println!("unknown ident {}", name); 884 | None 885 | } 886 | }) 887 | .collect(); 888 | 889 | quote!({ 890 | let (e, esz): (i32, _) = xdr_codec::Unpack::unpack(input)?; 891 | sz += esz; 892 | match e { 893 | #(#matchdefs)* 894 | e => return Err(xdr_codec::Error::invalidenum(e)) 895 | } 896 | }) 897 | } 898 | 899 | &Struct(ref decls) => { 900 | let decls: Vec<_> = decls 901 | .iter() 902 | .filter_map(|decl| decl.name_as_ident()) 903 | .map(|(field, ty)| { 904 | let unpack = ty.unpacker(symtab); 905 | quote!(#field: { let (v, fsz) = #unpack; sz += fsz; v },) 906 | }) 907 | .collect(); 908 | 909 | quote!(#name { #(#decls)* }) 910 | } 911 | 912 | &Union(ref sel, ref cases, ref defl) => { 913 | let sel = sel.as_ref(); 914 | let mut matches: Vec<_> = 915 | cases.iter() 916 | .map(|&UnionCase(ref val, ref decl)| { 917 | let label = val.as_ident(); 918 | let disc = match val.as_i64(symtab) { 919 | Some(v) => v as i32, 920 | None => return Err(Error::from(format!("discriminant value {:?} unknown", val))), 921 | }; 922 | 923 | let ret = match decl { 924 | //&Void => quote!(#disc => #name::#label,), 925 | &Void => quote!(x if x == (#disc as i32) => #name::#label,), 926 | &Named(_, ref ty) => { 927 | let unpack = ty.unpacker(symtab); 928 | //quote!(#disc => #name::#label({ let (v, fsz) = #unpack; sz += fsz; v }),) 929 | quote!(x if x == (#disc as i32) => #name::#label({ let (v, fsz) = #unpack; sz += fsz; v }),) 930 | }, 931 | }; 932 | Ok(ret) 933 | }) 934 | .collect::>>()?; 935 | 936 | if let &Some(ref decl) = defl { 937 | let decl = decl.as_ref(); 938 | let defl = match decl { 939 | &Void => quote!(_ => #name::default), 940 | &Named(_, ref ty) => { 941 | let unpack = ty.unpacker(symtab); 942 | quote!(_ => #name::default({ 943 | let (v, csz) = #unpack; 944 | sz += csz; 945 | v 946 | })) 947 | } 948 | }; 949 | 950 | matches.push(defl); 951 | } else { 952 | let defl = quote!(v => return Err(xdr_codec::Error::invalidcase(v as i32))); 953 | matches.push(defl); 954 | } 955 | 956 | let selunpack = match sel { 957 | &Void => panic!("void switch selector?"), 958 | &Named(_, ref ty) => ty.unpacker(symtab), 959 | }; 960 | 961 | quote!(match { let (v, dsz): (i32, _) = #selunpack; sz += dsz; v } { #(#matches)* }) 962 | } 963 | 964 | &Option(_) => ty.unpacker(symtab), 965 | 966 | &Flex(_, _) | &Array(_, _) => { 967 | let unpk = ty.unpacker(symtab); 968 | quote!({ let (v, usz) = #unpk; sz = usz; #name(v) }) 969 | } 970 | 971 | &Ident(_, _) => return Ok(None), 972 | 973 | _ if ty.is_prim(symtab) => return Ok(None), 974 | _ => return Err(Error::from(format!("unimplemented ty={:?}", ty))), 975 | }; 976 | 977 | Ok(Some(quote! { 978 | impl xdr_codec::Unpack for #name { 979 | #directive 980 | fn unpack(input: &mut In) -> xdr_codec::Result<(#name, usize)> { 981 | let mut sz = 0; 982 | Ok((#body, sz)) 983 | } 984 | } 985 | })) 986 | } 987 | } 988 | 989 | #[derive(Debug, Clone)] 990 | pub struct Symtab { 991 | consts: BTreeMap)>, 992 | typespecs: BTreeMap, 993 | typesyns: BTreeMap, 994 | } 995 | 996 | impl Symtab { 997 | pub fn new(defns: &Vec) -> Symtab { 998 | let mut ret = Symtab { 999 | consts: BTreeMap::new(), 1000 | typespecs: BTreeMap::new(), 1001 | typesyns: BTreeMap::new(), 1002 | }; 1003 | 1004 | ret.update_consts(&defns); 1005 | 1006 | ret 1007 | } 1008 | 1009 | fn update_consts(&mut self, defns: &Vec) { 1010 | for defn in defns { 1011 | match defn { 1012 | &Defn::Typespec(ref name, ref ty) => { 1013 | self.deftype(name, ty); 1014 | self.update_enum_consts(name, ty); 1015 | } 1016 | 1017 | &Defn::Const(ref name, val) => self.defconst(name, val, None), 1018 | 1019 | &Defn::Typesyn(ref name, ref ty) => { 1020 | self.deftypesyn(name, ty); 1021 | } 1022 | } 1023 | } 1024 | } 1025 | 1026 | fn update_enum_consts(&mut self, scope: &String, ty: &Type) { 1027 | let mut err = stderr(); 1028 | let mut prev = -1; 1029 | 1030 | if let &Type::Enum(ref edefn) = ty { 1031 | for &EnumDefn(ref name, ref maybeval) in edefn { 1032 | let v = match maybeval { 1033 | &None => prev + 1, 1034 | &Some(ref val) => { 1035 | match self.value(val) { 1036 | Some(c) => c, 1037 | None => { 1038 | let _ = writeln!(&mut err, "Unknown value {:?}", val); 1039 | continue; 1040 | } 1041 | } 1042 | } 1043 | }; 1044 | 1045 | prev = v; 1046 | 1047 | // println!("enum {} -> {}", name, v); 1048 | self.defconst(name, v, Some(scope.clone())); 1049 | } 1050 | } 1051 | } 1052 | 1053 | fn defconst>(&mut self, name: S, val: i64, scope: Option) { 1054 | self.consts.insert(From::from(name.as_ref()), (val, scope)); 1055 | } 1056 | 1057 | fn deftype>(&mut self, name: S, ty: &Type) { 1058 | self.typespecs.insert(From::from(name.as_ref()), ty.clone()); 1059 | } 1060 | 1061 | pub fn deftypesyn>(&mut self, name: S, ty: &Type) { 1062 | self.typesyns.insert(From::from(name.as_ref()), ty.clone()); 1063 | } 1064 | 1065 | pub fn getconst(&self, name: &String) -> Option<(i64, Option)> { 1066 | match self.consts.get(name) { 1067 | None => None, 1068 | Some(c) => Some(c.clone()), 1069 | } 1070 | } 1071 | 1072 | pub fn value(&self, val: &Value) -> Option { 1073 | match val { 1074 | &Value::Const(c) => Some(c), 1075 | &Value::Ident(ref id) => self.getconst(id).map(|(v, _)| v), 1076 | } 1077 | } 1078 | 1079 | pub fn typespec(&self, name: &String) -> Option<&Type> { 1080 | match self.typespecs.get(name) { 1081 | None => { 1082 | match self.typesyns.get(name) { 1083 | None => None, 1084 | Some(ty) => Some(ty), 1085 | } 1086 | } 1087 | Some(ty) => Some(ty), 1088 | } 1089 | } 1090 | 1091 | pub fn constants(&self) -> Iter)> { 1092 | self.consts.iter() 1093 | } 1094 | 1095 | pub fn typespecs(&self) -> Iter { 1096 | self.typespecs.iter() 1097 | } 1098 | 1099 | pub fn typesyns(&self) -> Iter { 1100 | self.typesyns.iter() 1101 | } 1102 | } 1103 | 1104 | 1105 | #[cfg(test)] 1106 | mod test; 1107 | -------------------------------------------------------------------------------- /xdrgen/src/spec/test.rs: -------------------------------------------------------------------------------- 1 | use super::specification; 2 | use super::super::generate; 3 | use std::io::Cursor; 4 | 5 | #[test] 6 | fn typedef_void() { 7 | let s = specification( 8 | r#" 9 | typedef void; /* syntactically defined, semantically meaningless */ 10 | "#, 11 | ); 12 | 13 | println!("spec {:?}", s); 14 | assert!(s.is_err()) 15 | } 16 | 17 | #[test] 18 | fn kwishnames() { 19 | let kws = vec![ 20 | "bool", 21 | "case", 22 | "const", 23 | "default", 24 | "double", 25 | "enum", 26 | "float", 27 | "hyper", 28 | "int", 29 | "opaque", 30 | "quadruple", 31 | "string", 32 | "struct", 33 | "switch", 34 | "typedef", 35 | "union", 36 | "unsigned", 37 | "void", 38 | ]; 39 | let specs = vec![ 40 | "const {}x = 1;", 41 | "struct {}x { int i; };", 42 | "struct foo { int {}x; };", 43 | "typedef int {}x;", 44 | "union {}x switch (int x) { case 1: void; };", 45 | "union x switch (int {}x) { case 1: void; };", 46 | "union x switch (int y) { case 1: int {}x; };", 47 | ]; 48 | 49 | for sp in &specs { 50 | for kw in &kws { 51 | let spec = sp.replace("{}", kw); 52 | let s = specification(&spec); 53 | println!("spec {} => {:?}", spec, s); 54 | assert!(s.is_ok()) 55 | } 56 | } 57 | } 58 | 59 | #[test] 60 | fn kwnames() { 61 | let kws = vec![ 62 | "bool", 63 | "case", 64 | "const", 65 | "default", 66 | "double", 67 | "enum", 68 | "float", 69 | "hyper", 70 | "int", 71 | "opaque", 72 | "quadruple", 73 | "string", 74 | "struct", 75 | "switch", 76 | "typedef", 77 | "union", 78 | "unsigned", 79 | "void", 80 | ]; 81 | let specs = vec![ 82 | "const {} = 1;", 83 | "struct {} { int i; };", 84 | "struct foo { int {}; };", 85 | "typedef int {};", 86 | "union {} switch (int x) { case 1: void; };", 87 | "union x switch (int {}) { case 1: void; };", 88 | "union x switch (int y) { case 1: int {}; };", 89 | ]; 90 | 91 | for sp in &specs { 92 | for kw in &kws { 93 | let spec = sp.replace("{}", kw); 94 | let s = specification(&spec); 95 | println!("spec {} => {:?}", spec, s); 96 | assert!(s.is_err()) 97 | } 98 | } 99 | } 100 | 101 | #[test] 102 | fn inline_struct() { 103 | let spec = r#" 104 | struct thing { 105 | struct { int a; int b; } thing; 106 | }; 107 | "#; 108 | let s = specification(spec); 109 | 110 | println!("spec {:?}", s); 111 | assert!(s.is_ok()); 112 | 113 | let g = generate("", Cursor::new(spec.as_bytes()), Vec::new()); 114 | assert!(g.is_err()); 115 | } 116 | 117 | #[test] 118 | fn inline_union() { 119 | let spec = r#" 120 | struct thing { 121 | union switch(int x) { case 0: int a; case 1: int b; } thing; 122 | }; 123 | "#; 124 | let s = specification(spec); 125 | 126 | println!("spec {:?}", s); 127 | assert!(s.is_ok()); 128 | 129 | let g = generate("", Cursor::new(spec.as_bytes()), Vec::new()); 130 | assert!(g.is_err()); 131 | } 132 | 133 | #[test] 134 | fn case_type() { 135 | let specs = vec![ 136 | "enum Foo { A, B, C }; union Bar switch (Foo x) { case A: void; case B: void; case C: void; };", 137 | "union Bar switch (int x) { case 1: void; case 2: void; case 3: void; };", 138 | ]; 139 | 140 | for sp in specs { 141 | let s = specification(sp); 142 | println!("spec sp \"{}\" => {:?}", sp, s); 143 | assert!(s.is_ok()); 144 | 145 | let g = generate("", Cursor::new(sp.as_bytes()), Vec::new()); 146 | assert!(g.is_ok()); 147 | } 148 | } 149 | 150 | #[test] 151 | fn case_type_mismatch() { 152 | let specs = vec![ 153 | "enum Foo { A, B, C}; union Bar switch (Foo x) { case 1: void; case 2: void; case 3: void; };", 154 | "enum Foo { A, B, C}; union Bar switch (int x) { case A: void; case B: void; case C: void; };", 155 | ]; 156 | 157 | for sp in specs { 158 | let s = specification(sp); 159 | println!("spec sp \"{}\" => {:?}", sp, s); 160 | assert!(s.is_ok()); 161 | 162 | let g = generate("", Cursor::new(sp.as_bytes()), Vec::new()); 163 | assert!(g.is_err()); 164 | } 165 | } 166 | 167 | #[test] 168 | fn constants() { 169 | let specs = vec![ 170 | "const A = 0;", 171 | "const A = 0x0;", 172 | "const A = 00;", 173 | "const A = -0;", 174 | "const A = 0x123;", 175 | "const A = 0123;", 176 | "const A = -0123;", 177 | "const A = 123;", 178 | "const A = -123;", 179 | ]; 180 | 181 | for sp in specs { 182 | let s = specification(sp); 183 | println!("spec sp \"{}\" => {:?}", sp, s); 184 | assert!(s.is_ok()); 185 | 186 | let g = generate("", Cursor::new(sp.as_bytes()), Vec::new()); 187 | assert!(g.is_ok()); 188 | } 189 | } 190 | 191 | #[test] 192 | fn union_simple() { 193 | let s = specification( 194 | r#" 195 | union foo switch (int x) { 196 | case 0: 197 | int val; 198 | }; 199 | "#, 200 | ); 201 | println!("spec {:?}", s); 202 | assert!(s.is_ok()) 203 | } 204 | 205 | #[test] 206 | fn union_default() { 207 | let s = specification( 208 | r#" 209 | union foo switch (int x) { 210 | case 0: 211 | int val; 212 | default: 213 | void; 214 | }; 215 | "#, 216 | ); 217 | println!("spec {:?}", s); 218 | assert!(s.is_ok()) 219 | } 220 | 221 | #[test] 222 | fn union_default_nonempty() { 223 | let s = specification( 224 | r#" 225 | union foo switch (int x) { 226 | case 0: 227 | int val; 228 | default: 229 | bool bye; 230 | }; 231 | "#, 232 | ); 233 | println!("spec {:?}", s); 234 | assert!(s.is_ok()) 235 | 236 | } 237 | 238 | #[test] 239 | fn fallthrough_case() { 240 | let s = specification( 241 | r#" 242 | union foo switch (int x) { 243 | case 0: 244 | case 1: 245 | int val; 246 | case 2: 247 | void; 248 | }; 249 | "#, 250 | ); 251 | println!("spec {:?}", s); 252 | assert!(s.is_ok()) 253 | } 254 | -------------------------------------------------------------------------------- /xdrgen/src/spec/xdr_nom.rs: -------------------------------------------------------------------------------- 1 | // Grammar for a .x file specifying XDR type codecs. Does not include any RPC syntax. Should match RFC4506. 2 | use nom::{Err, ErrorKind, IResult, Needed, is_digit, is_space, not_line_ending}; 3 | use nom::IResult::*; 4 | 5 | use std::str; 6 | 7 | use super::{Decl, Defn, EnumDefn, Type, UnionCase, Value}; 8 | use super::{CLONE, COPY, DEBUG, EQ, PARTIALEQ}; 9 | 10 | #[inline] 11 | fn ignore(_: T) -> () { 12 | () 13 | } 14 | 15 | // Complete tag 16 | fn ctag>(input: &[u8], tag: T) -> IResult<&[u8], &[u8]> { 17 | complete!(input, tag!(tag.as_ref())) 18 | } 19 | 20 | fn eof(input: &[u8]) -> IResult<&[u8], ()> { 21 | if input.len() == 0 { 22 | IResult::Done(input, ()) 23 | } else { 24 | IResult::Error(Err::Position(ErrorKind::Eof, input)) 25 | } 26 | } 27 | 28 | pub fn specification(input: &str) -> Result, String> { 29 | match spec(input.as_bytes()) { 30 | Done(_, spec) => Ok(spec), 31 | Error(Err::Position(kind, input)) => { 32 | Err(format!( 33 | "{:?}: {}", 34 | kind, 35 | String::from(str::from_utf8(input).unwrap()) 36 | )) 37 | } 38 | Error(err) => Err(format!("Error: {:?}", err)), 39 | Incomplete(need) => Err(format!("Incomplete {:?}", need)), 40 | } 41 | } 42 | 43 | named!(spec< Vec >, 44 | do_parse!( 45 | opt!(directive) >> 46 | defns: many0!(definition) >> 47 | spaces >> eof >> 48 | (defns)) 49 | ); 50 | 51 | #[test] 52 | fn test_spec() { 53 | assert_eq!(spec(&b"#include "[..]), 54 | Done(&b""[..], vec!())); 55 | 56 | assert_eq!(spec(&b"// hello\n#include "[..]), 57 | Done(&b""[..], vec!())); 58 | 59 | assert_eq!(spec(&b"#include \ntypedef int foo;"[..]), 60 | Done(&b""[..], vec!(Defn::typesyn("foo", Type::Int)))); 61 | 62 | assert_eq!(spec(&br#" 63 | /* test file */ 64 | #define foo bar 65 | const mip = 123; 66 | % passthrough 67 | typedef int foo; 68 | struct bar { 69 | int a; 70 | int b; 71 | }; 72 | #include "other" 73 | enum bop { a = 2, b = 1 }; 74 | "#[..]), 75 | Done(&b""[..], 76 | vec!(Defn::constant("mip", 123), 77 | Defn::typesyn("foo", Type::Int), 78 | Defn::typespec("bar", Type::Struct(vec!(Decl::named("a", Type::Int), 79 | Decl::named("b", Type::Int)))), 80 | Defn::typespec("bop", Type::Enum(vec!(EnumDefn::new("a", Some(Value::Const(2))), 81 | EnumDefn::new("b", Some(Value::Const(1))))))))); 82 | } 83 | 84 | named!(definition, 85 | alt!(type_def => { |t| t } | 86 | const_def => { |c| c })); 87 | 88 | fn is_hexdigit(ch: u8) -> bool { 89 | match ch as char { 90 | '0'...'9' | 'A'...'F' | 'a'...'f' => true, 91 | _ => false, 92 | } 93 | } 94 | 95 | fn is_octdigit(ch: u8) -> bool { 96 | match ch as char { 97 | '0'...'7' => true, 98 | _ => false, 99 | } 100 | } 101 | 102 | fn digit bool>(input: &[u8], isdigit: F) -> IResult<&[u8], &[u8]> { 103 | for (idx, item) in input.iter().enumerate() { 104 | if !isdigit(*item) { 105 | if idx == 0 { 106 | return Error(Err::Position(ErrorKind::Digit, input)); 107 | } else { 108 | return Done(&input[idx..], &input[0..idx]); 109 | } 110 | } 111 | } 112 | Incomplete(Needed::Unknown) 113 | } 114 | 115 | named!(lbrace, preceded!(spaces, apply!(ctag, "{"))); 116 | named!(rbrace, preceded!(spaces, apply!(ctag, "}"))); 117 | named!(lbrack, preceded!(spaces, apply!(ctag, "["))); 118 | named!(rbrack, preceded!(spaces, apply!(ctag, "]"))); 119 | named!(lparen, preceded!(spaces, apply!(ctag, "("))); 120 | named!(rparen, preceded!(spaces, apply!(ctag, ")"))); 121 | named!(lt, preceded!(spaces, apply!(ctag, "<"))); 122 | named!(gt, preceded!(spaces, apply!(ctag, ">"))); 123 | named!(colon, preceded!(spaces, apply!(ctag, ":"))); 124 | named!(semi, preceded!(spaces, apply!(ctag, ";"))); 125 | named!(comma, preceded!(spaces, apply!(ctag, ","))); 126 | named!(eq, preceded!(spaces, apply!(ctag, "="))); 127 | named!(star, preceded!(spaces, apply!(ctag, "*"))); 128 | 129 | named!(hexnumber, 130 | do_parse!( 131 | apply!(ctag, "0x") >> 132 | val: map_res!(apply!(digit, is_hexdigit), str::from_utf8) >> 133 | (i64::from_str_radix(val, 16).unwrap()) 134 | ) 135 | ); 136 | 137 | named!(octnumber, 138 | do_parse!( 139 | sign: opt!(apply!(ctag, "-")) >> 140 | apply!(ctag, "0") >> 141 | val: opt!(map_res!(apply!(digit, is_octdigit), str::from_utf8)) >> 142 | (i64::from_str_radix(val.unwrap_or("0"), 8).unwrap() * (if sign.is_some() { -1 } else { 1 })) 143 | ) 144 | ); 145 | 146 | named!(decnumber, 147 | do_parse!( 148 | sign: opt!(apply!(ctag, "-")) >> 149 | val: map_res!(apply!(digit, is_digit), str::from_utf8) >> 150 | (i64::from_str_radix(val, 10).unwrap() * (if sign.is_some() { -1 } else { 1 })) 151 | ) 152 | ); 153 | 154 | named!(number, preceded!(spaces, alt!(hexnumber | octnumber | decnumber))); 155 | 156 | #[test] 157 | fn test_nums() { 158 | // Complete number 159 | assert_eq!(number(&b"0x12344+"[..]), Done(&b"+"[..], 0x12344)); 160 | assert_eq!(number(&b"012344+"[..]), Done(&b"+"[..], 0o12344)); 161 | assert_eq!(number(&b"-012344+"[..]), Done(&b"+"[..], -0o12344)); 162 | assert_eq!(number(&b"12344+"[..]), Done(&b"+"[..], 12344)); 163 | assert_eq!(number(&b"-12344+"[..]), Done(&b"+"[..], -12344)); 164 | assert_eq!(number(&b"0+"[..]), Done(&b"+"[..], 0)); 165 | assert_eq!(number(&b"-0+"[..]), Done(&b"+"[..], 0)); 166 | 167 | // Space prefix number 168 | assert_eq!(number(&b" 0x12344+"[..]), Done(&b"+"[..], 0x12344)); 169 | assert_eq!(number(&b" 012344+"[..]), Done(&b"+"[..], 0o12344)); 170 | assert_eq!(number(&b" -012344+"[..]), Done(&b"+"[..], -0o12344)); 171 | assert_eq!(number(&b" 12344+"[..]), Done(&b"+"[..], 12344)); 172 | assert_eq!(number(&b" -12344+"[..]), Done(&b"+"[..], -12344)); 173 | assert_eq!(number(&b" 0+"[..]), Done(&b"+"[..], 0)); 174 | assert_eq!(number(&b" -0+"[..]), Done(&b"+"[..], 0)); 175 | 176 | // Incomplete number 177 | assert_eq!(number(&b"0x12344"[..]), Incomplete(Needed::Unknown)); 178 | assert_eq!(number(&b"012344"[..]), Incomplete(Needed::Unknown)); 179 | assert_eq!(number(&b"-012344"[..]), Incomplete(Needed::Unknown)); 180 | assert_eq!(number(&b"12344"[..]), Incomplete(Needed::Unknown)); 181 | assert_eq!(number(&b"-12344"[..]), Incomplete(Needed::Unknown)); 182 | assert_eq!(number(&b"0"[..]), Incomplete(Needed::Unknown)); 183 | assert_eq!(number(&b"-0"[..]), Incomplete(Needed::Unknown)); 184 | } 185 | 186 | fn token(input: &[u8]) -> IResult<&[u8], &[u8]> { 187 | let input = ws(input); 188 | 189 | for (idx, item) in input.iter().enumerate() { 190 | match *item as char { 191 | 'a'...'z' | 'A'...'Z' | '_' => continue, 192 | '0'...'9' if idx > 0 => continue, 193 | _ => { 194 | if idx > 0 { 195 | return Done(&input[idx..], &input[0..idx]); 196 | } else { 197 | return Error(Err::Position(ErrorKind::AlphaNumeric, input)); 198 | } 199 | } 200 | } 201 | } 202 | Incomplete(Needed::Unknown) 203 | } 204 | 205 | macro_rules! kw { 206 | ($fnname:ident, $kw:expr) => ( 207 | fn $fnname(input: &[u8]) -> IResult<&[u8], ()> { 208 | match token(input) { 209 | Done(rest, val) => 210 | if val == $kw { 211 | Done(rest, ()) 212 | } else { 213 | Error(Err::Position(ErrorKind::Custom(0), input)) 214 | }, 215 | Error(e) => Error(e), 216 | Incomplete(_) => { 217 | // If its either incomplete but longer that what we're looking for, or what we 218 | // have doesn't match, then its not for us. 219 | if input.len() > $kw.len() || input != &$kw[..input.len()] { 220 | Error(Err::Position(ErrorKind::Custom(0), input)) 221 | } else { 222 | Incomplete(Needed::Size($kw.len() - input.len())) 223 | } 224 | }, 225 | } 226 | }); 227 | } 228 | 229 | kw!(kw_bool, b"bool"); 230 | kw!(kw_case, b"case"); 231 | kw!(kw_char, b"char"); // special case - part time keyword 232 | kw!(kw_const, b"const"); 233 | kw!(kw_default, b"default"); 234 | kw!(kw_double, b"double"); 235 | kw!(kw_enum, b"enum"); 236 | kw!(kw_float, b"float"); 237 | kw!(kw_hyper, b"hyper"); 238 | kw!(kw_int, b"int"); 239 | kw!(kw_long, b"long"); // special case - part time keyword 240 | kw!(kw_opaque, b"opaque"); 241 | kw!(kw_quadruple, b"quadruple"); 242 | kw!(kw_short, b"short"); // special case - part time keyword 243 | kw!(kw_string, b"string"); 244 | kw!(kw_struct, b"struct"); 245 | kw!(kw_switch, b"switch"); 246 | kw!(kw_typedef, b"typedef"); 247 | kw!(kw_union, b"union"); 248 | kw!(kw_unsigned, b"unsigned"); 249 | kw!(kw_void, b"void"); 250 | 251 | named!(keyword<()>, 252 | alt!(kw_bool | 253 | kw_case | 254 | kw_const | 255 | kw_default | 256 | kw_double | 257 | kw_enum | 258 | kw_float | 259 | kw_hyper | 260 | kw_int | 261 | kw_opaque | 262 | kw_quadruple | 263 | kw_string | 264 | kw_struct | 265 | kw_switch | 266 | kw_typedef | 267 | kw_union | 268 | kw_unsigned | 269 | kw_void)); 270 | 271 | #[test] 272 | fn test_kw() { 273 | let kws = vec!("bool", "case", "const", "default", 274 | "double", "enum", "float", "hyper", "int", 275 | "opaque", "quadruple", "string", "struct", 276 | "switch", "typedef", "union", "unsigned", "void"); 277 | 278 | for k in &kws { 279 | println!("testing \"{}\"", k); 280 | match keyword((*k).as_bytes()) { 281 | Incomplete(_) => (), 282 | err => panic!("failed \"{}\": {:?}", k, err), 283 | } 284 | } 285 | 286 | for k in &kws { 287 | println!("testing \"{} \"", k); 288 | match keyword((String::from(*k) + " ").as_bytes()) { 289 | Done(rest, ()) if rest == &b" "[..] => (), 290 | err => panic!("failed \"{} \": {:?}", k, err), 291 | } 292 | } 293 | 294 | for k in &kws { 295 | println!("testing \"{}x \"", k); 296 | match keyword((String::from(*k) + "x ").as_bytes()) { 297 | Error(_) => (), 298 | err => panic!("failed \"{}x \": {:?}", k, err), 299 | } 300 | } 301 | 302 | for k in &kws { 303 | println!("testing \"{}x \"", k); 304 | match keyword((String::from(" ") + *k + " ").as_bytes()) { 305 | Done(rest, ()) if rest == &b" "[..] => (), 306 | err => panic!("failed \" {} \": {:?}", k, err), 307 | } 308 | } 309 | 310 | for nk in &vec!("boo", "in", "inx", "booll") { 311 | match keyword((*nk).as_bytes()) { 312 | e @ Done(..) => panic!("{:?} => {:?}", nk, e), 313 | e => println!("{:?} => {:?}", nk, e), 314 | } 315 | } 316 | 317 | } 318 | 319 | fn ident(input: &[u8]) -> IResult<&[u8], &str> { 320 | // Grab an identifier and make sure it isn't a keyword 321 | match token(input) { 322 | Done(rest, val) => { 323 | match keyword(input) { 324 | Done(..) => Error(Err::Position(ErrorKind::Custom(1), val)), 325 | Error(..) | Incomplete(..) => Done(rest, str::from_utf8(val).unwrap()), 326 | } 327 | } 328 | Error(e) => Error(e), 329 | Incomplete(need) => Incomplete(need), 330 | } 331 | } 332 | 333 | #[test] 334 | fn test_ident() { 335 | assert_eq!(ident(&b"foo "[..]), Done(&b" "[..], "foo")); 336 | assert_eq!(ident(&b" foo "[..]), Done(&b" "[..], "foo")); 337 | assert_eq!(ident(&b" bool "[..]), Error(Err::Position(ErrorKind::Custom(1), &b"bool"[..]))); 338 | } 339 | 340 | named!(blockcomment<()>, 341 | do_parse!(apply!(ctag, "/*") >> take_until_and_consume!(&b"*/"[..]) >> (()))); 342 | 343 | // `linecomment`, and `directive` end at eol, but do not consume it 344 | named!(linecomment<()>, 345 | do_parse!( 346 | apply!(ctag, "//") >> opt!(not_line_ending) >> peek!(alt!(eol | eof)) >> (()) 347 | ) 348 | ); 349 | 350 | // Directive should always follow eol unless its the first thing in the file 351 | named!(directive<()>, 352 | do_parse!( 353 | opt!(whitespace) >> 354 | alt!( 355 | apply!(ctag, "#") | 356 | apply!(ctag, "%")) >> 357 | opt!(not_line_ending) >> 358 | peek!(alt!(eol | eof)) >> (()) 359 | ) 360 | ); 361 | 362 | #[test] 363 | fn test_comments() { 364 | assert_eq!(blockcomment(&b"/* foo */bar"[..]), Done(&b"bar"[..], ())); 365 | assert_eq!(blockcomment(&b"/* blip /* foo */bar"[..]), Done(&b"bar"[..], ())); 366 | assert_eq!(blockcomment(&b"x"[..]), Error(Err::Position(ErrorKind::Tag, &b"x"[..]))); 367 | assert_eq!(linecomment(&b"// foo\nbar"[..]), Done(&b"\nbar"[..], ())); 368 | assert_eq!(linecomment(&b"// foo bar\n "[..]), Done(&b"\n "[..], ())); 369 | assert_eq!(linecomment(&b"x"[..]), Error(Err::Position(ErrorKind::Tag, &b"x"[..]))); 370 | 371 | assert_eq!(directive(&b"#define foo bar\n "[..]), Done(&b"\n "[..], ())); 372 | assert_eq!(directive(&b"%#define foo bar\n "[..]), Done(&b"\n "[..], ())); 373 | 374 | assert_eq!(directive(&b"x"[..]), Error(Err::Position(ErrorKind::Alt, &b"x"[..]))); 375 | 376 | assert_eq!(preceded!(&b"\n#define x\n"[..], eol, directive), 377 | Done(&b"\n"[..], ())); 378 | } 379 | 380 | named!(eol<()>, map!(alt!(apply!(ctag, "\n") | 381 | apply!(ctag, "\r\n") | 382 | apply!(ctag, "\u{2028}") | 383 | apply!(ctag, "\u{2029}")), 384 | ignore) 385 | ); 386 | 387 | named!(whitespace<()>, 388 | map!(take_while1!(is_space), ignore)); 389 | 390 | // `spaces` consumes spans of space and tab characters interpolated 391 | // with comments, c-preproc and passthrough lines. 392 | named!(spaces<()>, 393 | map!( 394 | many0!( 395 | alt!( do_parse!(eol >> opt!(complete!(directive)) >> (())) 396 | | whitespace 397 | | blockcomment 398 | | linecomment 399 | ) 400 | ), 401 | ignore 402 | ) 403 | ); 404 | 405 | fn ws(input: &[u8]) -> &[u8] { 406 | match spaces(input) { 407 | Done(rest, _) => rest, 408 | _ => input, 409 | } 410 | } 411 | 412 | #[test] 413 | fn test_spaces() { 414 | assert_eq!(eol(&b"\nx"[..]), Done(&b"x"[..], ())); 415 | assert_eq!(eol(&b"\r\nx"[..]), Done(&b"x"[..], ())); 416 | assert_eq!(eol(&b"\nx"[..]), Done(&b"x"[..], ())); 417 | 418 | assert_eq!(whitespace(&b"x"[..]), Error(Err::Position(ErrorKind::TakeWhile1, &b"x"[..]))); 419 | assert_eq!(whitespace(&b" x"[..]), Done(&b"x"[..], ())); 420 | assert_eq!(whitespace(&b" x"[..]), Done(&b"x"[..], ())); 421 | assert_eq!(whitespace(&b"\tx"[..]), Done(&b"x"[..], ())); 422 | assert_eq!(whitespace(&b" \tx"[..]), Done(&b"x"[..], ())); 423 | assert_eq!(whitespace(&b"\t x"[..]), Done(&b"x"[..], ())); 424 | 425 | assert_eq!(spaces(&b"x"[..]), Done(&b"x"[..], ())); 426 | assert_eq!(spaces(&b"\nx"[..]), Done(&b"x"[..], ())); 427 | assert_eq!(spaces(&b" x"[..]), Done(&b"x"[..], ())); 428 | assert_eq!(spaces(&b" x"[..]), Done(&b"x"[..], ())); 429 | assert_eq!(spaces(&b"\n\n x"[..]), Done(&b"x"[..], ())); 430 | assert_eq!(spaces(&b"\r\n x"[..]), Done(&b"x"[..], ())); 431 | assert_eq!(spaces(&b"//foo\n x"[..]), Done(&b"x"[..], ())); 432 | assert_eq!(spaces(&b"/*\n*/ x"[..]), Done(&b"x"[..], ())); 433 | assert_eq!(spaces(&b"\n#define a b\n x"[..]), Done(&b"x"[..], ())); 434 | assert_eq!(spaces(&b"\n%foo a b\n x"[..]), Done(&b"x"[..], ())); 435 | } 436 | 437 | named!(enum_type_spec< Vec >, 438 | preceded!(kw_enum, enum_body)); 439 | 440 | named!(enum_body< Vec >, 441 | do_parse!( 442 | lbrace >> 443 | b: separated_nonempty_list!(comma, enum_assign) >> 444 | rbrace >> 445 | (b) 446 | ) 447 | ); 448 | 449 | named!(enum_assign, 450 | do_parse!( 451 | id: ident >> 452 | v: opt!(preceded!(eq, value)) >> 453 | (EnumDefn::new(id, v)) 454 | ) 455 | ); 456 | 457 | named!(value, 458 | alt!(number => { |c| Value::Const(c) } | 459 | ident => { |id| Value::ident(id) } 460 | ) 461 | ); 462 | 463 | named!(struct_type_spec< Vec >, 464 | preceded!(kw_struct, struct_body)); 465 | 466 | named!(struct_body< Vec >, 467 | do_parse!( 468 | lbrace >> 469 | decls: many1!(terminated!(declaration, semi)) >> 470 | rbrace >> 471 | (decls) 472 | ) 473 | ); 474 | 475 | named!(union_type_spec<(Decl, Vec, Option)>, 476 | do_parse!(kw_union >> body:union_body >> (body))); 477 | 478 | named!(union_body<(Decl, Vec, Option)>, 479 | do_parse!( 480 | kw_switch >> lparen >> decl:declaration >> rparen >> 481 | lbrace >> 482 | ucss: many1!(union_case) >> 483 | dfl: opt!(union_default) >> 484 | rbrace >> 485 | (decl, ucss.into_iter().flat_map(|v| v).collect(), dfl) 486 | ) 487 | ); 488 | 489 | named!(union_case< Vec >, 490 | do_parse!( 491 | vs: many1!(do_parse!(kw_case >> v:value >> colon >> (v))) >> 492 | decl: declaration >> semi >> 493 | (vs.into_iter().map(|v| UnionCase(v, decl.clone())).collect()) 494 | ) 495 | ); 496 | 497 | named!(union_default, 498 | do_parse!( 499 | kw_default >> colon >> 500 | decl: declaration >> semi >> 501 | (decl) 502 | ) 503 | ); 504 | 505 | named!(declaration, 506 | alt!(kw_void => { |_| Decl::Void } | 507 | nonvoid_declaration)); 508 | 509 | named!(nonvoid_declaration, 510 | alt!( 511 | do_parse!(ty: array_type_spec >> id: ident >> lbrack >> sz:value >> rbrack >> 512 | (Decl::named(id, Type::array(ty, sz)))) 513 | | do_parse!(ty: array_type_spec >> id: ident >> lt >> sz:opt!(value) >> gt >> 514 | (Decl::named(id, Type::flex(ty, sz)))) 515 | | do_parse!(ty: type_spec >> star >> id: ident >> 516 | (Decl::named(id, Type::option(ty)))) 517 | | do_parse!(ty: type_spec >> id: ident >> 518 | (Decl::named(id, ty))) 519 | ) 520 | ); 521 | 522 | named!(array_type_spec, 523 | alt!(kw_opaque => { |_| Type::Opaque } | 524 | kw_string => { |_| Type::String } | 525 | type_spec 526 | ) 527 | ); 528 | 529 | #[test] 530 | fn test_decls() { 531 | assert_eq!(declaration(&b"void "[..]), Done(&b" "[..], Decl::Void)); 532 | 533 | assert_eq!(declaration(&b"int foo;"[..]), Done(&b";"[..], Decl::named("foo", Type::Int))); 534 | assert_eq!(declaration(&b"int foo[123] "[..]), 535 | Done(&b" "[..], Decl::named("foo", 536 | Type::Array(Box::new(Type::Int), Value::Const(123))))); 537 | 538 | assert_eq!(declaration(&b"int foo<123> "[..]), 539 | Done(&b" "[..], Decl::named("foo", 540 | Type::Flex(Box::new(Type::Int), Some(Value::Const(123)))))); 541 | assert_eq!(declaration(&b"int foo<> "[..]), 542 | Done(&b" "[..], Decl::named("foo", 543 | Type::Flex(Box::new(Type::Int), None)))); 544 | assert_eq!(declaration(&b"int *foo "[..]), 545 | Done(&b" "[..], Decl::named("foo", 546 | Type::Option(Box::new(Type::Int))))); 547 | 548 | assert_eq!(declaration(&b"opaque foo[123] "[..]), 549 | Done(&b" "[..], Decl::named("foo", 550 | Type::Array(Box::new(Type::Opaque), Value::Const(123))))); 551 | assert_eq!(declaration(&b"opaque foo<123> "[..]), 552 | Done(&b" "[..], Decl::named("foo", 553 | Type::Flex(Box::new(Type::Opaque), Some(Value::Const(123)))))); 554 | assert_eq!(declaration(&b"opaque foo<> "[..]), 555 | Done(&b" "[..], Decl::named("foo", 556 | Type::Flex(Box::new(Type::Opaque), None)))); 557 | 558 | assert_eq!(declaration(&b"string foo<123> "[..]), 559 | Done(&b" "[..], Decl::named("foo", 560 | Type::Flex(Box::new(Type::String), Some(Value::Const(123)))))); 561 | assert_eq!(declaration(&b"string foo<> "[..]), 562 | Done(&b" "[..], Decl::named("foo", 563 | Type::Flex(Box::new(Type::String), None)))); 564 | } 565 | 566 | named!(type_spec, 567 | preceded!(spaces, 568 | alt!( 569 | do_parse!(kw_unsigned >> kw_int >> (Type::UInt)) | 570 | do_parse!(kw_unsigned >> kw_long >> (Type::UInt)) | // backwards compat with rpcgen 571 | do_parse!(kw_unsigned >> kw_char >> // backwards compat with rpcgen 572 | (Type::ident_with_derives("u8", COPY | CLONE | EQ | PARTIALEQ | DEBUG))) | 573 | do_parse!(kw_unsigned >> kw_short >> (Type::UInt)) | // backwards compat with rpcgen 574 | do_parse!(kw_unsigned >> kw_hyper >> (Type::UHyper)) | 575 | kw_unsigned => { |_| Type::UInt } | // backwards compat with rpcgen 576 | kw_long => { |_| Type::Int } | // backwards compat with rpcgen 577 | kw_char => { // backwards compat with rpcgen 578 | |_| Type::ident_with_derives("i8", COPY | CLONE | EQ | PARTIALEQ | DEBUG) 579 | } | 580 | kw_short => { |_| Type::Int } | // backwards compat with rpcgen 581 | kw_int => { |_| Type::Int } | 582 | kw_hyper => { |_| Type::Hyper } | 583 | kw_float => { |_| Type::Float } | 584 | kw_double => { |_| Type::Double } | 585 | kw_quadruple => { |_| Type::Quadruple } | 586 | kw_bool => { |_| Type::Bool } | 587 | enum_type_spec => { |defns| Type::Enum(defns) } | 588 | struct_type_spec => { |defns| Type::Struct(defns) } | 589 | do_parse!(kw_struct >> id:ident >> (Type::ident(id))) | // backwards compat with rpcgen 590 | union_type_spec => { |u| Type::union(u) } | 591 | ident => { |id| Type::ident(id) } 592 | ) 593 | ) 594 | ); 595 | 596 | #[test] 597 | fn test_type() { 598 | assert_eq!(type_spec(&b"int "[..]), Done(&b" "[..], Type::Int)); 599 | assert_eq!(type_spec(&b"unsigned int "[..]), Done(&b" "[..], Type::UInt)); 600 | assert_eq!(type_spec(&b"unsigned\nint "[..]), Done(&b" "[..], Type::UInt)); 601 | assert_eq!(type_spec(&b"unsigned/* foo */int "[..]), Done(&b" "[..], Type::UInt)); 602 | assert_eq!(type_spec(&b"unsigned//\nint "[..]), Done(&b" "[..], Type::UInt)); 603 | 604 | assert_eq!(type_spec(&b"unsigned hyper "[..]), Done(&b" "[..], Type::UHyper)); 605 | 606 | assert_eq!(type_spec(&b"unsigned char "[..]), Done(&b" "[..], 607 | Type::Ident("u8".into(), Some(COPY | CLONE | EQ | PARTIALEQ | DEBUG)))); 608 | assert_eq!(type_spec(&b"unsigned short "[..]), Done(&b" "[..], Type::UInt)); 609 | 610 | assert_eq!(type_spec(&b" hyper "[..]), Done(&b" "[..], Type::Hyper)); 611 | assert_eq!(type_spec(&b" double "[..]), Done(&b" "[..], Type::Double)); 612 | assert_eq!(type_spec(&b"// thing\nquadruple "[..]), Done(&b" "[..], Type::Quadruple)); 613 | assert_eq!(type_spec(&b"// thing\n bool "[..]), Done(&b" "[..], Type::Bool)); 614 | 615 | assert_eq!(type_spec(&b"char "[..]), Done(&b" "[..], 616 | Type::Ident("i8".into(), Some(COPY | CLONE | EQ | PARTIALEQ | DEBUG)))); 617 | 618 | assert_eq!(type_spec(&b"short "[..]), Done(&b" "[..], Type::Int)); 619 | 620 | 621 | assert_eq!(type_spec(&b"struct { int a; int b; } "[..]), 622 | Done(&b" "[..], 623 | Type::Struct(vec!(Decl::named("a", Type::Int), 624 | Decl::named("b", Type::Int))))); 625 | 626 | assert_eq!(type_spec(&b"union switch (int a) { case 1: void; case 2: int a; default: void; } "[..]), 627 | Done(&b" "[..], 628 | Type::Union(Box::new(Decl::named("a", Type::Int)), 629 | vec!(UnionCase(Value::Const(1), Decl::Void), 630 | UnionCase(Value::Const(2), Decl::named("a", Type::Int))), 631 | Some(Box::new(Decl::Void))))); 632 | } 633 | 634 | #[test] 635 | fn test_enum() { 636 | assert_eq!(type_spec(&b"enum { a, b, c } "[..]), 637 | Done(&b" "[..], 638 | Type::Enum(vec!(EnumDefn::new("a", None), 639 | EnumDefn::new("b", None), 640 | EnumDefn::new("c", None))))); 641 | 642 | assert_eq!(type_spec(&b"enum { a = 1, b, c } "[..]), 643 | Done(&b" "[..], 644 | Type::Enum(vec!(EnumDefn::new("a", Some(Value::Const(1))), 645 | EnumDefn::new("b", None), 646 | EnumDefn::new("c", None))))); 647 | 648 | assert_eq!(type_spec(&b"enum { a = Bar, b, c } "[..]), 649 | Done(&b" "[..], 650 | Type::Enum(vec!(EnumDefn::new("a", Some(Value::ident("Bar"))), 651 | EnumDefn::new("b", None), 652 | EnumDefn::new("c", None))))); 653 | 654 | assert_eq!(type_spec(&b"enum { } "[..]), 655 | Error(Err::Position(ErrorKind::Alt, &b"enum { } "[..]))); 656 | } 657 | 658 | named!(const_def, 659 | do_parse!( 660 | kw_const >> id:ident >> eq >> v:number >> semi >> 661 | (Defn::constant(id, v))) 662 | ); 663 | 664 | #[test] 665 | fn test_const() { 666 | assert_eq!(const_def(&b"const foo = 123;"[..]), Done(&b""[..], Defn::constant("foo", 123))); 667 | } 668 | 669 | named!(type_def, 670 | alt!( 671 | do_parse!(kw_typedef >> decl: nonvoid_declaration >> semi >> 672 | ({ 673 | match decl.clone() { 674 | Decl::Named(name, ty) => { 675 | if ty.is_syn() { 676 | Defn::typesyn(name, ty) 677 | } else { 678 | Defn::typespec(name, ty) 679 | } 680 | }, 681 | Decl::Void => panic!("void non-void declaration?"), 682 | } 683 | }) 684 | ) 685 | | do_parse!(kw_enum >> id:ident >> e:enum_body >> semi >> (Defn::typespec(id, Type::Enum(e)))) 686 | | do_parse!(kw_struct >> id:ident >> s:struct_body >> semi >> (Defn::typespec(id, Type::Struct(s)))) 687 | | do_parse!(kw_union >> id:ident >> u:union_body >> semi >> (Defn::typespec(id, Type::union(u)))) 688 | ) 689 | ); 690 | 691 | #[test] 692 | fn test_typedef() { 693 | assert_eq!(type_def(&b"typedef int foo;"[..]), 694 | Done(&b""[..], Defn::typesyn("foo", Type::Int))); 695 | assert_eq!(type_def(&b"typedef unsigned int foo;"[..]), 696 | Done(&b""[..], Defn::typesyn("foo", Type::UInt))); 697 | assert_eq!(type_def(&b"typedef int foo<>;"[..]), 698 | Done(&b""[..], Defn::typespec("foo", Type::Flex(Box::new(Type::Int), None)))); 699 | 700 | assert_eq!(type_def(&b"enum foo { a };"[..]), 701 | Done(&b""[..], Defn::typespec("foo", Type::Enum(vec!(EnumDefn::new("a", None)))))); 702 | 703 | assert_eq!(type_def(&b"struct foo { int a; };"[..]), 704 | Done(&b""[..], Defn::typespec("foo", Type::Struct(vec!(Decl::named("a", Type::Int)))))); 705 | 706 | assert_eq!(type_def(&b"union foo switch(int a) { case 1: int a; };"[..]), 707 | Done(&b""[..], Defn::typespec("foo", 708 | Type::Union(Box::new(Decl::named("a", Type::Int)), 709 | vec!(UnionCase(Value::Const(1), Decl::named("a", Type::Int))), 710 | None)))); 711 | } 712 | -------------------------------------------------------------------------------- /xdrgen/src/xdrgen.rs: -------------------------------------------------------------------------------- 1 | #![crate_type = "bin"] 2 | 3 | extern crate xdrgen; 4 | extern crate env_logger; 5 | extern crate clap; 6 | 7 | use std::fs::File; 8 | use std::io::{BufReader, Write}; 9 | use std::io::{stderr, stdin, stdout}; 10 | 11 | use clap::App; 12 | 13 | use xdrgen::generate; 14 | 15 | fn main() { 16 | let _ = env_logger::init(); 17 | 18 | let matches = App::new("XDR code generator") 19 | .version(env!("CARGO_PKG_VERSION")) 20 | .arg_from_usage("[FILE] 'Set .x file'") 21 | .get_matches(); 22 | 23 | let output = stdout(); 24 | let mut err = stderr(); 25 | 26 | let res = if let Some(fname) = matches.value_of("FILE") { 27 | let f = match File::open(fname) { 28 | Ok(f) => f, 29 | Err(e) => { 30 | let _ = writeln!(&mut err, "Failed to open {}: {}", fname, e); 31 | std::process::exit(1); 32 | } 33 | }; 34 | generate(fname, BufReader::new(f), output) 35 | } else { 36 | generate("stdin", BufReader::new(stdin()), output) 37 | }; 38 | 39 | if let Err(e) = res { 40 | let _ = writeln!(&mut err, "Failed: {}", e); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /xdrgen/tests/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate xdrgen; 2 | extern crate xdr_codec; 3 | extern crate tempdir; 4 | #[macro_use] 5 | extern crate error_chain; 6 | 7 | use std::fs::{File, create_dir_all}; 8 | use std::io::{Cursor, Write}; 9 | use std::process::Command; 10 | 11 | use xdrgen::generate; 12 | use xdr_codec::Result; 13 | 14 | fn build_test(name: &str, xdr_spec: &str) -> Result<()> { 15 | let tempdir = tempdir::TempDir::new("build").expect("Failed to make tempdir"); 16 | let dir = tempdir.path(); 17 | 18 | println!("tempdir {:?}", dir); 19 | let _ = create_dir_all(&dir); 20 | 21 | let mainfile = dir.join(format!("{}.rs", name)); 22 | let testfile = dir.join(format!("{}_xdr.rs", name)); 23 | let cargohome = dir.join(".cargo"); 24 | let cargotoml = dir.join("Cargo.toml"); 25 | 26 | let toml = format!( 27 | r#" 28 | [package] 29 | name = "test" 30 | version = "0.0.0" 31 | publish = false 32 | 33 | [lib] 34 | name = "test" 35 | path = "{}" 36 | 37 | [dependencies] 38 | xdr-codec = {{ path = "{}" }} 39 | "#, 40 | mainfile.as_os_str().to_string_lossy(), 41 | std::env::current_dir()? 42 | .join("../xdr-codec") 43 | .as_os_str() 44 | .to_string_lossy() 45 | ); 46 | 47 | let template = format!( 48 | r#" 49 | #![allow(dead_code, non_camel_case_types, unused_assignments, unused_imports)] 50 | extern crate xdr_codec; 51 | 52 | mod test {{ 53 | use xdr_codec; 54 | include!("{}"); 55 | }} 56 | 57 | fn main() {{}} 58 | "#, 59 | testfile.as_os_str().to_string_lossy() 60 | ); 61 | 62 | { 63 | let mut main = File::create(&mainfile)?; 64 | main.write_all(template.as_bytes())?; 65 | } 66 | 67 | { 68 | let mut cargo = File::create(&cargotoml)?; 69 | cargo.write_all(toml.as_bytes())?; 70 | } 71 | 72 | let _ = create_dir_all(&cargohome); 73 | 74 | { 75 | let test = File::create(&testfile)?; 76 | generate(name, Cursor::new(xdr_spec.as_bytes()), test)?; 77 | } 78 | 79 | let compile = { 80 | let mut cmd = Command::new("cargo"); 81 | let cmd = cmd 82 | .current_dir(std::env::current_dir()?) 83 | //.env("CARGO_HOME", cargohome) 84 | .arg("test") 85 | .arg("--manifest-path").arg(cargotoml); 86 | println!("CWD: {:?} Command: {:?}", std::env::current_dir(), cmd); 87 | cmd.output()? 88 | }; 89 | 90 | println!( 91 | "stdout: {}\n, stderr: {}", 92 | String::from_utf8_lossy(&compile.stdout), 93 | String::from_utf8_lossy(&compile.stderr) 94 | ); 95 | 96 | if compile.status.success() { 97 | Ok(()) 98 | } else { 99 | bail!("couldn't compile") 100 | } 101 | } 102 | 103 | #[test] 104 | fn typedef_arrays() { 105 | let name = "typedef_arrays"; 106 | let spec = r#" 107 | typedef opaque buf1<20>; 108 | typedef opaque buf2[10]; 109 | typedef opaque buf3<>; 110 | "#; 111 | 112 | if let Err(e) = build_test(name, spec) { 113 | panic!("test {} failed: {}", name, e); 114 | } 115 | } 116 | 117 | #[test] 118 | fn recursive_type() { 119 | let name = "recursive_type"; 120 | let spec = r#" 121 | struct list { list *next; }; 122 | "#; 123 | if let Err(e) = build_test(name, spec) { 124 | panic!("test {} failed: {}", name, e); 125 | } 126 | } 127 | 128 | #[test] 129 | fn union_with_default() { 130 | let name = "union_with_default"; 131 | let spec = r#" 132 | union foo switch (int bar) { 133 | case 1: 134 | int val; 135 | default: 136 | void; 137 | }; 138 | "#; 139 | 140 | if let Err(e) = build_test(name, spec) { 141 | panic!("test {} failed: {}", name, e); 142 | } 143 | } 144 | 145 | #[test] 146 | fn union_default_nonempty() { 147 | let name = "union_default_nonempty"; 148 | let spec = r#" 149 | union foo switch (int bar) { 150 | case 1: 151 | int val; 152 | default: 153 | opaque buf<>; 154 | }; 155 | "#; 156 | 157 | if let Err(e) = build_test(name, spec) { 158 | panic!("test {} failed: {}", name, e); 159 | } 160 | } 161 | 162 | #[test] 163 | fn simple() { 164 | let name = "simple"; 165 | let specs = vec![ 166 | "struct foo { int bar; unsigned int blat; hyper foo; unsigned hyper hyperfoo; };", 167 | "const blop = 123;", 168 | "typedef opaque Ioaddr<>;", 169 | ]; 170 | 171 | for (i, spec) in specs.into_iter().enumerate() { 172 | let name = format!("{}_{}", name, i); 173 | 174 | if let Err(e) = build_test(&name, spec) { 175 | panic!("test {} failed: {}", name, e); 176 | } 177 | } 178 | } 179 | 180 | #[test] 181 | fn rfc4506() { 182 | let name = "rfc4506"; 183 | let spec = r#" 184 | 185 | const MAXUSERNAME = 32; /* max length of a user name */ 186 | const MAXFILELEN = 65535; /* max length of a file */ 187 | const MAXNAMELEN = 255; /* max length of a file name */ 188 | 189 | /* 190 | * Types of files: 191 | */ 192 | enum filekind { 193 | TEXT = 0, /* ascii data */ 194 | DATA = 1, /* raw data */ 195 | EXEC = 2 /* executable */ 196 | }; 197 | 198 | /* 199 | * File information, per kind of file: 200 | */ 201 | union filetype switch (filekind kind) { 202 | case TEXT: 203 | void; /* no extra information */ 204 | case DATA: 205 | string creator; /* data creator */ 206 | case EXEC: 207 | string interpretor; /* program interpretor */ 208 | }; 209 | 210 | /* 211 | * A complete file: 212 | */ 213 | struct file { 214 | string filename; /* name of file */ 215 | filetype type; /* info about file */ 216 | string owner; /* owner of file */ 217 | opaque data; /* file data */ 218 | }; 219 | "#; 220 | 221 | if let Err(e) = build_test(name, spec) { 222 | panic!("test {} failed: {}", name, e); 223 | } 224 | } 225 | 226 | #[test] 227 | fn enums() { 228 | let name = "enums"; 229 | let spec = r#" 230 | enum Foo { 231 | A = 0, 232 | B = -1 233 | }; 234 | struct Bar { Foo x; }; 235 | "#; 236 | 237 | if let Err(e) = build_test(name, spec) { 238 | panic!("test {} failed: {}", name, e); 239 | } 240 | } 241 | 242 | #[test] 243 | fn unions() { 244 | let name = "unions"; 245 | let spec = r#" 246 | enum Foo { 247 | A = 0, 248 | B = -1 249 | }; 250 | union foo switch (Foo bar) { 251 | case A: int val; 252 | case B: void; 253 | default: int other; 254 | }; 255 | union foo2 switch (Foo bar) { 256 | case A: void; 257 | case B: int a; 258 | default: int other; 259 | }; 260 | "#; 261 | 262 | if let Err(e) = build_test(name, spec) { 263 | panic!("test {} failed: {}", name, e); 264 | } 265 | } 266 | 267 | #[test] 268 | fn consts() { 269 | let name = "consts"; 270 | let spec = r#" 271 | const FOO = 1; 272 | const BAR = -1; 273 | "#; 274 | 275 | if let Err(e) = build_test(name, spec) { 276 | panic!("test {} failed: {}", name, e); 277 | } 278 | } 279 | 280 | #[test] 281 | fn arrays() { 282 | let name = "arrays"; 283 | let spec = r#" 284 | struct a { opaque data[15]; }; 285 | struct b { int things[10]; }; 286 | struct c { string decitweet[14]; }; 287 | struct d { c tweetses[10]; }; 288 | struct big { c tweetses[100]; }; 289 | "#; 290 | 291 | if let Err(e) = build_test(name, spec) { 292 | panic!("test {} failed: {}", name, e); 293 | } 294 | } 295 | 296 | #[test] 297 | fn flex() { 298 | let name = "flex"; 299 | let spec = r#" 300 | struct a { opaque data<>; opaque limdata<15>; }; 301 | struct b { string s<>; string limstr<32>; }; 302 | struct c { a athing<>; a alim<10>; }; 303 | "#; 304 | 305 | if let Err(e) = build_test(name, spec) { 306 | panic!("test {} failed: {}", name, e); 307 | } 308 | } 309 | 310 | #[test] 311 | fn derive_float() { 312 | let name = "derive_float"; 313 | let spec = r#" 314 | struct a { float a; double b; }; 315 | "#; 316 | 317 | if let Err(e) = build_test(name, spec) { 318 | panic!("test {} failed: {}", name, e); 319 | } 320 | } 321 | --------------------------------------------------------------------------------