diff options
author | Jeff Vander Stoep <jeffv@google.com> | 2020-12-07 17:22:08 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2020-12-07 17:22:08 +0000 |
commit | 64b7afd4a649f7fcfe117c341f44805c77daa9ab (patch) | |
tree | 30b3efa938d801f0479e5ea2c6c647c74e60afa1 | |
parent | 8ecf69645e5dc06b2d864b8228d1b5b19cefaaf6 (diff) | |
parent | 10c89c58562f8e90c25e7573096e71dc83dc4786 (diff) | |
download | untrusted-64b7afd4a649f7fcfe117c341f44805c77daa9ab.tar.gz |
untrusted crate v0.7.1 am: 10c89c5856
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/untrusted/+/1517419
Change-Id: I4bca812c0af519c580fb83c0441d221bb95d1ad9
-rw-r--r-- | .cargo_vcs_info.json | 5 | ||||
-rw-r--r-- | .gitattributes | 8 | ||||
-rw-r--r-- | .gitignore | 37 | ||||
-rw-r--r-- | .travis.yml | 15 | ||||
-rw-r--r-- | Cargo.toml | 29 | ||||
l--------- | LICENSE | 1 | ||||
-rw-r--r-- | LICENSE.txt | 13 | ||||
-rw-r--r-- | METADATA | 19 | ||||
-rw-r--r-- | MODULE_LICENSE_ISC | 0 | ||||
-rw-r--r-- | OWNERS | 1 | ||||
-rw-r--r-- | README.md | 109 | ||||
-rw-r--r-- | rustfmt.toml | 13 | ||||
-rw-r--r-- | src/untrusted.rs | 374 | ||||
-rw-r--r-- | tests/tests.rs | 98 |
14 files changed, 722 insertions, 0 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json new file mode 100644 index 0000000..05e75c4 --- /dev/null +++ b/.cargo_vcs_info.json @@ -0,0 +1,5 @@ +{ + "git": { + "sha1": "c3a0e38470aa35565764a68c65b75b9e2ffd732d" + } +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..35a6201 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,8 @@ +* text=auto !eol +*.sln eol=crlf +*.vcxproj eol=crlf +*.vcxproj.filters eol=crlf +*.props eol=crlf +*.bat eol=crlf +*.rc eol=crlf +*.pl linguist-language=Assembly diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..71aa63b --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +build/ +ssl/test/runner/runner +*.bk +*.orig +*.swp +*.swo +doc/*.html +doc/doc.css +*~ + +# Visual Studio Junk +.vs/ +*.opensdf +*.psess +*.sdf +*.sln.docstates +*.suo +*.user +*.userosscache +*.VC.db +*.VC.opendb +*.vsp +*.vspx +*.rsproj +*.sln +*.vcxproj +*.filters + + +# Cargo Junk +Cargo.lock +target/ + +# JetBrains Junk +.idea +*.iml +CMakeLists.txt diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..9579d74 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +language: rust +sudo: false +matrix: + allow_failures: + - rust: nightly + include: + - rust: stable + + - rust: beta + + - rust: nightly + +script: + - cargo test -vv --release + - cargo test -vv diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9da787a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,29 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +edition = "2018" +name = "untrusted" +version = "0.7.1" +authors = ["Brian Smith <brian@briansmith.org>"] +description = "Safe, fast, zero-panic, zero-crashing, zero-allocation parsing of untrusted inputs in Rust." +documentation = "https://briansmith.org/rustdoc/untrusted/" +readme = "README.md" +license = "ISC" +repository = "https://github.com/briansmith/untrusted" +[profile.bench] +opt-level = 2 +lto = true + +[lib] +name = "untrusted" +path = "src/untrusted.rs" @@ -0,0 +1 @@ +LICENSE.txt
\ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..7151db6 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,13 @@ +// Copyright 2015-2016 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/METADATA b/METADATA new file mode 100644 index 0000000..60df743 --- /dev/null +++ b/METADATA @@ -0,0 +1,19 @@ +name: "untrusted" +description: "Safe, fast, zero-panic, zero-crashing, zero-allocation parsing of untrusted inputs in Rust." +third_party { + url { + type: HOMEPAGE + value: "https://crates.io/crates/untrusted" + } + url { + type: ARCHIVE + value: "https://static.crates.io/crates/untrusted/untrusted-0.7.1.crate" + } + version: "0.7.1" + license_type: NOTICE + last_upgrade_date { + year: 2020 + month: 11 + day: 26 + } +} diff --git a/MODULE_LICENSE_ISC b/MODULE_LICENSE_ISC new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/MODULE_LICENSE_ISC @@ -0,0 +1 @@ +include platform/prebuilts/rust:/OWNERS diff --git a/README.md b/README.md new file mode 100644 index 0000000..9cbf9df --- /dev/null +++ b/README.md @@ -0,0 +1,109 @@ +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + + + +untrusted.rs +============ + +Safe, fast, zero-panic, zero-crashing, zero-allocation parsing of untrusted +inputs in Rust. + +untrusted.rs is 100% Rust with no use of `unsafe`. It never uses the heap. +No part of untrusted.rs's API will ever panic or cause a crash. It is +`#![no_std]` and so it works perfectly with both libcore- and libstd- based +projects. It does not depend on any crates other than libcore. + +untrusted.rs is intended to be used with the latest version of Rust Stable. +It should usually work with the latest Rust Beta and Rust Nightly versions +too. Using a version of untrusted.rs other than the latest release available +on crates.io is not recommended. + + + +Documentation +------------- + +See the documentation at +https://briansmith.org/rustdoc/untrusted/. + +To use untrusted.rs in your project, add a dependency to your +Cargo.toml like this: + +``` +[dependencies] +untrusted = "0.2" +``` + + + +Examples +-------- + +[*ring*](https://github.com/briansmith/ring)'s parser for the subset of ASN.1 +DER it needs to understand, +[`ring::der`](https://github.com/briansmith/ring/blob/master/src/der.rs), is +built on top of untrusted.rs. *ring* also uses untrusted.rs to parse ECC public +keys, RSA PKCS#1 1.5 padding, and everything else. + +All of [webpki](https://github.com/briansmith/webpki)'s parsing of X.509 +certificates (also ASN.1 DER) is done using untrusted.rs. + + + +Contributing +------------ + +Patches welcome! + +When contributing changes, state that you agree to license your contribution +under the same terms as the existing code by putting this at the bottom of your +commit message: + +``` + +I agree to license my contributions to each file under the terms given +at the top of each file I changed. +``` + +Currently, the biggest needs for this library are: + +* Unit tests. +* Documentation. +* More examples. +* Static analysis and fuzzing. + + + +Online Automated Testing +------------------------ + +Travis CI is used for Android, Linux, and Mac OS X. The tests are run for the +current release of each Rust channel (Stable, Beta, Nightly). Since +untrusted.rs only depends on libcore and it only uses 100% cross-platform code +without using `unsafe`, it should work anywhere as long as these platforms are +passing. + +<a title="Build Status" href=https://travis-ci.org/briansmith/untrusted><img src=https://travis-ci.org/briansmith/untrusted.svg?branch=master></a> + + + +Bug Reporting +------------- + +Please report bugs either as pull requests or as issues in [the issue +tracker](https://github.com/briansmith/untrusted/issues). untrusted.rs has a +**full disclosure** vulnerability policy. **Please do NOT attempt to report +any security vulnerability in this code privately to anybody.** + + + +License +------- + +See [LICENSE.txt](LICENSE.txt), an ISC-style (simplified MIT) license. diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..a9ae3c4 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,13 @@ +edition = "2018" +fn_args_density = "Compressed" +fn_single_line = true +match_arm_blocks = false +match_block_trailing_comma = true +max_width = 100 +merge_imports = true +newline_style = "Unix" +reorder_imports = true +trailing_comma = "Vertical" +use_field_init_shorthand = true +use_try_shorthand = true +wrap_comments = true diff --git a/src/untrusted.rs b/src/untrusted.rs new file mode 100644 index 0000000..2f88bb4 --- /dev/null +++ b/src/untrusted.rs @@ -0,0 +1,374 @@ +// Copyright 2015-2016 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +//! untrusted.rs: Safe, fast, zero-panic, zero-crashing, zero-allocation +//! parsing of untrusted inputs in Rust. +//! +//! <code>git clone https://github.com/briansmith/untrusted</code> +//! +//! untrusted.rs goes beyond Rust's normal safety guarantees by also +//! guaranteeing that parsing will be panic-free, as long as +//! `untrusted::Input::as_slice_less_safe()` is not used. It avoids copying +//! data and heap allocation and strives to prevent common pitfalls such as +//! accidentally parsing input bytes multiple times. In order to meet these +//! goals, untrusted.rs is limited in functionality such that it works best for +//! input languages with a small fixed amount of lookahead such as ASN.1, TLS, +//! TCP/IP, and many other networking, IPC, and related protocols. Languages +//! that require more lookahead and/or backtracking require some significant +//! contortions to parse using this framework. It would not be realistic to use +//! it for parsing programming language code, for example. +//! +//! The overall pattern for using untrusted.rs is: +//! +//! 1. Write a recursive-descent-style parser for the input language, where the +//! input data is given as a `&mut untrusted::Reader` parameter to each +//! function. Each function should have a return type of `Result<V, E>` for +//! some value type `V` and some error type `E`, either or both of which may +//! be `()`. Functions for parsing the lowest-level language constructs +//! should be defined. Those lowest-level functions will parse their inputs +//! using `::read_byte()`, `Reader::peek()`, and similar functions. +//! Higher-level language constructs are then parsed by calling the +//! lower-level functions in sequence. +//! +//! 2. Wrap the top-most functions of your recursive-descent parser in +//! functions that take their input data as an `untrusted::Input`. The +//! wrapper functions should call the `Input`'s `read_all` (or a variant +//! thereof) method. The wrapper functions are the only ones that should be +//! exposed outside the parser's module. +//! +//! 3. After receiving the input data to parse, wrap it in an `untrusted::Input` +//! using `untrusted::Input::from()` as early as possible. Pass the +//! `untrusted::Input` to the wrapper functions when they need to be parsed. +//! +//! In general parsers built using `untrusted::Reader` do not need to explicitly +//! check for end-of-input unless they are parsing optional constructs, because +//! `Reader::read_byte()` will return `Err(EndOfInput)` on end-of-input. +//! Similarly, parsers using `untrusted::Reader` generally don't need to check +//! for extra junk at the end of the input as long as the parser's API uses the +//! pattern described above, as `read_all` and its variants automatically check +//! for trailing junk. `Reader::skip_to_end()` must be used when any remaining +//! unread input should be ignored without triggering an error. +//! +//! untrusted.rs works best when all processing of the input data is done +//! through the `untrusted::Input` and `untrusted::Reader` types. In +//! particular, avoid trying to parse input data using functions that take +//! byte slices. However, when you need to access a part of the input data as +//! a slice to use a function that isn't written using untrusted.rs, +//! `Input::as_slice_less_safe()` can be used. +//! +//! It is recommend to use `use untrusted;` and then `untrusted::Input`, +//! `untrusted::Reader`, etc., instead of using `use untrusted::*`. Qualifying +//! the names with `untrusted` helps remind the reader of the code that it is +//! dealing with *untrusted* input. +//! +//! # Examples +//! +//! [*ring*](https://github.com/briansmith/ring)'s parser for the subset of +//! ASN.1 DER it needs to understand, +//! [`ring::der`](https://github.com/briansmith/ring/blob/master/src/der.rs), +//! is built on top of untrusted.rs. *ring* also uses untrusted.rs to parse ECC +//! public keys, RSA PKCS#1 1.5 padding, and for all other parsing it does. +//! +//! All of [webpki](https://github.com/briansmith/webpki)'s parsing of X.509 +//! certificates (also ASN.1 DER) is done using untrusted.rs. + +#![doc(html_root_url = "https://briansmith.org/rustdoc/")] +// `#[derive(...)]` uses `#[allow(unused_qualifications)]` internally. +#![deny(unused_qualifications)] +#![forbid( + anonymous_parameters, + box_pointers, + missing_docs, + trivial_casts, + trivial_numeric_casts, + unsafe_code, + unstable_features, + unused_extern_crates, + unused_import_braces, + unused_results, + variant_size_differences, + warnings +)] +#![no_std] + +/// A wrapper around `&'a [u8]` that helps in writing panic-free code. +/// +/// No methods of `Input` will ever panic. +#[derive(Clone, Copy, Debug, Eq)] +pub struct Input<'a> { + value: no_panic::Slice<'a>, +} + +impl<'a> Input<'a> { + /// Construct a new `Input` for the given input `bytes`. + pub const fn from(bytes: &'a [u8]) -> Self { + // This limit is important for avoiding integer overflow. In particular, + // `Reader` assumes that an `i + 1 > i` if `input.value.get(i)` does + // not return `None`. According to the Rust language reference, the + // maximum object size is `core::isize::MAX`, and in practice it is + // impossible to create an object of size `core::usize::MAX` or larger. + Self { + value: no_panic::Slice::new(bytes), + } + } + + /// Returns `true` if the input is empty and false otherwise. + #[inline] + pub fn is_empty(&self) -> bool { self.value.is_empty() } + + /// Returns the length of the `Input`. + #[inline] + pub fn len(&self) -> usize { self.value.len() } + + /// Calls `read` with the given input as a `Reader`, ensuring that `read` + /// consumed the entire input. If `read` does not consume the entire input, + /// `incomplete_read` is returned. + pub fn read_all<F, R, E>(&self, incomplete_read: E, read: F) -> Result<R, E> + where + F: FnOnce(&mut Reader<'a>) -> Result<R, E>, + { + let mut input = Reader::new(*self); + let result = read(&mut input)?; + if input.at_end() { + Ok(result) + } else { + Err(incomplete_read) + } + } + + /// Access the input as a slice so it can be processed by functions that + /// are not written using the Input/Reader framework. + #[inline] + pub fn as_slice_less_safe(&self) -> &'a [u8] { self.value.as_slice_less_safe() } +} + +impl<'a> From<&'a [u8]> for Input<'a> { + #[inline] + fn from(value: &'a [u8]) -> Self { Self { value: no_panic::Slice::new(value)} } +} + +// #[derive(PartialEq)] would result in lifetime bounds that are +// unnecessarily restrictive; see +// https://github.com/rust-lang/rust/issues/26925. +impl PartialEq<Input<'_>> for Input<'_> { + #[inline] + fn eq(&self, other: &Input) -> bool { + self.as_slice_less_safe() == other.as_slice_less_safe() + } +} + +impl PartialEq<[u8]> for Input<'_> { + #[inline] + fn eq(&self, other: &[u8]) -> bool { self.as_slice_less_safe() == other } +} + +impl PartialEq<Input<'_>> for [u8] { + #[inline] + fn eq(&self, other: &Input) -> bool { other.as_slice_less_safe() == self } +} + +/// Calls `read` with the given input as a `Reader`, ensuring that `read` +/// consumed the entire input. When `input` is `None`, `read` will be +/// called with `None`. +pub fn read_all_optional<'a, F, R, E>( + input: Option<Input<'a>>, incomplete_read: E, read: F, +) -> Result<R, E> +where + F: FnOnce(Option<&mut Reader<'a>>) -> Result<R, E>, +{ + match input { + Some(input) => { + let mut input = Reader::new(input); + let result = read(Some(&mut input))?; + if input.at_end() { + Ok(result) + } else { + Err(incomplete_read) + } + }, + None => read(None), + } +} + +/// A read-only, forward-only* cursor into the data in an `Input`. +/// +/// Using `Reader` to parse input helps to ensure that no byte of the input +/// will be accidentally processed more than once. Using `Reader` in +/// conjunction with `read_all` and `read_all_optional` helps ensure that no +/// byte of the input is accidentally left unprocessed. The methods of `Reader` +/// never panic, so `Reader` also assists the writing of panic-free code. +/// +/// \* `Reader` is not strictly forward-only because of the method +/// `get_input_between_marks`, which is provided mainly to support calculating +/// digests over parsed data. +#[derive(Debug)] +pub struct Reader<'a> { + input: no_panic::Slice<'a>, + i: usize, +} + +/// An index into the already-parsed input of a `Reader`. +pub struct Mark { + i: usize, +} + +impl<'a> Reader<'a> { + /// Construct a new Reader for the given input. Use `read_all` or + /// `read_all_optional` instead of `Reader::new` whenever possible. + #[inline] + pub fn new(input: Input<'a>) -> Self { + Self { + input: input.value, + i: 0, + } + } + + /// Returns `true` if the reader is at the end of the input, and `false` + /// otherwise. + #[inline] + pub fn at_end(&self) -> bool { self.i == self.input.len() } + + /// Returns an `Input` for already-parsed input that has had its boundaries + /// marked using `mark`. + #[inline] + pub fn get_input_between_marks( + &self, mark1: Mark, mark2: Mark, + ) -> Result<Input<'a>, EndOfInput> { + self.input + .subslice(mark1.i..mark2.i) + .map(|subslice| Input { value: subslice }) + .ok_or(EndOfInput) + } + + /// Return the current position of the `Reader` for future use in a call + /// to `get_input_between_marks`. + #[inline] + pub fn mark(&self) -> Mark { Mark { i: self.i } } + + /// Returns `true` if there is at least one more byte in the input and that + /// byte is equal to `b`, and false otherwise. + #[inline] + pub fn peek(&self, b: u8) -> bool { + match self.input.get(self.i) { + Some(actual_b) => b == *actual_b, + None => false, + } + } + + /// Reads the next input byte. + /// + /// Returns `Ok(b)` where `b` is the next input byte, or `Err(EndOfInput)` + /// if the `Reader` is at the end of the input. + #[inline] + pub fn read_byte(&mut self) -> Result<u8, EndOfInput> { + match self.input.get(self.i) { + Some(b) => { + self.i += 1; // safe from overflow; see Input::from(). + Ok(*b) + }, + None => Err(EndOfInput), + } + } + + /// Skips `num_bytes` of the input, returning the skipped input as an + /// `Input`. + /// + /// Returns `Ok(i)` if there are at least `num_bytes` of input remaining, + /// and `Err(EndOfInput)` otherwise. + #[inline] + pub fn read_bytes(&mut self, num_bytes: usize) -> Result<Input<'a>, EndOfInput> { + let new_i = self.i.checked_add(num_bytes).ok_or(EndOfInput)?; + let ret = self + .input + .subslice(self.i..new_i) + .map(|subslice| Input { value: subslice }) + .ok_or(EndOfInput)?; + self.i = new_i; + Ok(ret) + } + + /// Skips the reader to the end of the input, returning the skipped input + /// as an `Input`. + #[inline] + pub fn read_bytes_to_end(&mut self) -> Input<'a> { + let to_skip = self.input.len() - self.i; + self.read_bytes(to_skip).unwrap() + } + + /// Calls `read()` with the given input as a `Reader`. On success, returns a + /// pair `(bytes_read, r)` where `bytes_read` is what `read()` consumed and + /// `r` is `read()`'s return value. + pub fn read_partial<F, R, E>(&mut self, read: F) -> Result<(Input<'a>, R), E> + where + F: FnOnce(&mut Reader<'a>) -> Result<R, E>, + { + let start = self.i; + let r = read(self)?; + let bytes_read = Input { + value: self.input.subslice(start..self.i).unwrap() + }; + Ok((bytes_read, r)) + } + + /// Skips `num_bytes` of the input. + /// + /// Returns `Ok(i)` if there are at least `num_bytes` of input remaining, + /// and `Err(EndOfInput)` otherwise. + #[inline] + pub fn skip(&mut self, num_bytes: usize) -> Result<(), EndOfInput> { + self.read_bytes(num_bytes).map(|_| ()) + } + + /// Skips the reader to the end of the input. + #[inline] + pub fn skip_to_end(&mut self) -> () { let _ = self.read_bytes_to_end(); } +} + +/// The error type used to indicate the end of the input was reached before the +/// operation could be completed. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct EndOfInput; + +mod no_panic { + use core; + + /// A wrapper around a slice that exposes no functions that can panic. + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub struct Slice<'a> { + bytes: &'a [u8], + } + + impl<'a> Slice<'a> { + #[inline] + pub const fn new(bytes: &'a [u8]) -> Self { Self { bytes } } + + #[inline] + pub fn get(&self, i: usize) -> Option<&u8> { self.bytes.get(i) } + + #[inline] + pub fn subslice(&self, r: core::ops::Range<usize>) -> Option<Self> { + self.bytes.get(r).map(|bytes| Self { bytes }) + } + + #[inline] + pub fn is_empty(&self) -> bool { self.bytes.is_empty() } + + #[inline] + pub fn len(&self) -> usize { self.bytes.len() } + + #[inline] + pub fn as_slice_less_safe(&self) -> &'a [u8] { self.bytes } + } + +} // mod no_panic diff --git a/tests/tests.rs b/tests/tests.rs new file mode 100644 index 0000000..087e963 --- /dev/null +++ b/tests/tests.rs @@ -0,0 +1,98 @@ +// Copyright 2015-2019 Brian Smith. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +#[test] +fn test_input_from() { let _ = untrusted::Input::from(b"foo"); } + +#[test] +fn test_input_is_empty() { + let input = untrusted::Input::from(b""); + assert!(input.is_empty()); + let input = untrusted::Input::from(b"foo"); + assert!(!input.is_empty()); +} + +#[test] +fn test_input_len() { + let input = untrusted::Input::from(b"foo"); + assert_eq!(input.len(), 3); +} + +#[test] +fn test_input_read_all() { + let input = untrusted::Input::from(b"foo"); + let result = input.read_all(untrusted::EndOfInput, |input| { + assert_eq!(b'f', input.read_byte()?); + assert_eq!(b'o', input.read_byte()?); + assert_eq!(b'o', input.read_byte()?); + assert!(input.at_end()); + Ok(()) + }); + assert_eq!(result, Ok(())); +} + +#[test] +fn test_input_read_all_unconsume() { + let input = untrusted::Input::from(b"foo"); + let result = input.read_all(untrusted::EndOfInput, |input| { + assert_eq!(b'f', input.read_byte()?); + assert!(!input.at_end()); + Ok(()) + }); + assert_eq!(result, Err(untrusted::EndOfInput)); +} + +#[test] +fn test_input_as_slice_less_safe() { + let slice = b"foo"; + let input = untrusted::Input::from(slice); + assert_eq!(input.as_slice_less_safe(), slice); +} + +#[test] +fn using_reader_after_skip_and_get_error_returns_error_must_not_panic() { + let input = untrusted::Input::from(&[]); + let r = input.read_all(untrusted::EndOfInput, |input| { + let r = input.read_bytes(1); + assert_eq!(r, Err(untrusted::EndOfInput)); + Ok(input.read_bytes_to_end()) + }); + let _ = r; // "Use" r. The value of `r` is undefined here. +} + +#[test] +fn size_assumptions() { + // Assume that a pointer can address any point in the address space, and + // infer that this implies that a byte slice will never be + // `core::usize::MAX` bytes long. + assert_eq!(core::mem::size_of::<*const u8>(), core::mem::size_of::<usize>()); +} + +#[test] +fn const_fn() { + const _INPUT: untrusted::Input<'static> = untrusted::Input::from(&[]); +} + +#[test] +fn test_vec_into() { + extern crate std; + let vec = vec![0u8; 0]; + let _x: untrusted::Input = (&vec[..]).into(); +} + +#[test] +fn test_from_slice() { + let slice: &[u8] = &[0u8]; + let _x: untrusted::Input = slice.into(); +}
\ No newline at end of file |