diff --git a/.travis.yml b/.travis.yml index 3bf43c3..f9ba6a2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,3 +37,6 @@ matrix: - env: SUITE="rust" language: rust rust: nightly + - env: SUITE="rust" RUST_FEATURES="--no-default-features" + language: rust + rust: nightly diff --git a/README.md b/README.md index 785394f..2b4a8e9 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,11 @@ # ![zser][zser-logo-image] [![Build Status][build-image]][build-link] -[![Code Climate][codeclimate-image]][codeclimate-link] [![MIT licensed][license-image]][license-link] [zser-logo-image]: https://raw.githubusercontent.com/zcred/logos/master/zser-logo-md.png [build-image]: https://secure.travis-ci.org/zcred/zser.svg?branch=master [build-link]: http://travis-ci.org/zcred/zser -[codeclimate-image]: https://codeclimate.com/github/zcred/zser.svg?branch=master -[codeclimate-link]: https://codeclimate.com/github/zcred/zser [license-image]: https://img.shields.io/badge/license-MIT-blue.svg [license-link]: https://github.com/zcred/zser/blob/master/LICENSE.txt diff --git a/ci.sh b/ci.sh index 089c406..761af16 100755 --- a/ci.sh +++ b/ci.sh @@ -29,6 +29,7 @@ ruby) rust) cd rust cargo test + cargo build $RUST_FEATURES ;; *) echo "*** ERROR: Unknown test suite: '$SUITE'" diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 7e8ffb5..34f2b32 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -4,110 +4,40 @@ version = "0.0.1" dependencies = [ "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "data-encoding 2.0.0-rc.1 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "leb128 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "backtrace" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace-sys 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "backtrace-sys" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "gcc 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "byteorder" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "cfg-if" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "data-encoding" version = "2.0.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "dbghelp-sys" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "dtoa" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "error-chain" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "gcc" -version = "0.3.46" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "itoa" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "leb128" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "libc" -version = "0.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "num-traits" version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "rustc-demangle" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "serde" version = "1.0.8" @@ -124,33 +54,12 @@ dependencies = [ "serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [metadata] -"checksum backtrace 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "72f9b4182546f4b04ebc4ab7f84948953a118bd6021a1b6a6c909e3e94f6be76" -"checksum backtrace-sys 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3a0d842ea781ce92be2bf78a9b38883948542749640b8378b3b2f03d1fd9f1ff" "checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" -"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c" "checksum data-encoding 2.0.0-rc.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c48768b270ab6458e2fba295bd3593f76d22058945afb5bdc6a1b1436511402b" -"checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850" "checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90" -"checksum error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9435d864e017c3c6afeac1654189b06cdb491cf2ff73dbf0d73b0f292f42ff8" -"checksum gcc 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)" = "181e3cebba1d663bd92eb90e2da787e10597e027eb00de8d742b260a7850948f" "checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c" -"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum leb128 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "792224a73787e4b0f59e387e4f53d23dfc1d72e3c82fcbf6d3e14de6aabad212" -"checksum libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)" = "e7eb6b826bfc1fdea7935d46556250d1799b7fe2d9f7951071f4291710665e3e" "checksum num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "e1cbfa3781f3fe73dc05321bed52a06d2d491eaa764c52335cf4399f046ece99" -"checksum rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3058a43ada2c2d0b92b3ae38007a2d0fa5e9db971be260e0171408a4ff471c95" "checksum serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c2f530d36fb84ec48fb7146936881f026cdbf4892028835fd9398475f82c1bb4" "checksum serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "48b04779552e92037212c3615370f6bd57a40ebba7f20e554ff9f55e41a69a7b" -"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index addccf9..0ab1d41 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -10,9 +10,6 @@ readme = "README.md" categories = ["authentication", "cryptography", "encoding"] keywords = ["authentication", "cryptography", "security", "serialization", "merkle"] -[dependencies] -error-chain = "0.10" - [dependencies.byteorder] version = "0.5" default-features = false diff --git a/rust/src/decoder.rs b/rust/src/decoder.rs index 70dc425..9eda637 100644 --- a/rust/src/decoder.rs +++ b/rust/src/decoder.rs @@ -1,5 +1,8 @@ //! Decodes a zser message into `Zser::Value` +#[cfg(not(feature = "std"))] +use collections::vec::Vec; + use parser::Handler; use value::{Map, Value}; diff --git a/rust/src/errors.rs b/rust/src/errors.rs index 66cec0e..23cc30e 100644 --- a/rust/src/errors.rs +++ b/rust/src/errors.rs @@ -1,36 +1,110 @@ -//! errors.rs: error types based on error-chain +//! errors.rs: error types used by this crate -#![allow(missing_docs)] +use core::fmt::{self, Debug, Display}; +use core::result; -error_chain! { - types { - Error, ErrorKind, ResultExt, Result; - } +#[cfg(feature = "std")] +use std::error; - errors { - OversizedMessage(len: usize, limit: usize) { - description("message too long") - display("{}-byte message exceeds limit of {} bytes", len, limit) - } +#[cfg(not(feature = "std"))] +use collections::boxed::Box; + +#[cfg(not(feature = "std"))] +use collections::string::String; + +/// Errors which can occur when serializing or deserializing zser messages +pub struct Error { + err: Box, +} + +/// `Result` alias used by this crate +pub type Result = result::Result; + +#[derive(Debug)] +struct ErrorImpl { + kind: ErrorKind, + byte_offset: Option, +} + +/// Error variants encountered in this crate +#[derive(Debug)] +pub enum ErrorKind { + /// Message exceeds configured size limit + OversizedMessage(usize), + + /// Message exceeds configured maximum for nested messages + MaxDepthExceeded(usize), + + /// Expected more data in message + TruncatedMessage(String), + + /// Message encoded with an unknown wiretype + UnknownWiretype(u64), + + /// Unconsumed messages remaining in buffer + UnconsumedMessages(usize), +} - MaxDepthExceeded(max: usize) { - description("max nested message depth exceeded") - display("max nested message depth of {} bytes exceeded", max) +impl Display for ErrorKind { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ErrorKind::OversizedMessage(limit) => { + write!(f, "message exceeds limit of {} bytes", limit) + } + ErrorKind::MaxDepthExceeded(max) => { + write!(f, "max nested message depth of {} exceeded", max) + } + ErrorKind::TruncatedMessage(ref msg) => write!(f, "message truncated: {}", msg), + ErrorKind::UnknownWiretype(wiretype) => write!(f, "unknown wiretype: {}", wiretype), + ErrorKind::UnconsumedMessages(count) => { + write!(f, "unconsumed messages in buffer: {} messages", count) + } } + } +} - TruncatedMessage(msg: String) { - description("message truncated") - display("message truncated: {}", msg) +#[cfg(feature = "std")] +impl error::Error for Error { + fn description(&self) -> &str { + match self.err.kind { + ErrorKind::OversizedMessage(_) => "message too long", + ErrorKind::MaxDepthExceeded(_) => "maximum number of nested messages exceeded", + ErrorKind::TruncatedMessage(_) => "message truncated", + ErrorKind::UnknownWiretype(_) => "unknown wiretype", + ErrorKind::UnconsumedMessages(_) => "unconsumed messages in buffer", } + } +} - UnknownWiretype(wiretype: u64) { - description("unknown wiretype") - display("unknown wiretype: {}", wiretype) +impl From for Error { + fn from(ek: ErrorKind) -> Self { + Error { + err: Box::new(ErrorImpl { + kind: ek, + byte_offset: None, + }), } + } +} - UnconsumedMessages(count: usize) { - description("unconsumed messages remaining in buffer") - display("unconsumed messages in buffer: {} messages", count) +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&*self.err, f) + } +} + +impl Display for ErrorImpl { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.byte_offset { + Some(offset) => write!(f, "{} at byte {}", self.kind, offset), + None => Display::fmt(&self.kind, f), } + + } +} + +impl Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(&*self.err, f) } } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index be6097d..44165c0 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -13,11 +13,12 @@ #![cfg_attr(feature = "bench", feature(test))] extern crate byteorder; -#[macro_use] -extern crate error_chain; #[cfg(not(feature = "std"))] -extern crate collections; +#[macro_use] extern crate collections; + +#[cfg(feature = "std")] +extern crate core; #[cfg(all(feature = "bench", test))] extern crate test; diff --git a/rust/src/parser.rs b/rust/src/parser.rs index f08c9d9..19d2d57 100644 --- a/rust/src/parser.rs +++ b/rust/src/parser.rs @@ -41,7 +41,7 @@ impl<'m, H: Handler> Parser<'m, H> { /// Parse the given zser message, invoking callbacks as necessary pub fn parse(&mut self, message: &'m [u8]) -> Result<()> { if message.len() > self.max_length { - return Err(ErrorKind::OversizedMessage(message.len(), self.max_length).into()); + return Err(ErrorKind::OversizedMessage(self.max_length).into()); } if self.remaining.len() >= self.max_depth { diff --git a/rust/src/value.rs b/rust/src/value.rs index d244afc..4a9f3b2 100644 --- a/rust/src/value.rs +++ b/rust/src/value.rs @@ -1,14 +1,23 @@ //! The Value enum: a loosely typed way of representing zser messages. -#[cfg(not(feature = "std"))] -pub use collections::HashMap; - #[cfg(feature = "std")] pub use std::collections::HashMap; -/// Integer ID -> Value mapping used to represent messages internally +#[cfg(not(feature = "std"))] +pub use collections::btree_map::BTreeMap; + +#[cfg(not(feature = "std"))] +use collections::vec::Vec; + +/// Integer ID -> Value mapping with `hashDoS`-resistant `HashMap` on std +#[cfg(feature = "std")] pub type Map = HashMap; +/// Integer ID -> Value mapping, using BTreeMap without std since we don't have HashMap +// TODO: hashDoS resistance in no_std environments +#[cfg(not(feature = "std"))] +pub type Map = BTreeMap; + /// Represents any value that can occur in a zser message #[derive(Debug, PartialEq)] pub enum Value { diff --git a/rust/src/varint.rs b/rust/src/varint.rs index a30739a..26442f5 100644 --- a/rust/src/varint.rs +++ b/rust/src/varint.rs @@ -3,6 +3,9 @@ use byteorder::{ByteOrder, LittleEndian}; use errors::*; +#[cfg(not(feature = "std"))] +use collections::string::ToString; + /// Encode a 64-bit unsigned integer in zsuint64 form pub fn encode(value: u64, out: &mut [u8]) -> usize { let mut length = 1; @@ -33,7 +36,7 @@ pub fn decode(input: &mut &[u8]) -> Result { let prefix = *bytes .first() - .ok_or_else(|| ErrorKind::TruncatedMessage("missing varint prefix".to_owned()))?; + .ok_or_else(|| ErrorKind::TruncatedMessage("missing varint prefix".to_string()))?; if prefix == 0 { if bytes.len() >= 9 { @@ -41,14 +44,14 @@ pub fn decode(input: &mut &[u8]) -> Result { *input = &bytes[9..]; return Ok(result); } else { - return Err(ErrorKind::TruncatedMessage("truncated varint".to_owned()).into()); + return Err(ErrorKind::TruncatedMessage("truncated varint".to_string()).into()); } } let count = prefix.trailing_zeros() as usize + 1; if bytes.len() < count { - return Err(ErrorKind::TruncatedMessage("truncated varint".to_owned()).into()); + return Err(ErrorKind::TruncatedMessage("truncated varint".to_string()).into()); } let result = LittleEndian::read_uint(bytes, count) >> count;