├── .gitignore ├── .travis.yml ├── .travis └── publish_docs.sh ├── COPYING ├── Cargo.toml ├── README.md ├── bench ├── Cargo.toml └── bench.rs ├── codegen ├── Cargo.toml └── src │ ├── build.rs │ ├── lib.rs │ └── syntex.rs ├── io ├── Cargo.toml └── src │ ├── align.rs │ ├── buf_seeker.rs │ ├── lib.rs │ ├── read_exact.rs │ ├── region.rs │ ├── seek_forward.rs │ └── take.rs ├── macros ├── Cargo.toml ├── doctest │ └── Cargo.toml ├── src │ └── lib.rs └── tests │ ├── code.rs │ └── test.rs ├── packed ├── Cargo.toml └── src │ └── lib.rs ├── pod ├── Cargo.toml ├── src │ ├── code.rs │ ├── endian.rs │ ├── lib.rs │ └── pod.rs └── tests │ └── test.rs ├── src.rs ├── tests ├── Cargo.toml ├── build.rs └── tests │ └── test.rs └── uninitialized ├── Cargo.toml └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | Cargo.lock 3 | target/ 4 | /.gitattributes 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - nightly 4 | - beta 5 | - stable 6 | sudo: false 7 | os: 8 | - linux 9 | 10 | env: 11 | global: 12 | - secure: "mmDJNwk0zfPTK6JHDMcHlfs+If7jFMVKUcZGJQCCv5ESvORMEnfuAigI4VTqogw96cteaaAgWwRKdwRFBjfLAf4nYw1f+4M1cxUBGt5n5+bo6/AQVYsLfiz6NAez6d1yDqxChGSrZtWbDDh9+HU27tzUV6x6rOHeetV34zAfX16z7ppTaOW6YJgPINQ5c0oHYgk+Oa0yqlgdwuq9IcQnff5n7F/5eD426b1RDfh8zCsGuYj/1DPTR8l6t5jwShX2jAM3uRnvnSEIbMKQJFUCWQkoXS6BPQ31seWt6x+3i0qJlcA5Kb/ghc6U7nMTfxQMEXyAsnoMms9vBY+Q7JOfuZiyyELM7sigylOU6PDf+ORjqgyU5AI7Dletrt3NB4tFSj/Za6h/d2v5GOPsF7PRa65tJcMtUthW4cXv8Yict8OD/dDoz9IvqWwFGU/yKbhY+rvThRJxpmsDx8CcE1obuHunFqggIQn0mX5niZliJILkcGU2qYMc27szSlFLjMtgEkxkxlHtKUGVdqdo+lod7H5bq8nfw4/TKU2mzC5Kf7FbeLvdAUPwDTaThGpcgmQWiD52hEcvAFyR83JVsZ+fSXpyzBtrdkLNkrHk83qxHdTjkWV0RXQlno255MCEhGGKPTxpV+lD5voMTV2idxCO8QvFsNY6zAXfbKAid+Z89h4=" 13 | - secure: "JjFImwQpfHO/hkrDEGAdvG35m0l7HY7pDHaupVhk8nXqPCzIxWhYA2rueP8JIuItfi7jh3Fw43LBjVwidcnXUGLOxsviPdbIQxo51xlKct0zF5dnTDaixAAyrXchgv4scLIxOWAj8z2Me2xFmpVBGiLyS9z0uxuyjV6re7bxU/sHQ7rRIVW1oVCRFiXRXsUIqcgW28fsMUfePswO6+taRBRcaThGYfZ1s3cRfCHFTV+QkWjDANf2CiXy3cHE/YqGAV8yicXvC8CLoiXFt+N6EIhf5UXMZRROgsFRjPp7f42j70Jt9V3vOdcq94NG8Yiv54bRZ/fK5mSCD+f5TjXVcJfx7elwSKrbJB10YDR299wPSrOqpIF2ANRc7lq0+7qU/KeWWnEj4Y+59NxufBC6SiutFFcPzHxhMZf+uhY2/m1Vlw33Hc7ufdzmFENRzrKFCuFkXY+t0xNbUKD7qKG3F07fXipxMiTvITjHPtZC+H6sLeFOmwkKqPm4mvr0ZMvClkenzhiXhaRhEJ8fpSMzsS7PVzyzVxw0LEWyw/BQqjaCn/gb0m74Dmus9ZiotBHPVQsS/QM/qkBhncXv/kz0yalTBwOt90E4DQsoIZiYlW7D8DZ/hEJqvWBoa8HkjNR9HJP5UjCgwKzxSh2iYzPutHftvUoRtM3nThzxd39dTzI=" 14 | matrix: 15 | - FEATURES= 16 | ALT= 17 | 18 | matrix: 19 | include: 20 | - rust: nightly 21 | env: FEATURES="--features unstable" 22 | ALT="-unstable" 23 | 24 | cache: 25 | directories: 26 | - $TRAVIS_BUILD_DIR/target 27 | - $HOME/.cargo 28 | 29 | script: 30 | - export CARGO_TARGET_DIR="$TRAVIS_BUILD_DIR/target/$TRAVIS_RUST_VERSION$ALT" 31 | - (cd uninitialized && cargo test -v) 32 | - (cd packed && cargo test -v $FEATURES) 33 | - (cd io && cargo test -v) 34 | - (cd pod && cargo test -v $FEATURES) 35 | - (cargo build -v $FEATURES) 36 | - (cd codegen && cargo test) 37 | - (cd tests && cargo test -v $FEATURES) 38 | - "[ $TRAVIS_RUST_VERSION != nightly ] || (cd macros && cargo test -v && cd doctest && cargo test -v)" 39 | 40 | before_deploy: 41 | - (cd uninitialized && cargo doc --no-deps -v && cargo package -v && cargo publish --token "$CRATES_IO_TOKEN" || true) 42 | - (cd packed && cargo doc --no-deps -v && cargo package -v && cargo publish --token "$CRATES_IO_TOKEN" || true) 43 | - (cd io && cargo doc --no-deps -v && cargo package -v && cargo publish --token "$CRATES_IO_TOKEN" || true) 44 | - (cd pod && cargo doc --no-deps -v && cargo package -v && cargo publish --token "$CRATES_IO_TOKEN" || true) 45 | - (cd codegen && cargo doc --no-deps -v && cargo package -v && cargo publish --token "$CRATES_IO_TOKEN" || true) 46 | - (cd macros && cargo doc --no-deps -v && cargo package -v && cargo publish --token "$CRATES_IO_TOKEN" || true) 47 | - (cargo doc --no-deps -v && cargo package -v && cargo publish --token "$CRATES_IO_TOKEN" || true) 48 | - (cd bench && cargo bench -q > $CARGO_TARGET_DIR/benchmarks.txt) 49 | 50 | deploy: 51 | provider: releases 52 | api_key: 53 | secure: "Y7yRI4sQySToVsYtwVRMEIcd7CD0nuh1MarSfeQp6GKbvY5j8MUyYbZk8CvvlHD8bI/kicvb1/jxNY+SBhIgxNWXDtxIg8Qv9B8dpSfXctHyxXJkRlHZZZHfrkiPmHtWW8S1PCkfRgpP5wHIu1d5V6hAY+kQo4eiAm4bZY6njap3by25NiyWCZmgFkMPsoRyZGE0d2Vm28e2xHyENjeDvjwk8KjarS7I4h9JEq8xV7xQp1LGlOLqDbgcZPECFS9AzMsodmPBcG38TU7DTkq2gam6lNTluVjaOEvyfrTlTTAtbh+uWelCNzUVQgHHcPXHllZpkxSdnEoZx99y4oLHK91X2qryBypJ8kqOFHfweys3DvL/Mm/520IaNA0JpjdMai9DXuS+glz988DPiWK25vdHSiIq0JsAJVQ6binZvTFpsZfjDlDjC51Gxa7VCzwTisFikw60llI346wBYgFOKmQgXZp/CRr0buEmc3Rs5TsdbtkM7GTTDorfeVJdK9HwjwEU87mpudcR5/kQqRhG4pPElv2N4VwBrhh0YgNupVaDpQhDwexQv6AvwTCMTTwHYeOEOCtBdj90XyyI0uNcET5O11Pybmc4eH4PBM/HsvuYjJFVWNVG+kQQilkAk0AYGxOBVKR0hJmaumPGLOF/Q/YYJUZIqG0N1wQV9WHgGjo=" 54 | file: $CARGO_TARGET_DIR/package/packed-$TRAVIS_TAG.crate 55 | file: $CARGO_TARGET_DIR/package/nue-io-$TRAVIS_TAG.crate 56 | file: $CARGO_TARGET_DIR/package/pod-$TRAVIS_TAG.crate 57 | file: $CARGO_TARGET_DIR/package/nue-codegen-$TRAVIS_TAG.crate 58 | file: $CARGO_TARGET_DIR/package/nue-macros-$TRAVIS_TAG.crate 59 | file: $CARGO_TARGET_DIR/package/nue-$TRAVIS_TAG.crate 60 | skip_cleanup: true 61 | on: 62 | tags: true 63 | all_branches: true 64 | condition: 65 | - "$TRAVIS_RUST_VERSION = nightly" 66 | - "-n \"$FEATURES\"" 67 | 68 | after_deploy: 69 | - .travis/publish_docs.sh "$TRAVIS_TAG" 70 | -------------------------------------------------------------------------------- /.travis/publish_docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eu 3 | 4 | TAG=$1 5 | 6 | cd $CARGO_TARGET_DIR/doc 7 | cp -a ../benchmarks.txt ./ 8 | 9 | git init 10 | git config user.name "arcnmx" 11 | git config user.email "arcnmx@users.noreply.github.com" 12 | 13 | git remote add origin "https://$GH_TOKEN@github.com/$TRAVIS_REPO_SLUG" 14 | git fetch origin gh-pages 15 | git checkout -b gh-pages 16 | git reset origin/gh-pages 17 | 18 | git add -A . 19 | git commit -m "$TAG" 20 | git push -q origin HEAD:gh-pages 21 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 arcnmx 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nue" 3 | version = "0.3.0" 4 | authors = ["arcnmx"] 5 | 6 | description = "I/O, POD, and misc. binary data encoding" 7 | documentation = "http://arcnmx.github.io/nue/nue/" 8 | repository = "https://github.com/arcnmx/nue" 9 | readme = "README.md" 10 | keywords = ["nue", "pod", "data", "encode", "endian"] 11 | license = "MIT" 12 | 13 | [lib] 14 | name = "nue" 15 | path = "src.rs" 16 | 17 | [[bench]] 18 | name = "nue" 19 | path = "bench.rs" 20 | 21 | [features] 22 | unstable = ["pod/unstable", "packed/unstable"] 23 | 24 | [dependencies.nue-io] 25 | version = "0.3" 26 | path = "io" 27 | 28 | [dependencies.pod] 29 | version = "0.3" 30 | path = "pod" 31 | 32 | [dependencies.packed] 33 | version = "0.3" 34 | path = "packed" 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nue 2 | 3 | [![travis-badge][]][travis] [![release-badge][]][cargo] [![docs-badge][]][docs] [![license-badge][]][license] 4 | 5 | A collection of tools for working with binary data and POD structs in Rust. 6 | 7 | - [pod][docs-pod] is an approach at building a safe interface for 8 | transmuting POD structs to and from byte slices. 9 | - [packed][docs-packed] exposes unaligned packed data structurs in a safe 10 | and stable manner. 11 | - [nue-macros][docs-macros] provides helpers for `pod`, as well 12 | as a serialization-like library for dealing with binary streams of data. 13 | - [nue-codegen][docs-codegen] allows the use of `nue-macros` without 14 | syntax extensions on stable Rust. 15 | - [nue-io][docs-io] contains various supporting structs and traits for 16 | readers and writers. 17 | 18 | 19 | [travis-badge]: https://img.shields.io/travis/arcnmx/nue/master.svg?style=flat-square 20 | [travis]: https://travis-ci.org/arcnmx/nue 21 | [release-badge]: https://img.shields.io/crates/v/nue.svg?style=flat-square 22 | [cargo]: https://crates.io/search?q=nue 23 | [docs-badge]: https://img.shields.io/badge/API-docs-blue.svg?style=flat-square 24 | [docs]: http://arcnmx.github.io/nue/nue/ 25 | [docs-packed]: http://arcnmx.github.io/nue/packed/ 26 | [docs-io]: http://arcnmx.github.io/nue/nue_io/ 27 | [docs-pod]: http://arcnmx.github.io/nue/pod/ 28 | [docs-macros]: http://arcnmx.github.io/nue/nue_macros/ 29 | [docs-codegen]: http://arcnmx.github.io/nue/nue_codegen/ 30 | [license-badge]: https://img.shields.io/badge/license-MIT-lightgray.svg?style=flat-square 31 | [license]: https://github.com/arcnmx/nue/blob/master/COPYING 32 | -------------------------------------------------------------------------------- /bench/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nue-bench" 3 | version = "0.0.0" 4 | authors = ["arcnmx"] 5 | 6 | [[bench]] 7 | name = "nue" 8 | path = "bench.rs" 9 | 10 | [features] 11 | unstable = ["nue/unstable"] 12 | 13 | [dev-dependencies.nue] 14 | path = ".." 15 | 16 | [dev-dependencies.pod] 17 | path = "../pod" 18 | 19 | [dev-dependencies.nue-macros] 20 | path = "../macros" 21 | -------------------------------------------------------------------------------- /bench/bench.rs: -------------------------------------------------------------------------------- 1 | #![feature(custom_attribute, plugin, custom_derive, test)] 2 | #![plugin(nue_macros)] 3 | 4 | extern crate test; 5 | extern crate nue; 6 | extern crate pod; 7 | 8 | use std::mem::size_of; 9 | use std::io::{BufReader, Cursor, Repeat, Seek, SeekFrom, repeat}; 10 | use pod::{Be, Le, Pod, Decode, Encode}; 11 | use test::black_box as bb; 12 | use test::Bencher; 13 | 14 | #[derive(Copy, Clone, PodPacked)] 15 | struct DataPod { 16 | data: [u8; 0x20], 17 | magic: Be, 18 | random: [Le; 0x10], 19 | } 20 | 21 | #[derive(NueEncode, NueDecode)] 22 | struct DataCode { 23 | data: [u8; 0x20], 24 | magic: Be, 25 | random: [Le; 0x10], 26 | } 27 | 28 | fn repeat_stream() -> BufReader { 29 | BufReader::new(repeat(0x5a)) 30 | } 31 | 32 | fn memory_stream() -> Cursor> { 33 | Cursor::new(vec![0x5a; 0x200]) 34 | } 35 | 36 | fn data_pod() -> DataPod { 37 | DataPod { 38 | data: [0x5a; 0x20], 39 | magic: Be::new(0x5a5a5a5a), 40 | random: [Le::new(0x5a5a5a5a5a5a5a5a); 0x10], 41 | } 42 | } 43 | 44 | fn data_code() -> DataCode { 45 | DataCode { 46 | data: [0x5a; 0x20], 47 | magic: Be::new(0x5a5a5a5a), 48 | random: [Le::new(0x5a5a5a5a5a5a5a5a); 0x10], 49 | } 50 | } 51 | 52 | #[bench] 53 | fn bench_repeat_pod_decode(b: &mut Bencher) { 54 | let mut stream = repeat_stream(); 55 | 56 | b.iter(|| bb(DataPod::decode(&mut stream).unwrap())); 57 | } 58 | 59 | #[bench] 60 | fn bench_repeat_decode(b: &mut Bencher) { 61 | let mut stream = repeat_stream(); 62 | 63 | b.iter(|| bb(DataCode::decode(&mut stream).unwrap())); 64 | } 65 | 66 | #[bench] 67 | fn bench_pod_decode(b: &mut Bencher) { 68 | let mut stream = memory_stream(); 69 | 70 | b.iter(|| { 71 | stream.seek(SeekFrom::Start(0)).unwrap(); 72 | bb(DataPod::decode(&mut stream).unwrap()) 73 | }); 74 | } 75 | 76 | #[bench] 77 | fn bench_decode(b: &mut Bencher) { 78 | let mut stream = memory_stream(); 79 | 80 | b.iter(|| { 81 | stream.seek(SeekFrom::Start(0)).unwrap(); 82 | bb(DataCode::decode(&mut stream).unwrap()) 83 | }); 84 | } 85 | 86 | #[bench] 87 | fn bench_pod_decode_ref(b: &mut Bencher) { 88 | let stream = memory_stream().into_inner(); 89 | 90 | b.iter(|| bb(DataPod::from_slice(&stream[..size_of::()]))); 91 | } 92 | 93 | #[bench] 94 | fn bench_pod_decode_ref_copy(b: &mut Bencher) { 95 | let stream = memory_stream().into_inner(); 96 | 97 | b.iter(|| bb(*DataPod::from_slice(&stream[..size_of::()]))); 98 | } 99 | 100 | #[bench] 101 | fn bench_pod_encode(b: &mut Bencher) { 102 | let mut stream = memory_stream(); 103 | let data = data_pod(); 104 | 105 | b.iter(|| { 106 | stream.seek(SeekFrom::Start(0)).unwrap(); 107 | data.encode(&mut stream).unwrap(); 108 | bb(&stream); 109 | }); 110 | } 111 | 112 | #[bench] 113 | fn bench_encode(b: &mut Bencher) { 114 | let mut stream = memory_stream(); 115 | let data = data_code(); 116 | 117 | b.iter(|| { 118 | stream.seek(SeekFrom::Start(0)).unwrap(); 119 | data.encode(&mut stream).unwrap(); 120 | bb(&stream); 121 | }); 122 | } 123 | 124 | #[bench] 125 | fn bench_pod_encode_ref(b: &mut Bencher) { 126 | let mut stream = memory_stream().into_inner(); 127 | let data = data_pod(); 128 | 129 | b.iter(|| { 130 | let mem = Pod::from_mut_slice(&mut stream[..size_of::()]); 131 | *mem = data; 132 | bb(mem); 133 | }); 134 | } 135 | -------------------------------------------------------------------------------- /codegen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nue-codegen" 3 | version = "0.3.0" 4 | authors = ["arcnmx"] 5 | 6 | description = "Stable POD and binary data encoding I/O macros" 7 | documentation = "http://arcnmx.github.io/nue/nue_macros/" 8 | repository = "https://github.com/arcnmx/nue" 9 | readme = "../README.md" 10 | keywords = ["nue", "pod", "data", "encode", "decode"] 11 | license = "MIT" 12 | 13 | build = "src/build.rs" 14 | 15 | [features] 16 | default = ["with-syntex"] 17 | unstable = ["quasi_macros"] 18 | with-syntex = ["quasi/with-syntex", "quasi_codegen", "quasi_codegen/with-syntex", "syntex", "syntex_syntax"] 19 | 20 | [lib] 21 | name = "nue_codegen" 22 | path = "src/syntex.rs" 23 | 24 | [build-dependencies] 25 | quasi_codegen = { verision = "0.3", optional = true } 26 | syntex = { version = "0.7", optional = true } 27 | 28 | [dependencies] 29 | aster = { version = "0.4", default-features = false } 30 | quasi = { verision = "0.3", default-features = false } 31 | quasi_macros = { version = "0.3", optional = true } 32 | syntex = { version = "0.7", optional = true } 33 | syntex_syntax = { version = "0.10", optional = true } 34 | -------------------------------------------------------------------------------- /codegen/src/build.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "with-syntex")] 2 | mod inner { 3 | extern crate syntex; 4 | extern crate quasi_codegen; 5 | 6 | use std::env; 7 | use std::path::Path; 8 | 9 | pub fn main() { 10 | let out_dir = env::var_os("OUT_DIR").unwrap(); 11 | let mut registry = syntex::Registry::new(); 12 | quasi_codegen::register(&mut registry); 13 | 14 | let src = Path::new("src/lib.rs"); 15 | let dst = Path::new(&out_dir).join("lib.rs"); 16 | 17 | registry.expand("", &src, &dst).unwrap(); 18 | } 19 | } 20 | 21 | #[cfg(not(feature = "with-syntex"))] 22 | mod inner { 23 | pub fn main() {} 24 | } 25 | 26 | fn main() { 27 | inner::main(); 28 | } 29 | -------------------------------------------------------------------------------- /codegen/src/lib.rs: -------------------------------------------------------------------------------- 1 | use aster::AstBuilder; 2 | use quasi::ExtParseUtils; 3 | use syntax::ast::{self, MetaItem, MetaItem_, StructField_, Lit_}; 4 | use syntax::codemap::{Span, Spanned}; 5 | use syntax::ext::base::{Annotatable, ExtCtxt}; 6 | use syntax::ptr::P; 7 | use syntax::attr; 8 | 9 | fn derive_type<'a>(cx: &mut ExtCtxt, span: Span, meta_item: &MetaItem, annotatable: &'a Annotatable) -> 10 | Option<(AstBuilder, &'a P, ast::Generics, P, ast::Path)> { 11 | let item = match annotatable { 12 | &Annotatable::Item(ref item) => item, 13 | _ => { 14 | cx.span_err(meta_item.span, "`derive` may only be applied to structs or enums"); 15 | return None; 16 | } 17 | }; 18 | 19 | let generics = match item.node { 20 | ast::ItemStruct(_, ref generics) => generics, 21 | ast::ItemEnum(_, ref generics) => generics, 22 | _ => { 23 | cx.span_err(meta_item.span, "`derive` may only be applied to structs or enums"); 24 | return None; 25 | }, 26 | }; 27 | 28 | let builder = AstBuilder::new().span(span); 29 | 30 | let generics = builder.from_generics(generics.clone()) 31 | .add_ty_param_bound( 32 | builder.path().global().ids(&["pod", "Pod"]).build() 33 | ).build(); 34 | 35 | let ty_path = builder.path().segment(item.ident).with_generics(generics.clone()).build().build(); 36 | let ty = builder.ty().build_path(ty_path.clone()); 37 | 38 | Some((builder, item, generics, ty, ty_path)) 39 | } 40 | 41 | fn expand_packed(cx: &mut ExtCtxt, span: Span, meta_item: &MetaItem, annotatable: Annotatable) -> Annotatable { 42 | let item = match annotatable { 43 | Annotatable::Item(item) => item, 44 | _ => { 45 | cx.span_err(meta_item.span, "`derive` may only be applied to structs or enums"); 46 | return annotatable; 47 | } 48 | }; 49 | 50 | let builder = AstBuilder::new().span(span); 51 | 52 | let repr = builder.attr().list("repr").words(["C"].iter()).build(); 53 | let packed = builder.attr().word("__nue_packed"); 54 | let derive_packed = builder.attr().word("derive_Packed"); 55 | 56 | Annotatable::Item(item.map(|mut item| { 57 | attr::mark_used(&packed); 58 | item.attrs.push(repr); 59 | item.attrs.push(packed); 60 | item.attrs.push(derive_packed); 61 | item 62 | })) 63 | } 64 | 65 | fn expand_derive_pod_packed(cx: &mut ExtCtxt, span: Span, meta_item: &MetaItem, annotatable: Annotatable) -> Annotatable { 66 | let item = match annotatable { 67 | Annotatable::Item(item) => item, 68 | _ => { 69 | cx.span_err(meta_item.span, "`derive` may only be applied to structs or enums"); 70 | return annotatable; 71 | } 72 | }; 73 | 74 | let builder = AstBuilder::new().span(span); 75 | 76 | let packed = builder.attr().word("packed"); 77 | let derive_pod = builder.attr().word("derive_Pod"); 78 | 79 | Annotatable::Item(item.map(|mut item| { 80 | item.attrs.push(packed); 81 | item.attrs.push(derive_pod); 82 | item 83 | })) 84 | } 85 | 86 | fn expr_is_false(expr: &P) -> bool { 87 | match &expr.node { 88 | &ast::Expr_::ExprLit(ref lit) if lit.node == Lit_::LitBool(false) => true, 89 | _ => false, 90 | } 91 | } 92 | 93 | fn expand_derive_packed(cx: &mut ExtCtxt, span: Span, meta_item: &MetaItem, annotatable: &Annotatable, push: &mut FnMut(Annotatable)) { 94 | let (_, item, generics, ty, _) = if let Some(ret) = derive_type(cx, span, meta_item, annotatable) { 95 | ret 96 | } else { 97 | return 98 | }; 99 | 100 | if !item.attrs.iter().any(|a| match &a.node.value.node { 101 | &MetaItem_::MetaWord(ref name) if *name == "__nue_packed" || *name == "packed" => true, 102 | _ => false, 103 | }) { 104 | cx.span_err(meta_item.span, "packed types require #[packed]"); 105 | return; 106 | } 107 | 108 | let assertions = match item.node { 109 | ast::ItemStruct(ref struct_def, _) => { 110 | struct_def.fields.iter().map(|field| { 111 | let ty = &field.node.ty; 112 | quote_stmt!(cx, assert::<$ty>();).unwrap() 113 | }).collect::>() 114 | }, 115 | _ => { 116 | cx.span_err(meta_item.span, "packed types must be structs"); 117 | return 118 | }, 119 | }; 120 | 121 | let where_clause = &generics.where_clause; 122 | 123 | let impl_item = quote_item!(cx, 124 | #[automatically_derived] 125 | unsafe impl $generics ::nue::Packed for $ty $where_clause { 126 | fn __assert_unaligned() { 127 | fn assert() { } 128 | 129 | $assertions 130 | } 131 | } 132 | ).unwrap(); 133 | push(Annotatable::Item(impl_item)); 134 | 135 | let impl_item = quote_item!(cx, 136 | #[automatically_derived] 137 | unsafe impl $generics ::nue::Unaligned for $ty $where_clause { } 138 | ).unwrap(); 139 | push(Annotatable::Item(impl_item)); 140 | } 141 | 142 | fn expand_derive_pod(cx: &mut ExtCtxt, span: Span, meta_item: &MetaItem, annotatable: &Annotatable, push: &mut FnMut(Annotatable)) { 143 | let (_, item, generics, ty, _) = if let Some(ret) = derive_type(cx, span, meta_item, annotatable) { 144 | ret 145 | } else { 146 | return 147 | }; 148 | 149 | if !item.attrs.iter().any(|a| match &a.node.value.node { 150 | &MetaItem_::MetaWord(ref name) if *name == "__nue_packed" || *name == "packed" => true, 151 | _ => false, 152 | }) { 153 | cx.span_err(meta_item.span, "POD types require #[packed]"); 154 | return; 155 | } 156 | 157 | let assertions = match item.node { 158 | ast::ItemStruct(ref struct_def, _) => { 159 | struct_def.fields.iter().map(|field| { 160 | let ty = &field.node.ty; 161 | quote_stmt!(cx, assert::<$ty>();).unwrap() 162 | }).collect::>() 163 | }, 164 | _ => { 165 | cx.span_err(meta_item.span, "POD types must be structs"); 166 | return 167 | }, 168 | }; 169 | 170 | let where_clause = &generics.where_clause; 171 | 172 | let impl_item = quote_item!(cx, 173 | #[automatically_derived] 174 | unsafe impl $generics ::pod::Pod for $ty $where_clause { 175 | fn __assert_pod() { 176 | fn assert() { } 177 | 178 | $assertions 179 | } 180 | } 181 | ).unwrap(); 182 | 183 | push(Annotatable::Item(impl_item)) 184 | } 185 | 186 | fn expand_derive_encode(cx: &mut ExtCtxt, span: Span, meta_item: &MetaItem, annotatable: &Annotatable, push: &mut FnMut(Annotatable)) { 187 | let (builder, item, generics, ty, _) = if let Some(ret) = derive_type(cx, span, meta_item, annotatable) { 188 | ret 189 | } else { 190 | return 191 | }; 192 | 193 | let mut needs_seek = false; 194 | 195 | let encoders = match item.node { 196 | ast::ItemStruct(ref struct_def, _) => { 197 | struct_def.fields.iter().enumerate().map(|(i, field)| { 198 | let field = &field.node; 199 | let expr = match field.kind { 200 | ast::NamedField(name, _) => quote_expr!(cx, &self.$name), 201 | ast::UnnamedField(_) => builder.expr().addr_of().tup_field(i).build(builder.expr().self_()), 202 | }; 203 | 204 | let mut cond = None; 205 | 206 | let statement = quote_stmt!(cx, 207 | let _ = try!(::nue::Encode::encode($expr, __w)); 208 | ).unwrap(); 209 | let mut statement = vec![statement]; 210 | 211 | for attr in field_attrs(cx, field, "nue_enc", false) { 212 | match attr { 213 | FieldAttribute::Cond(expr) => cond = Some(expr), 214 | FieldAttribute::Default(_) => (), 215 | FieldAttribute::Align(expr) => { 216 | needs_seek = true; 217 | statement.insert(0, quote_stmt!(cx, let _ = try!(::nue::SeekAlignExt::align_to(__w, $expr)); ).unwrap()); 218 | }, 219 | FieldAttribute::Skip(expr) => { 220 | needs_seek = true; 221 | statement.insert(0, quote_stmt!(cx, 222 | let _ = try!(::nue::SeekForward::seek_forward(__w, $expr)); 223 | ).unwrap()); 224 | }, 225 | FieldAttribute::Limit(expr) => statement.insert(0, quote_stmt!(cx, let __w = &mut ::nue::Take::new(::std::borrow::BorrowMut::borrow_mut(__w), $expr); ).unwrap()), 226 | FieldAttribute::Consume(expr) => statement.push(quote_stmt!(cx, 227 | if $expr { 228 | let _ = try!(match ::std::io::copy(&mut ::std::io::repeat(0), __w) { 229 | ::std::result::Result::Err(ref err) if err.kind() == ::std::io::ErrorKind::WriteZero => Ok(0), 230 | res => res, 231 | }); 232 | } 233 | ).unwrap()), 234 | FieldAttribute::Assert(expr) => statement.insert(0, quote_stmt!(cx, 235 | if !$expr { 236 | return Err(::std::io::Error::new(::std::io::ErrorKind::InvalidInput, concat!("assertion ", stringify!($expr), " failed"))); 237 | } 238 | ).unwrap()), 239 | } 240 | } 241 | 242 | if let Some(cond) = cond { 243 | if expr_is_false(&cond) { 244 | quote_stmt!(cx, {}).unwrap() 245 | } else { 246 | quote_stmt!(cx, 247 | if $cond { 248 | $statement 249 | } 250 | ).unwrap() 251 | } 252 | } else { 253 | quote_stmt!(cx, { $statement }).unwrap() 254 | } 255 | }).collect::>() 256 | }, 257 | ast::ItemEnum(..) => unimplemented!(), 258 | _ => { 259 | cx.span_err(meta_item.span, "`derive` must be used on structs and enums"); 260 | return; 261 | }, 262 | }; 263 | 264 | let needs_seek = if needs_seek { 265 | quote_stmt!(cx, 266 | let __w = &mut ::nue::ReadWriteTell::new(::nue::SeekForwardWrite::new(::nue::SeekAll::new(__w))); 267 | ) 268 | } else { 269 | quote_stmt!(cx, let __w = &mut ::nue::SeekAll::new(__w);) 270 | }.unwrap(); 271 | 272 | let where_clause = &generics.where_clause; 273 | 274 | let impl_item = quote_item!(cx, 275 | #[automatically_derived] 276 | impl $generics ::nue::Encode for $ty $where_clause { 277 | type Options = (); 278 | 279 | fn encode<__W: ::std::io::Write>(&self, __w: &mut __W) -> ::std::io::Result<()> { 280 | $needs_seek 281 | $encoders 282 | 283 | Ok(()) 284 | } 285 | } 286 | ).unwrap(); 287 | 288 | push(Annotatable::Item(impl_item)); 289 | } 290 | 291 | fn expand_derive_decode(cx: &mut ExtCtxt, span: Span, meta_item: &MetaItem, annotatable: &Annotatable, push: &mut FnMut(Annotatable)) { 292 | let (builder, item, generics, ty, ty_path) = if let Some(ret) = derive_type(cx, span, meta_item, annotatable) { 293 | ret 294 | } else { 295 | return 296 | }; 297 | 298 | let mut needs_seek = false; 299 | let mut tuple_struct = false; 300 | 301 | let (decoders, decoder_fields) = match item.node { 302 | ast::ItemStruct(ref struct_def, _) => { 303 | struct_def.fields.iter().enumerate().map(|(i, field)| { 304 | let field = &field.node; 305 | let (let_name, field_name) = match field.kind { 306 | ast::NamedField(name, _) => (builder.id(format!("__self_0{}", name)), Some(name)), 307 | ast::UnnamedField(_) => { 308 | tuple_struct = true; 309 | (builder.id(format!("__self_0{}", i)), None) 310 | }, 311 | }; 312 | 313 | let (mut cond, mut cond_default) = (None, None); 314 | let field_type = &field.ty; 315 | 316 | let statement = quote_stmt!(cx, 317 | let $let_name: $field_type = try!(::nue::Decode::decode(__r)); 318 | ).unwrap(); 319 | let mut statement = vec![statement]; 320 | 321 | for attr in field_attrs(cx, field, "nue_dec", true) { 322 | match attr { 323 | FieldAttribute::Cond(expr) => cond = Some(expr), 324 | FieldAttribute::Default(expr) => cond_default = Some(expr), 325 | FieldAttribute::Align(expr) => { 326 | needs_seek = true; 327 | statement.insert(0, quote_stmt!(cx, let _ = try!(::nue::SeekAlignExt::align_to(__r, $expr)); ).unwrap()); 328 | }, 329 | FieldAttribute::Skip(expr) => { 330 | needs_seek = true; 331 | statement.insert(0, quote_stmt!(cx, 332 | let _ = try!(::nue::SeekForward::seek_forward(__r, $expr)); 333 | ).unwrap()); 334 | }, 335 | FieldAttribute::Limit(expr) => statement.insert(0, quote_stmt!(cx, let __r = &mut ::nue::Take::new(::std::borrow::BorrowMut::borrow_mut(__r), $expr); ).unwrap()), 336 | FieldAttribute::Consume(expr) => statement.push(quote_stmt!(cx, 337 | if $expr { 338 | let _ = try!(::std::io::copy(__r, &mut ::std::io::sink())); 339 | } 340 | ).unwrap()), 341 | FieldAttribute::Assert(expr) => statement.push(quote_stmt!(cx, 342 | if !$expr { 343 | return Err(::std::io::Error::new(::std::io::ErrorKind::InvalidInput, concat!("assertion ", stringify!($expr), " failed"))); 344 | } 345 | ).unwrap()), 346 | 347 | } 348 | } 349 | 350 | let statement = if let Some(cond) = cond { 351 | let default = cond_default.unwrap_or_else(|| quote_expr!(cx, ::std::default::Default::default())); 352 | 353 | if expr_is_false(&cond) { 354 | quote_stmt!(cx, let $let_name = $default;).unwrap() 355 | } else { 356 | quote_stmt!(cx, 357 | let $let_name = if $cond { 358 | $statement; 359 | $let_name 360 | } else { 361 | $default 362 | }; 363 | ).unwrap() 364 | } 365 | } else { 366 | quote_stmt!(cx, let $let_name = { $statement; $let_name };).unwrap() 367 | }; 368 | 369 | (statement, (let_name, field_name)) 370 | }).unzip::<_, _, Vec<_>, Vec<_>>() 371 | }, 372 | ast::ItemEnum(..) => { 373 | cx.span_err(meta_item.span, "enums cannot be decoded"); 374 | return; 375 | }, 376 | _ => { 377 | cx.span_err(meta_item.span, "`derive` must be used on structs and enums"); 378 | return; 379 | }, 380 | }; 381 | 382 | let needs_seek = if needs_seek { 383 | quote_stmt!(cx, 384 | let __r = &mut ::nue::ReadWriteTell::new(::nue::SeekForwardRead::new(::nue::SeekAll::new(__r))); 385 | ) 386 | } else { 387 | quote_stmt!(cx, let __r = &mut ::nue::SeekAll::new(__r);) 388 | }.unwrap(); 389 | 390 | let result = if tuple_struct { 391 | builder.expr().call().build_path(ty_path).with_args(decoder_fields.into_iter().map(|(let_name, _)| builder.expr().id(let_name))).build() 392 | } else { 393 | builder.expr().struct_path(ty_path).with_id_exprs(decoder_fields.into_iter().map(|(let_name, field_name)| (field_name.unwrap(), builder.expr().id(let_name)))).build() 394 | }; 395 | 396 | let where_clause = &generics.where_clause; 397 | 398 | let impl_item = quote_item!(cx, 399 | #[automatically_derived] 400 | impl $generics ::nue::Decode for $ty $where_clause { 401 | type Options = (); 402 | 403 | fn decode<__R: ::std::io::Read>(__r: &mut __R) -> ::std::io::Result { 404 | $needs_seek 405 | $decoders 406 | let __result = $result; 407 | 408 | let _ = try!(::nue::Decode::validate(&__result)); 409 | 410 | Ok(__result) 411 | } 412 | } 413 | ).unwrap(); 414 | 415 | push(Annotatable::Item(impl_item)); 416 | } 417 | 418 | fn field_attrs(cx: &mut ExtCtxt, field: &StructField_, meta_name: &'static str, replace_self: bool) -> Vec { 419 | fn attr_expr(cx: &mut ExtCtxt, replace_self: bool, value: &str) -> P { 420 | let value = if replace_self { 421 | value.replace("self.", "__self_0") 422 | } else { 423 | value.into() 424 | }; 425 | cx.parse_expr(value) 426 | } 427 | 428 | let attr = field.attrs.iter().filter_map(|v| match &v.node.value.node { 429 | &MetaItem_::MetaList(ref name, ref attrs) if *name == meta_name || *name == "nue" => { 430 | attr::mark_used(v); 431 | 432 | Some(attrs) 433 | }, 434 | _ => None, 435 | }); 436 | 437 | 438 | let mut attrs = Vec::new(); 439 | for attr in attr { 440 | for attr in attr.iter() { 441 | match &attr.node { 442 | &MetaItem_::MetaNameValue(ref name, Spanned { node: Lit_::LitStr(ref value, _), .. } ) => match &**name { 443 | "assert" => attrs.push(FieldAttribute::Assert(attr_expr(cx, replace_self, &value))), 444 | "align" => attrs.push(FieldAttribute::Align(attr_expr(cx, replace_self, &value))), 445 | "skip" => attrs.push(FieldAttribute::Skip(attr_expr(cx, replace_self, &value))), 446 | "limit" => attrs.push(FieldAttribute::Limit(attr_expr(cx, replace_self, &value))), 447 | "cond" => attrs.push(FieldAttribute::Cond(attr_expr(cx, replace_self, &value))), 448 | "default" => attrs.push(FieldAttribute::Default(attr_expr(cx, replace_self, &value))), 449 | "consume" => attrs.push(FieldAttribute::Consume(attr_expr(cx, replace_self, &value))), 450 | _ => { 451 | cx.span_err(attr.span, "invalid attribute key"); 452 | break 453 | }, 454 | }, 455 | _ => { 456 | cx.span_err(attr.span, "invalid attribute"); 457 | break 458 | }, 459 | } 460 | } 461 | } 462 | attrs 463 | } 464 | 465 | enum FieldAttribute { 466 | Cond(P), 467 | Default(P), 468 | Align(P), 469 | Limit(P), 470 | Skip(P), 471 | Consume(P), 472 | Assert(P), 473 | } 474 | -------------------------------------------------------------------------------- /codegen/src/syntex.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "with-syntex"), feature(rustc_private, plugin))] 2 | #![cfg_attr(not(feature = "with-syntex"), plugin(quasi_macros))] 3 | #![deny(missing_docs)] 4 | 5 | //! nue derive syntax extension. 6 | //! 7 | //! Provides the `#[derive(PodPacked, Pod, NueEncode, NueDecode)]` extensions documented in `nue-macros`. 8 | //! 9 | //! ## Stable 10 | //! 11 | //! See the [syntex documentation](https://github.com/erickt/rust-syntex/blob/master/README.md) 12 | //! for instructions on how to set up your project to use these macros in stable Rust. 13 | //! 14 | //! ## Nightly / Unstable 15 | //! 16 | //! See the example in `nue-macros` for usage as a normal syntax extension. 17 | 18 | extern crate aster; 19 | extern crate quasi; 20 | #[cfg(feature = "with-syntex")] 21 | extern crate syntex; 22 | #[cfg(feature = "with-syntex")] 23 | extern crate syntex_syntax as syntax; 24 | #[cfg(not(feature = "with-syntex"))] 25 | extern crate syntax; 26 | #[cfg(not(feature = "with-syntex"))] 27 | extern crate rustc; 28 | 29 | #[cfg(feature = "with-syntex")] 30 | include!(concat!(env!("OUT_DIR"), "/lib.rs")); 31 | 32 | #[cfg(not(feature = "with-syntex"))] 33 | include!("lib.rs"); 34 | 35 | /// Registers the plugin for expansion with syntex. 36 | #[cfg(feature = "with-syntex")] 37 | pub fn register(reg: &mut syntex::Registry) { 38 | use syntax::{ast, fold}; 39 | 40 | reg.add_attr("feature(custom_derive)"); 41 | reg.add_attr("feature(custom_attribute)"); 42 | 43 | reg.add_modifier("packed", expand_packed); 44 | reg.add_modifier("derive_PodPacked", expand_derive_pod_packed); 45 | reg.add_decorator("derive_Packed", expand_derive_packed); 46 | reg.add_decorator("derive_Pod", expand_derive_pod); 47 | reg.add_decorator("derive_NueEncode", expand_derive_encode); 48 | reg.add_decorator("derive_NueDecode", expand_derive_decode); 49 | 50 | reg.add_post_expansion_pass(strip_attributes); 51 | 52 | #[cfg(feature = "with-syntex")] 53 | fn strip_attributes(krate: ast::Crate) -> ast::Crate { 54 | struct StripAttributeFolder; 55 | 56 | impl fold::Folder for StripAttributeFolder { 57 | fn fold_attribute(&mut self, attr: ast::Attribute) -> Option { 58 | match attr.node.value.node { 59 | ast::MetaWord(ref n) if *n == "__nue_packed" => { return None; }, 60 | ast::MetaList(ref n, _) if *n == "nue" || *n == "nue_enc" || *n == "nue_dec" => { return None; }, 61 | _ => {} 62 | } 63 | 64 | Some(attr) 65 | } 66 | 67 | fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac { 68 | fold::noop_fold_mac(mac, self) 69 | } 70 | } 71 | 72 | fold::Folder::fold_crate(&mut StripAttributeFolder, krate) 73 | } 74 | } 75 | 76 | #[doc(hidden)] 77 | #[cfg(not(feature = "with-syntex"))] 78 | pub fn register(reg: &mut rustc::plugin::Registry) { 79 | reg.register_syntax_extension( 80 | syntax::parse::token::intern("packed"), 81 | syntax::ext::base::MultiModifier( 82 | Box::new(expand_packed) 83 | ) 84 | ); 85 | 86 | reg.register_syntax_extension( 87 | syntax::parse::token::intern("derive_Packed"), 88 | syntax::ext::base::MultiDecorator( 89 | Box::new(expand_derive_packed) 90 | ) 91 | ); 92 | 93 | reg.register_syntax_extension( 94 | syntax::parse::token::intern("derive_PodPacked"), 95 | syntax::ext::base::MultiModifier( 96 | Box::new(expand_derive_pod_packed) 97 | ) 98 | ); 99 | 100 | reg.register_syntax_extension( 101 | syntax::parse::token::intern("derive_Pod"), 102 | syntax::ext::base::MultiDecorator( 103 | Box::new(expand_derive_pod) 104 | ) 105 | ); 106 | 107 | reg.register_syntax_extension( 108 | syntax::parse::token::intern("derive_NueEncode"), 109 | syntax::ext::base::MultiDecorator( 110 | Box::new(expand_derive_encode) 111 | ) 112 | ); 113 | 114 | reg.register_syntax_extension( 115 | syntax::parse::token::intern("derive_NueDecode"), 116 | syntax::ext::base::MultiDecorator( 117 | Box::new(expand_derive_decode) 118 | ) 119 | ); 120 | } 121 | -------------------------------------------------------------------------------- /io/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nue-io" 3 | version = "0.3.0" 4 | authors = ["arcnmx"] 5 | 6 | description = "Binary data encoding and I/O" 7 | documentation = "http://arcnmx.github.io/nue/nue_io/" 8 | repository = "https://github.com/arcnmx/nue" 9 | readme = "../README.md" 10 | keywords = ["binary", "data", "encoding", "decoding", "serialization"] 11 | license = "MIT" 12 | 13 | [dependencies] 14 | byteorder = "0.3" 15 | resize-slice = "0.1" 16 | 17 | [dependencies.uninitialized] 18 | version = "0.0" 19 | path = "../uninitialized" 20 | -------------------------------------------------------------------------------- /io/src/align.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use seek_forward::{Tell, SeekForward}; 3 | 4 | /// An extension trait that will seek to meet a specified alignment. 5 | pub trait SeekAlignExt { 6 | /// Seeks forward to a multiple of `alignment`. 7 | /// 8 | /// Returns the resulting offset in the stream upon success. 9 | fn align_to(&mut self, alignment: u64) -> io::Result; 10 | } 11 | 12 | impl SeekAlignExt for T { 13 | fn align_to(&mut self, alignment: u64) -> io::Result { 14 | let pos = try!(self.tell()); 15 | let pos_alignment = pos % alignment; 16 | if pos_alignment > 0 { 17 | self.seek_forward(alignment - pos_alignment).map(|v| v + pos) 18 | } else { 19 | Ok(pos) 20 | } 21 | } 22 | } 23 | 24 | #[test] 25 | fn align() { 26 | use std::io::Cursor; 27 | use seek_forward::{SeekAll, SeekAbsolute}; 28 | 29 | let data = [0; 0x80]; 30 | let mut cursor = SeekAll::new(Cursor::new(&data[..])); 31 | 32 | cursor.seek_absolute(0x25).unwrap(); 33 | 34 | cursor.align_to(0x10).unwrap(); 35 | assert_eq!(cursor.tell().unwrap(), 0x30); 36 | 37 | cursor.align_to(0x20).unwrap(); 38 | assert_eq!(cursor.tell().unwrap(), 0x40); 39 | 40 | cursor.align_to(0x20).unwrap(); 41 | assert_eq!(cursor.tell().unwrap(), 0x40); 42 | } 43 | -------------------------------------------------------------------------------- /io/src/buf_seeker.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, Read, BufRead}; 2 | use std::cmp::min; 3 | use resize_slice::SliceExt; 4 | use seek_forward::{SeekForward, SeekBackward, SeekRewind, SeekAbsolute, SeekEnd, Tell}; 5 | 6 | const DEFAULT_BUF_SIZE: usize = 0x400 * 0x40; 7 | 8 | /// A buffered reader that allows for seeking within the buffer. 9 | /// 10 | /// Unlike `std::io::BufReader`, seeking doesn't invalidate the buffer. 11 | pub struct BufSeeker { 12 | inner: T, 13 | buf: Vec, 14 | pos: usize, 15 | } 16 | 17 | impl BufSeeker { 18 | /// Creates a new `BufSeeker` around the specified `Read`. 19 | pub fn new(inner: T) -> Self { 20 | BufSeeker::with_capacity(DEFAULT_BUF_SIZE, inner) 21 | } 22 | 23 | /// Creates a `BufSeeker` with a specific buffer size. 24 | pub fn with_capacity(cap: usize, inner: T) -> Self { 25 | BufSeeker { 26 | inner: inner, 27 | buf: Vec::with_capacity(cap), 28 | pos: 0, 29 | } 30 | } 31 | 32 | /// Unwraps the `BufSeeker`, returning the underlying reader. 33 | /// 34 | /// Note that any leftover data in the buffer will be lost. 35 | pub fn into_inner(self) -> T { 36 | self.inner 37 | } 38 | } 39 | 40 | impl SeekForward for BufSeeker { 41 | #[inline] 42 | fn seek_forward(&mut self, offset: u64) -> io::Result { 43 | let pos = (self.buf.len() - self.pos) as u64; 44 | if offset <= pos as u64 { 45 | self.pos += offset as usize; 46 | Ok(offset) 47 | } else { 48 | let offset = offset - pos; 49 | let res = try!(self.inner.seek_forward(offset)); 50 | self.pos = self.buf.len(); 51 | Ok(res + pos) 52 | } 53 | } 54 | } 55 | 56 | impl SeekBackward for BufSeeker { 57 | #[inline] 58 | fn seek_backward(&mut self, offset: u64) -> io::Result { 59 | let pos = self.pos as u64; 60 | if offset <= pos { 61 | self.pos -= offset as usize; 62 | Ok(offset) 63 | } else { 64 | let offset = offset + pos; 65 | let res = try!(self.inner.seek_backward(offset)); 66 | self.pos = self.buf.len(); 67 | Ok(res - pos) 68 | } 69 | } 70 | } 71 | 72 | impl SeekRewind for BufSeeker { 73 | #[inline] 74 | fn seek_rewind(&mut self) -> io::Result<()> { 75 | try!(self.inner.seek_rewind()); 76 | self.pos = self.buf.len(); 77 | Ok(()) 78 | } 79 | } 80 | 81 | impl Tell for BufSeeker { 82 | #[inline] 83 | fn tell(&mut self) -> io::Result { 84 | self.inner.tell().map(|v| v - (self.buf.len() - self.pos) as u64) 85 | } 86 | } 87 | 88 | impl SeekAbsolute for BufSeeker { 89 | #[inline] 90 | fn seek_absolute(&mut self, pos: u64) -> io::Result { 91 | let pos = try!(self.inner.seek_absolute(pos)); 92 | self.pos = self.buf.len(); 93 | Ok(pos) 94 | } 95 | } 96 | 97 | impl SeekEnd for BufSeeker { 98 | #[inline] 99 | fn seek_end(&mut self, offset: i64) -> io::Result { 100 | let pos = try!(self.inner.seek_end(offset)); 101 | self.pos = self.buf.len(); 102 | Ok(pos) 103 | } 104 | } 105 | 106 | impl Read for BufSeeker { 107 | #[inline] 108 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 109 | if self.pos >= self.buf.len() && buf.len() >= self.buf.capacity() { 110 | self.inner.read(buf) 111 | } else { 112 | let read = buf.copy_from(try!(self.fill_buf())); 113 | self.consume(read); 114 | Ok(read) 115 | } 116 | } 117 | } 118 | 119 | impl BufRead for BufSeeker { 120 | #[inline] 121 | fn fill_buf(&mut self) -> io::Result<&[u8]> { 122 | use std::slice::from_raw_parts_mut; 123 | 124 | if self.pos >= self.buf.len() { 125 | unsafe { 126 | let buf = from_raw_parts_mut(self.buf.as_mut_ptr(), self.buf.capacity()); 127 | self.buf.set_len(try!(self.inner.read(buf))); 128 | self.pos = 0; 129 | } 130 | } 131 | Ok(&self.buf[self.pos..]) 132 | } 133 | 134 | #[inline] 135 | fn consume(&mut self, amt: usize) { 136 | self.pos = min(self.pos + amt, self.buf.len()); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /io/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(missing_docs)] 2 | 3 | //! Utilities for working with I/O streams. 4 | 5 | extern crate byteorder; 6 | extern crate uninitialized; 7 | extern crate resize_slice; 8 | 9 | /// An extension for `Read`ing an exact amount of data. 10 | pub mod read_exact; 11 | 12 | mod seek_forward; 13 | 14 | mod buf_seeker; 15 | mod region; 16 | mod align; 17 | mod take; 18 | 19 | pub use seek_forward::{ 20 | SeekRewind, SeekForward, SeekBackward, SeekAbsolute, SeekEnd, Tell, 21 | ReadWriteTell, SeekForwardRead, SeekForwardWrite, SeekAbsoluteRewind, SeekAll 22 | }; 23 | pub use read_exact::ReadExactExt; 24 | pub use buf_seeker::BufSeeker; 25 | pub use region::Region; 26 | pub use align::SeekAlignExt; 27 | pub use take::Take; 28 | -------------------------------------------------------------------------------- /io/src/read_exact.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, Read}; 2 | 3 | pub use byteorder::Error; 4 | 5 | /// Extension trait that provides `read_exact` for all `Read` implementations. 6 | pub trait ReadExactExt { 7 | /// Reads into the entirety of `buf` or fails with an error. 8 | /// 9 | /// The `Read` equivalent of `Write::write_all`. 10 | /// Retries upon `Interrupted` errors. 11 | fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Error>; 12 | 13 | /// Reads as much as possible into `buf` until EOF. 14 | /// 15 | /// Retries upon `Interrupted` errors. 16 | fn read_exact_eof(&mut self, buf: &mut [u8]) -> io::Result; 17 | } 18 | 19 | impl ReadExactExt for R { 20 | #[inline] 21 | fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Error> { 22 | match self.read_exact_eof(buf) { 23 | Ok(len) if len == buf.len() => Ok(()), 24 | Ok(_) => Err(Error::UnexpectedEOF), 25 | Err(e) => Err(e.into()), 26 | } 27 | } 28 | 29 | #[inline] 30 | fn read_exact_eof(&mut self, buf: &mut [u8]) -> io::Result { 31 | let mut n = 0; 32 | while n < buf.len() { 33 | match self.read(&mut buf[n..]) { 34 | Err(ref e) if e.kind() == io::ErrorKind::Interrupted => (), 35 | Err(e) => return Err(e), 36 | Ok(0) => break, 37 | Ok(len) => n += len, 38 | } 39 | } 40 | 41 | Ok(n) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /io/src/region.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, Read, Write, BufRead}; 2 | use std::cmp::min; 3 | use seek_forward::{SeekForward, SeekAbsolute, Tell, SeekRewind, SeekEnd, SeekBackward}; 4 | 5 | /// Creates an isolated segment of an underlying stream. 6 | /// 7 | /// Seeks past the region will be capped, and reaching the end of 8 | /// the region will result in EOF when reading or writing. 9 | pub struct Region { 10 | inner: T, 11 | start: u64, 12 | end: u64, 13 | } 14 | 15 | impl Region { 16 | /// Creates a new `Region` at the specified offsets of `inner`. 17 | pub fn new(inner: T, start: u64, end: u64) -> Self { 18 | Region { 19 | inner: inner, 20 | start: start, 21 | end: end, 22 | } 23 | } 24 | 25 | /// Returns the region bounds. 26 | pub fn region(&self) -> (u64, u64) { 27 | (self.start, self.end) 28 | } 29 | 30 | /// Unwraps the `Region` to return the inner stream. 31 | pub fn into_inner(self) -> T { 32 | self.inner 33 | } 34 | } 35 | 36 | impl Region { 37 | fn position(&mut self) -> io::Result { 38 | let pos = try!(self.inner.tell()); 39 | 40 | if pos < self.start { 41 | self.inner.seek_absolute(self.start) 42 | } else { 43 | Ok(pos) 44 | } 45 | } 46 | 47 | fn limit(&mut self, len: u64) -> io::Result { 48 | let pos = try!(self.position()); 49 | let end = self.end; 50 | 51 | Ok(limit(pos, end, len)) 52 | } 53 | } 54 | 55 | fn limit(pos: u64, end: u64, len: u64) -> u64 { 56 | let limit = if pos >= end { 57 | 0 58 | } else { 59 | end - pos 60 | }; 61 | 62 | min(limit, len) 63 | } 64 | 65 | impl Read for Region { 66 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 67 | let len = try!(self.limit(buf.len() as u64)) as usize; 68 | 69 | if len == 0 { 70 | Ok(0) 71 | } else { 72 | let read = try!(self.inner.read(&mut buf[..len])); 73 | Ok(read) 74 | } 75 | } 76 | } 77 | 78 | impl Write for Region { 79 | fn write(&mut self, buf: &[u8]) -> io::Result { 80 | let len = try!(self.limit(buf.len() as u64)) as usize; 81 | 82 | if len == 0 { 83 | Ok(0) 84 | } else { 85 | self.inner.write(&buf[..len]) 86 | } 87 | } 88 | 89 | fn flush(&mut self) -> io::Result<()> { 90 | self.inner.flush() 91 | } 92 | } 93 | 94 | impl SeekAbsolute for Region { 95 | fn seek_absolute(&mut self, pos: u64) -> io::Result { 96 | self.inner.seek_absolute(min(self.start.saturating_add(pos), self.end)).map(|v| v - self.start) 97 | } 98 | } 99 | 100 | impl SeekForward for Region { 101 | fn seek_forward(&mut self, offset: u64) -> io::Result { 102 | let offset = try!(self.limit(offset)); 103 | self.inner.seek_forward(offset) 104 | } 105 | } 106 | 107 | impl SeekBackward for Region { 108 | fn seek_backward(&mut self, offset: u64) -> io::Result { 109 | let off = try!(self.inner.tell()).saturating_sub(self.start);; 110 | let offset = min(off, offset); 111 | self.inner.seek_backward(offset) 112 | } 113 | } 114 | 115 | impl SeekRewind for Region { 116 | fn seek_rewind(&mut self) -> io::Result<()> { 117 | self.inner.seek_absolute(self.start).map(|_| ()) 118 | } 119 | } 120 | 121 | impl SeekEnd for Region { 122 | fn seek_end(&mut self, offset: i64) -> io::Result { 123 | self.inner.seek_absolute((self.end as i64 + offset) as u64).map(|v| v - self.start) 124 | } 125 | } 126 | 127 | impl Tell for Region { 128 | fn tell(&mut self) -> io::Result { 129 | self.inner.tell().map(|v| min(self.end, v.saturating_sub(self.start))) 130 | } 131 | } 132 | 133 | impl BufRead for Region { 134 | fn fill_buf(&mut self) -> io::Result<&[u8]> { 135 | let pos = try!(self.position()); 136 | 137 | let buf = try!(self.inner.fill_buf()); 138 | let len = limit(pos, self.end, buf.len() as u64) as usize; 139 | 140 | Ok(&buf[..len]) 141 | } 142 | 143 | fn consume(&mut self, amt: usize) { 144 | self.inner.consume(amt); 145 | } 146 | } 147 | 148 | #[cfg(test)] 149 | mod tests { 150 | use std::io::{Cursor, Read}; 151 | use super::Region; 152 | use read_exact::ReadExactExt; 153 | use seek_forward::{SeekAll, SeekAbsolute}; 154 | 155 | fn data(count: usize) -> Vec { 156 | use std::iter::{repeat}; 157 | repeat(0).enumerate().map(|(i, _)| i as u8).take(count).collect() 158 | } 159 | 160 | #[test] 161 | fn region() { 162 | let data = data(0x100); 163 | let cursor = SeekAll::new(Cursor::new(data.clone())); 164 | 165 | let mut region = Region::new(cursor, 0x40, 0x80); 166 | let mut odata = vec![0u8; 0x40]; 167 | region.read_exact(&mut odata).unwrap(); 168 | assert_eq!(&odata[..], &data[0x40..0x80]); 169 | 170 | region.seek_absolute(0x20).unwrap(); 171 | region.read_exact(&mut odata[..0x20]).unwrap(); 172 | assert_eq!(&odata[..0x20], &data[0x60..0x80]); 173 | 174 | region.seek_absolute(0).unwrap(); 175 | region.read_exact(&mut odata[..]).unwrap(); 176 | assert_eq!(&odata[..], &data[0x40..0x80]); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /io/src/seek_forward.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, Read, Write, BufRead, Seek, SeekFrom, copy, sink, repeat}; 2 | 3 | /// A limited form of seeking that can only be reset from the beginning. 4 | /// 5 | /// Useful for expressing compressed or cipher streams. 6 | pub trait SeekRewind { 7 | /// Seeks back to the beginning of the stream. 8 | /// 9 | /// Conceptually equivalent to `seek(SeekFrom::Start(0))`. 10 | fn seek_rewind(&mut self) -> io::Result<()>; 11 | } 12 | 13 | /// A limited form of seeking that only allows seeking forward. 14 | pub trait SeekForward { 15 | /// Seeks forward in the stream. 16 | /// 17 | /// Returns the number of bytes skipped. 18 | fn seek_forward(&mut self, offset: u64) -> io::Result; 19 | } 20 | 21 | /// A limited form of seeking that only allows seeking backward. 22 | pub trait SeekBackward { 23 | /// Seeks backward in the stream. 24 | /// 25 | /// Returns the number of bytes reversed by. 26 | fn seek_backward(&mut self, offset: u64) -> io::Result; 27 | } 28 | 29 | /// A limited form of seeking that only seeks to an offset from the end of the stream. 30 | pub trait SeekEnd { 31 | /// Seeks to the end of the stream + `offset`. 32 | /// 33 | /// Returns the new position in the stream. 34 | fn seek_end(&mut self, offset: i64) -> io::Result; 35 | } 36 | 37 | /// A limited form of seeking that only seeks to an absolute position from the start. 38 | pub trait SeekAbsolute { 39 | /// Seeks to the specified position in the stream. 40 | /// 41 | /// Returns the new position in the stream. 42 | fn seek_absolute(&mut self, pos: u64) -> io::Result; 43 | } 44 | 45 | /// Exposes the current position in the stream without changing it. 46 | pub trait Tell { 47 | /// Returns the current absolute position in the stream. 48 | fn tell(&mut self) -> io::Result; 49 | } 50 | 51 | impl<'a, T: Tell + ?Sized> Tell for &'a mut T { 52 | #[inline] 53 | fn tell(&mut self) -> io::Result { 54 | (**self).tell() 55 | } 56 | } 57 | 58 | impl<'a, T: SeekForward + ?Sized> SeekForward for &'a mut T { 59 | #[inline] 60 | fn seek_forward(&mut self, offset: u64) -> io::Result { 61 | (**self).seek_forward(offset) 62 | } 63 | } 64 | 65 | impl<'a, T: SeekAbsolute + ?Sized> SeekAbsolute for &'a mut T { 66 | #[inline] 67 | fn seek_absolute(&mut self, pos: u64) -> io::Result { 68 | (**self).seek_absolute(pos) 69 | } 70 | } 71 | 72 | impl<'a, T: SeekRewind + ?Sized> SeekRewind for &'a mut T { 73 | #[inline] 74 | fn seek_rewind(&mut self) -> io::Result<()> { 75 | (**self).seek_rewind() 76 | } 77 | } 78 | 79 | impl<'a, T: SeekBackward + ?Sized> SeekBackward for &'a mut T { 80 | #[inline] 81 | fn seek_backward(&mut self, offset: u64) -> io::Result { 82 | (**self).seek_backward(offset) 83 | } 84 | } 85 | 86 | impl<'a, T: SeekEnd + ?Sized> SeekEnd for &'a mut T { 87 | #[inline] 88 | fn seek_end(&mut self, offset: i64) -> io::Result { 89 | (**self).seek_end(offset) 90 | } 91 | } 92 | 93 | impl Tell for Box { 94 | #[inline] 95 | fn tell(&mut self) -> io::Result { 96 | (**self).tell() 97 | } 98 | } 99 | 100 | impl SeekForward for Box { 101 | #[inline] 102 | fn seek_forward(&mut self, offset: u64) -> io::Result { 103 | (**self).seek_forward(offset) 104 | } 105 | } 106 | 107 | impl SeekAbsolute for Box { 108 | #[inline] 109 | fn seek_absolute(&mut self, pos: u64) -> io::Result { 110 | (**self).seek_absolute(pos) 111 | } 112 | } 113 | 114 | impl SeekRewind for Box { 115 | #[inline] 116 | fn seek_rewind(&mut self) -> io::Result<()> { 117 | (**self).seek_rewind() 118 | } 119 | } 120 | 121 | impl SeekBackward for Box { 122 | #[inline] 123 | fn seek_backward(&mut self, offset: u64) -> io::Result { 124 | (**self).seek_backward(offset) 125 | } 126 | } 127 | 128 | impl SeekEnd for Box { 129 | #[inline] 130 | fn seek_end(&mut self, offset: i64) -> io::Result { 131 | (**self).seek_end(offset) 132 | } 133 | } 134 | 135 | /// A forward seeking wrapper around a `Read` type. 136 | pub struct SeekForwardRead { 137 | inner: T, 138 | } 139 | 140 | /// A forward seeking wrapper around a `Write` type. 141 | pub struct SeekForwardWrite { 142 | inner: T, 143 | } 144 | 145 | /// An absolute seeking wrapper around a `Tell + SeekForward + SeekRewind` type. 146 | pub struct SeekAbsoluteRewind { 147 | inner: T, 148 | } 149 | 150 | /// A wrapper that decomposes `Seek` into its individual traits. 151 | pub struct SeekAll { 152 | inner: T, 153 | } 154 | 155 | /// A wrapper that implements `Tell` for streams that don't support it. 156 | pub struct ReadWriteTell { 157 | inner: T, 158 | pos: u64, 159 | } 160 | 161 | impl SeekForward for SeekForwardRead { 162 | fn seek_forward(&mut self, offset: u64) -> io::Result { 163 | if offset == 0 { 164 | Ok(0) 165 | } else { 166 | copy(&mut self.inner.by_ref().take(offset), &mut sink()) 167 | } 168 | } 169 | } 170 | 171 | impl SeekForward for SeekForwardWrite { 172 | fn seek_forward(&mut self, offset: u64) -> io::Result { 173 | if offset == 0 { 174 | Ok(0) 175 | } else { 176 | copy(&mut repeat(0).take(offset), self) 177 | } 178 | } 179 | } 180 | 181 | impl SeekAbsolute for SeekAbsoluteRewind { 182 | fn seek_absolute(&mut self, pos: u64) -> io::Result { 183 | if pos == 0 { 184 | return self.inner.seek_rewind().map(|_| 0); 185 | } 186 | 187 | let tell = try!(self.inner.tell()); 188 | let tell = if tell > pos { 189 | try!(self.inner.seek_rewind().map(|_| 0)) 190 | } else { 191 | tell 192 | }; 193 | 194 | let diff = pos - tell; 195 | self.inner.seek_forward(diff).map(|v| tell + v) 196 | } 197 | } 198 | 199 | impl Tell for ReadWriteTell { 200 | #[inline] 201 | fn tell(&mut self) -> io::Result { 202 | Ok(self.pos) 203 | } 204 | } 205 | 206 | impl Read for ReadWriteTell { 207 | #[inline] 208 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 209 | let read = try!(self.inner.read(buf)); 210 | self.pos += read as u64; 211 | Ok(read) 212 | } 213 | } 214 | 215 | impl BufRead for ReadWriteTell { 216 | #[inline] 217 | fn fill_buf(&mut self) -> io::Result<&[u8]> { 218 | self.inner.fill_buf() 219 | } 220 | 221 | #[inline] 222 | fn consume(&mut self, amt: usize) { 223 | self.inner.consume(amt); 224 | self.pos += amt as u64; 225 | } 226 | 227 | #[inline] 228 | fn read_until(&mut self, byte: u8, buf: &mut Vec) -> io::Result { 229 | let read = try!(self.inner.read_until(byte, buf)); 230 | self.pos += read as u64; 231 | Ok(read) 232 | } 233 | 234 | #[inline] 235 | fn read_line(&mut self, buf: &mut String) -> io::Result { 236 | let read = try!(self.inner.read_line(buf)); 237 | self.pos += read as u64; 238 | Ok(read) 239 | } 240 | } 241 | 242 | impl Write for ReadWriteTell { 243 | #[inline] 244 | fn write(&mut self, buf: &[u8]) -> io::Result { 245 | let write = try!(self.inner.write(buf)); 246 | self.pos += write as u64; 247 | Ok(write) 248 | } 249 | 250 | #[inline] 251 | fn flush(&mut self) -> io::Result<()> { 252 | self.inner.flush() 253 | } 254 | } 255 | 256 | impl SeekForward for ReadWriteTell { 257 | #[inline] 258 | fn seek_forward(&mut self, offset: u64) -> io::Result { 259 | let offset = try!(self.inner.seek_forward(offset)); 260 | self.pos += offset; 261 | Ok(offset) 262 | } 263 | } 264 | 265 | impl SeekRewind for ReadWriteTell { 266 | #[inline] 267 | fn seek_rewind(&mut self) -> io::Result<()> { 268 | try!(self.inner.seek_rewind()); 269 | self.pos = 0; 270 | Ok(()) 271 | } 272 | } 273 | 274 | impl SeekAbsolute for ReadWriteTell { 275 | #[inline] 276 | fn seek_absolute(&mut self, pos: u64) -> io::Result { 277 | let pos = try!(self.inner.seek_absolute(pos)); 278 | self.pos = pos; 279 | Ok(pos) 280 | } 281 | } 282 | 283 | /*impl Seek for T { 284 | fn seek(&mut self, pos: SeekFrom) -> io::Result { 285 | match pos { 286 | SeekFrom::Start(pos) => self.inner.seek_absolute(pos), 287 | SeekFrom::Current(offset) if offset > 0 => 288 | self.inner.seek_forward(offset as u64).and_then(|_| self.inner.tell()), 289 | SeekFrom::Current(offset) if offset == 0 => self.inner.tell(), 290 | SeekFrom::Current(offset) => self.inner.seek_backward(-offset as u64).and_then(|_| self.inner.tell()), 291 | SeekFrom::End(offset) => self.seek_end(offset), 292 | } 293 | } 294 | }*/ 295 | 296 | /*impl SeekBackward for _ { 297 | #[inline] 298 | fn seek_backward(&mut self, offset: u64) -> io::Result { 299 | let pos = try!(self.inner.tell()).saturating_sub(-offset as u64); 300 | self.inner.seek_absolute(pos) 301 | } 302 | }*/ 303 | 304 | impl Tell for SeekAll { 305 | #[inline] 306 | fn tell(&mut self) -> io::Result { 307 | self.inner.seek(SeekFrom::Current(0)) 308 | } 309 | } 310 | 311 | impl SeekForward for SeekAll { 312 | #[inline] 313 | fn seek_forward(&mut self, offset: u64) -> io::Result { 314 | self.inner.seek(SeekFrom::Current(offset as i64)).map(|_| offset) 315 | } 316 | } 317 | 318 | impl SeekAbsolute for SeekAll { 319 | #[inline] 320 | fn seek_absolute(&mut self, pos: u64) -> io::Result { 321 | self.inner.seek(SeekFrom::Start(pos)) 322 | } 323 | } 324 | 325 | impl SeekRewind for SeekAll { 326 | #[inline] 327 | fn seek_rewind(&mut self) -> io::Result<()> { 328 | self.inner.seek(SeekFrom::Start(0)).map(|_| ()) 329 | } 330 | } 331 | 332 | impl SeekBackward for SeekAll { 333 | #[inline] 334 | fn seek_backward(&mut self, offset: u64) -> io::Result { 335 | self.inner.seek(SeekFrom::Current(-(offset as i64))) 336 | } 337 | } 338 | 339 | impl SeekEnd for SeekAll { 340 | #[inline] 341 | fn seek_end(&mut self, offset: i64) -> io::Result { 342 | self.inner.seek(SeekFrom::End(offset)) 343 | } 344 | } 345 | 346 | macro_rules! impl_seek { 347 | ($t:ident => SeekRewind) => { 348 | impl SeekRewind for $t { 349 | #[inline] 350 | fn seek_rewind(&mut self) -> io::Result<()> { 351 | self.inner.seek_rewind() 352 | } 353 | } 354 | }; 355 | ($t:ident => SeekForward) => { 356 | impl SeekForward for $t { 357 | #[inline] 358 | fn seek_forward(&mut self, offset: u64) -> io::Result { 359 | self.inner.seek_forward(offset) 360 | } 361 | } 362 | }; 363 | ($t:ident => SeekBackward) => { 364 | impl SeekBackward for $t { 365 | #[inline] 366 | fn seek_backward(&mut self, offset: u64) -> io::Result { 367 | self.inner.seek_backward(offset) 368 | } 369 | } 370 | }; 371 | ($t:ident => SeekAbsolute) => { 372 | impl SeekAbsolute for $t { 373 | #[inline] 374 | fn seek_absolute(&mut self, pos: u64) -> io::Result { 375 | self.inner.seek_absolute(pos) 376 | } 377 | } 378 | }; 379 | ($t:ident => SeekEnd) => { 380 | impl SeekEnd for $t { 381 | #[inline] 382 | fn seek_end(&mut self, offset: i64) -> io::Result { 383 | self.inner.seek_end(offset) 384 | } 385 | } 386 | }; 387 | ($t:ident => Tell) => { 388 | impl Tell for $t { 389 | #[inline] 390 | fn tell(&mut self) -> io::Result { 391 | self.inner.tell() 392 | } 393 | } 394 | }; 395 | ($t:ident => Read) => { 396 | impl Read for $t { 397 | #[inline] 398 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 399 | self.inner.read(buf) 400 | } 401 | } 402 | }; 403 | ($t:ident => Write) => { 404 | impl Write for $t { 405 | #[inline] 406 | fn write(&mut self, buf: &[u8]) -> io::Result { 407 | self.inner.write(buf) 408 | } 409 | 410 | #[inline] 411 | fn flush(&mut self) -> io::Result<()> { 412 | self.inner.flush() 413 | } 414 | } 415 | }; 416 | ($t:ident => BufRead) => { 417 | impl BufRead for $t { 418 | #[inline] 419 | fn fill_buf(&mut self) -> io::Result<&[u8]> { 420 | self.inner.fill_buf() 421 | } 422 | 423 | #[inline] 424 | fn consume(&mut self, amt: usize) { 425 | self.inner.consume(amt) 426 | } 427 | } 428 | }; 429 | } 430 | 431 | impl_seek!(SeekForwardRead => SeekRewind); 432 | impl_seek!(SeekForwardRead => Tell); 433 | impl_seek!(SeekForwardRead => SeekAbsolute); 434 | impl_seek!(SeekForwardRead => SeekEnd); 435 | impl_seek!(SeekForwardRead => SeekBackward); 436 | impl_seek!(SeekForwardRead => BufRead); 437 | impl_seek!(SeekForwardRead => Read); 438 | 439 | impl_seek!(SeekForwardWrite => SeekRewind); 440 | impl_seek!(SeekForwardWrite => Tell); 441 | impl_seek!(SeekForwardWrite => SeekAbsolute); 442 | impl_seek!(SeekForwardWrite => SeekEnd); 443 | impl_seek!(SeekForwardWrite => SeekBackward); 444 | impl_seek!(SeekForwardWrite => Write); 445 | 446 | impl_seek!(SeekAbsoluteRewind => SeekRewind); 447 | impl_seek!(SeekAbsoluteRewind => Tell); 448 | impl_seek!(SeekAbsoluteRewind => SeekForward); 449 | impl_seek!(SeekAbsoluteRewind => SeekEnd); 450 | impl_seek!(SeekAbsoluteRewind => SeekBackward); 451 | impl_seek!(SeekAbsoluteRewind => Read); 452 | impl_seek!(SeekAbsoluteRewind => Write); 453 | impl_seek!(SeekAbsoluteRewind => BufRead); 454 | 455 | impl_seek!(SeekAll => BufRead); 456 | impl_seek!(SeekAll => Read); 457 | impl_seek!(SeekAll => Write); 458 | 459 | impl SeekForwardRead { 460 | /// Creates a new `SeekForwardRead`. 461 | pub fn new(inner: T) -> Self { 462 | SeekForwardRead { 463 | inner: inner, 464 | } 465 | } 466 | } 467 | 468 | impl SeekForwardWrite { 469 | /// Creates a new `SeekForwardWrite`. 470 | pub fn new(inner: T) -> Self { 471 | SeekForwardWrite { 472 | inner: inner, 473 | } 474 | } 475 | } 476 | 477 | impl SeekAbsoluteRewind { 478 | /// Creates a new `SeekAbsoluteRewind`. 479 | pub fn new(inner: T) -> Self { 480 | SeekAbsoluteRewind { 481 | inner: inner, 482 | } 483 | } 484 | } 485 | 486 | impl ReadWriteTell { 487 | /// Creates a new `ReadWriteTell`. 488 | pub fn new(inner: T) -> Self { 489 | ReadWriteTell { 490 | inner: inner, 491 | pos: 0, 492 | } 493 | } 494 | } 495 | 496 | impl SeekAll { 497 | /// Creates a new `SeekAll`. 498 | pub fn new(inner: T) -> Self { 499 | SeekAll { 500 | inner: inner, 501 | } 502 | } 503 | } 504 | -------------------------------------------------------------------------------- /io/src/take.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, Read, Write}; 2 | use std::cmp::min; 3 | use seek_forward::{SeekForward, Tell}; 4 | 5 | /// Wraps around a stream to limit the length of the underlying stream. 6 | /// 7 | /// This implementation differs from `std::io::Take` in that it also allows writes, 8 | /// and seeking forward is allowed if the underlying stream supports it. 9 | pub struct Take { 10 | inner: T, 11 | limit: u64, 12 | } 13 | 14 | impl Take { 15 | /// Creates a new `Take` with `limit` bytes 16 | pub fn new(inner: T, limit: u64) -> Self { 17 | Take { 18 | inner: inner, 19 | limit: limit, 20 | } 21 | } 22 | } 23 | 24 | impl Write for Take { 25 | fn write(&mut self, buf: &[u8]) -> io::Result { 26 | let limit = min(self.limit, buf.len() as u64); 27 | 28 | if limit == 0 { 29 | return Ok(0) 30 | } 31 | 32 | let buf = &buf[..limit as usize]; 33 | let inner = try!(self.inner.write(buf)); 34 | self.limit -= inner as u64; 35 | Ok(inner) 36 | } 37 | 38 | fn flush(&mut self) -> io::Result<()> { 39 | self.inner.flush() 40 | } 41 | } 42 | 43 | impl Read for Take { 44 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 45 | let limit = min(self.limit, buf.len() as u64); 46 | 47 | if limit == 0 { 48 | return Ok(0) 49 | } 50 | 51 | let buf = &mut buf[..limit as usize]; 52 | let inner = try!(self.inner.read(buf)); 53 | self.limit -= inner as u64; 54 | Ok(inner) 55 | } 56 | } 57 | 58 | impl SeekForward for Take { 59 | fn seek_forward(&mut self, offset: u64) -> io::Result { 60 | let res = try!(self.inner.seek_forward(min(offset, self.limit))); 61 | self.limit -= res; 62 | Ok(res) 63 | } 64 | } 65 | 66 | impl Tell for Take { 67 | fn tell(&mut self) -> io::Result { 68 | self.inner.tell() 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nue-macros" 3 | version = "0.3.0" 4 | authors = ["arcnmx"] 5 | 6 | description = "POD and binary data encoding I/O macros" 7 | documentation = "http://arcnmx.github.io/nue/nue_macros/" 8 | repository = "https://github.com/arcnmx/nue" 9 | readme = "../README.md" 10 | keywords = ["nue", "pod", "data", "encode", "endian"] 11 | license = "MIT" 12 | 13 | [lib] 14 | name = "nue_macros" 15 | plugin = true 16 | 17 | [dependencies.nue-codegen] 18 | version = "0.3" 19 | path = "../codegen" 20 | default-features = false 21 | features = ["unstable"] 22 | 23 | [dev-dependencies.nue] 24 | version = "0.3" 25 | features = ["unstable"] 26 | path = "../" 27 | 28 | [dev-dependencies.pod] 29 | version = "0.3" 30 | features = ["unstable"] 31 | path = "../pod" 32 | 33 | [[test]] 34 | name = "test" 35 | path = "tests/test.rs" 36 | -------------------------------------------------------------------------------- /macros/doctest/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | version = "0.0.0" 3 | name = "nue_macros_test" 4 | 5 | [lib] 6 | name = "nue_macros_test" 7 | path = "../src/lib.rs" 8 | 9 | [dependencies.nue-codegen] 10 | path = "../../codegen" 11 | default-features = false 12 | features = ["unstable"] 13 | 14 | [dev-dependencies.pod] 15 | features = ["unstable"] 16 | path = "../../pod" 17 | 18 | [dev-dependencies.nue] 19 | features = ["unstable"] 20 | path = "../../" 21 | 22 | [dev-dependencies.nue-macros] 23 | path = "../" 24 | -------------------------------------------------------------------------------- /macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(plugin_registrar, rustc_private)] 2 | 3 | //! `#[derive(..)]` attributes for POD and binary encodable types. 4 | //! 5 | //! # Attributes 6 | //! 7 | //! ## `#[packed]` 8 | //! 9 | //! Applies `#[repr(Packed)]` while also ensuring that all members safely allow 10 | //! unaligned access. 11 | //! 12 | //! ``` 13 | //! #![feature(plugin, custom_derive, custom_attribute)] 14 | //! #![plugin(nue_macros)] 15 | //! 16 | //! extern crate nue; 17 | //! use nue::{Pod, Aligned, Un}; 18 | //! 19 | //! # fn main() { 20 | //! #[packed] 21 | //! struct Data(u8, Un); 22 | //! 23 | //! let data = Data(5, 5.unaligned()); 24 | //! assert_eq!(data.0, 5u8); 25 | //! assert_eq!(u32::aligned(data.1), 5u32); 26 | //! # } 27 | //! ``` 28 | //! 29 | //! ## `#[derive(Pod)]` 30 | //! 31 | //! Marks a struct as `pod::Pod`. It must only contain other `Pod` members, and 32 | //! the type must be packed. 33 | //! 34 | //! ### `#[derive(PodPacked)]` 35 | //! 36 | //! Marks a struct as `pod::Pod`, and also applies the `#[packed]` 37 | //! attribute to the type. 38 | //! 39 | //! ### Example 40 | //! 41 | //! ``` 42 | //! #![feature(plugin, custom_derive, custom_attribute)] 43 | //! #![plugin(nue_macros)] 44 | //! 45 | //! extern crate pod; 46 | //! extern crate nue; 47 | //! use pod::Pod; 48 | //! 49 | //! # fn main() { 50 | //! #[derive(Pod)] 51 | //! #[packed] 52 | //! struct Data(u8); 53 | //! 54 | //! assert_eq!(Data(5).as_slice(), &[5]); 55 | //! # } 56 | //! ``` 57 | //! 58 | //! ## `#[derive(NueEncode, NueDecode)]` 59 | //! 60 | //! Implements `nue::Encode` and `nue::Decode` on the struct. 61 | //! All fields must also implement `Encode` / `Decode` (or be skipped by a `nue` attribute). 62 | //! 63 | //! ### `#[nue(...)]`, `#[nue_enc(...)]`, `#[nue_dec(...)]` 64 | //! 65 | //! Additional coding options may be provided per field using the `nue` attributes. 66 | //! They will affect how the parent type is encoded or decoded. The attributes accept 67 | //! arbitrary Rust expressions. Member variables may be accessed through `self`. 68 | //! 69 | //! `nue_enc` only applies to encoding, `nue_dec` applies to decoding, and `nue` applies to both. 70 | //! The order of the attributes doesn't usually matter, though `align` and `skip` interact 71 | //! differently depending on which is defined first. 72 | //! 73 | //! #### `assert` 74 | //! 75 | //! Asserts that some property is true before continuing with the operation. 76 | //! 77 | //! ``` 78 | //! # #![feature(plugin, custom_derive, custom_attribute)] #![plugin(nue_macros)] 79 | //! # extern crate nue; 80 | //! use nue::Decode; 81 | //! 82 | //! # fn main() { 83 | //! #[derive(NueDecode)] 84 | //! struct Data( 85 | //! #[nue(assert = "self.0 == 0")] 86 | //! u8 87 | //! ); 88 | //! 89 | //! let data = &[1]; 90 | //! assert!(&Data::decode_slice(data).is_err()); 91 | //! 92 | //! let data = &[0]; 93 | //! assert!(&Data::decode_slice(data).is_ok()); 94 | //! # } 95 | //! ``` 96 | //! 97 | //! #### `align` 98 | //! 99 | //! Aligns the field to an offset of the given multiple. 100 | //! 101 | //! ``` 102 | //! # #![feature(plugin, custom_derive, custom_attribute)] #![plugin(nue_macros)] 103 | //! # extern crate nue; 104 | //! use nue::Encode; 105 | //! 106 | //! # fn main() { 107 | //! #[derive(NueEncode)] 108 | //! struct Data( 109 | //! u8, 110 | //! #[nue(align = "self.0 as u64 + 1")] 111 | //! &'static str 112 | //! ); 113 | //! 114 | //! let data = Data(2, "hi"); 115 | //! let cmp = &[2, 0, 0, b'h', b'i']; 116 | //! assert_eq!(&data.encode_vec().unwrap(), cmp); 117 | //! # } 118 | //! ``` 119 | //! 120 | //! #### `skip` 121 | //! 122 | //! Discards the provided amount of bytes before encoding/decoding the value. 123 | //! 124 | //! ``` 125 | //! # #![feature(plugin, custom_derive, custom_attribute)] #![plugin(nue_macros)] 126 | //! # extern crate nue; 127 | //! use nue::Encode; 128 | //! 129 | //! # fn main() { 130 | //! #[derive(NueEncode)] 131 | //! struct Data( 132 | //! u8, 133 | //! #[nue(skip = "1")] 134 | //! &'static str 135 | //! ); 136 | //! 137 | //! let data = Data(2, "hi"); 138 | //! let cmp = &[2, 0, b'h', b'i']; 139 | //! assert_eq!(&data.encode_vec().unwrap(), cmp); 140 | //! # } 141 | //! ``` 142 | //! 143 | //! #### `cond` 144 | //! 145 | //! Conditionally encodes or decodes the field. If the condition is not met, 146 | //! `Default::default()` will be used. 147 | //! `false` is a static guarantee that the field will be ignored. 148 | //! 149 | //! ``` 150 | //! # #![feature(plugin, custom_derive, custom_attribute)] #![plugin(nue_macros)] 151 | //! # extern crate nue; 152 | //! use nue::Encode; 153 | //! 154 | //! # fn main() { 155 | //! #[derive(NueEncode)] 156 | //! struct Data<'a>( 157 | //! u8, 158 | //! #[nue(cond = "false")] 159 | //! &'a () // Note that this type does not implement `Encode` 160 | //! ); 161 | //! 162 | //! let u = (); 163 | //! let data = Data(2, &u); 164 | //! let cmp = &[2]; 165 | //! assert_eq!(&data.encode_vec().unwrap(), cmp); 166 | //! # } 167 | //! ``` 168 | //! 169 | //! #### `default` 170 | //! 171 | //! Determines the default value to be used if `cond` evaluates to false. 172 | //! 173 | //! ``` 174 | //! # #![feature(plugin, custom_derive, custom_attribute)] #![plugin(nue_macros)] 175 | //! # extern crate nue; 176 | //! use nue::Decode; 177 | //! 178 | //! # fn main() { 179 | //! #[derive(NueDecode, PartialEq, Debug)] 180 | //! struct Data( 181 | //! u8, 182 | //! #[nue(cond = "self.0 == 1", default = "5")] 183 | //! u8 184 | //! ); 185 | //! 186 | //! let data = &[2]; 187 | //! assert_eq!(&Data::decode_slice(data).unwrap(), &Data(2, 5)); 188 | //! 189 | //! let data = &[1, 2]; 190 | //! assert_eq!(&Data::decode_slice(data).unwrap(), &Data(1, 2)); 191 | //! # } 192 | //! ``` 193 | //! 194 | //! #### `limit` 195 | //! 196 | //! Limits the amount of bytes that can be consumed or written during coding. 197 | //! 198 | //! ``` 199 | //! # #![feature(plugin, custom_derive, custom_attribute)] #![plugin(nue_macros)] 200 | //! # extern crate nue; 201 | //! use nue::Decode; 202 | //! 203 | //! # fn main() { 204 | //! #[derive(NueDecode)] 205 | //! struct Data( 206 | //! #[nue(limit = "4")] 207 | //! String, 208 | //! ); 209 | //! 210 | //! let data = b"hello"; 211 | //! assert_eq!(&Data::decode_slice(data).unwrap().0, "hell"); 212 | //! # } 213 | //! ``` 214 | //! 215 | //! #### `consume` 216 | //! 217 | //! When set, uses all of `limit` even if the type did not encode or decode the entire byte region. 218 | //! 219 | //! ``` 220 | //! # #![feature(plugin, custom_derive, custom_attribute)] #![plugin(nue_macros)] 221 | //! # extern crate nue; 222 | //! use nue::Decode; 223 | //! use std::ffi::CString; 224 | //! 225 | //! # fn main() { 226 | //! #[derive(NueDecode)] 227 | //! struct Data( 228 | //! #[nue(limit = "8", consume = "true")] 229 | //! CString, 230 | //! u8 231 | //! ); 232 | //! 233 | //! let data = b"hello\0\0\0\x05"; 234 | //! let data = Data::decode_slice(data).unwrap(); 235 | //! assert_eq!(data.0.to_bytes(), b"hello"); 236 | //! assert_eq!(data.1, 5); 237 | //! # } 238 | //! ``` 239 | 240 | extern crate rustc; 241 | extern crate nue_codegen; 242 | 243 | #[plugin_registrar] 244 | #[doc(hidden)] 245 | pub fn register(reg: &mut rustc::plugin::Registry) { 246 | nue_codegen::register(reg); 247 | } 248 | -------------------------------------------------------------------------------- /macros/tests/code.rs: -------------------------------------------------------------------------------- 1 | use std::io::Cursor; 2 | use std::mem::size_of; 3 | use nue::{Encode, Decode, Un, Aligned}; 4 | 5 | #[derive(NueEncode, NueDecode, PartialEq, Debug)] 6 | struct _PodTest; 7 | 8 | #[test] 9 | fn encode_decode() { 10 | #[derive(PodPacked)] 11 | struct POD1 { 12 | _0: u8, 13 | _1: Un, 14 | } 15 | 16 | const UNIT: &'static () = &(); 17 | 18 | #[derive(NueEncode, NueDecode, PartialEq, Debug)] 19 | struct POD2 { 20 | _0: u8, 21 | #[nue(align = "1", skip = "0", limit = "2", consume = "true", cond = "self._0 == 1", default = "0u16.unaligned()")] 22 | _1: Un, 23 | #[nue(cond = "false", default = "UNIT")] 24 | _2: &'static (), 25 | _3: (), 26 | } 27 | 28 | let pod1 = POD1 { _0: 1, _1: 2u16.unaligned() }; 29 | let pod2 = POD2 { _0: 1, _1: 2u16.unaligned(), _2: UNIT, _3: () }; 30 | 31 | let buffer1 = Vec::new(); 32 | let mut buffer1 = Cursor::new(buffer1); 33 | pod1.encode(&mut buffer1).unwrap(); 34 | 35 | let buffer2 = Vec::new(); 36 | let mut buffer2 = Cursor::new(buffer2); 37 | pod2.encode(&mut buffer2).unwrap(); 38 | 39 | let buffer1 = buffer1.into_inner(); 40 | let buffer2 = buffer2.into_inner(); 41 | 42 | assert_eq!(size_of::(), 3); 43 | assert_eq!(&buffer1, &buffer2); 44 | 45 | let mut buffer2 = Cursor::new(buffer2); 46 | let pod2_decoded = Decode::decode(&mut buffer2).unwrap(); 47 | assert_eq!(&pod2, &pod2_decoded); 48 | } 49 | -------------------------------------------------------------------------------- /macros/tests/test.rs: -------------------------------------------------------------------------------- 1 | #![feature(plugin, custom_derive, custom_attribute)] 2 | #![plugin(nue_macros)] 3 | 4 | extern crate pod; 5 | extern crate nue; 6 | 7 | mod code; 8 | -------------------------------------------------------------------------------- /packed/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "packed" 3 | version = "0.3.0" 4 | authors = ["arcnmx"] 5 | 6 | description = "A safe #[repr(packed)] interface" 7 | documentation = "http://arcnmx.github.io/nue/packed/" 8 | repository = "https://github.com/arcnmx/nue" 9 | readme = "../README.md" 10 | keywords = ["nue", "packed", "data", "unaligned", "aligned"] 11 | license = "MIT" 12 | 13 | [features] 14 | unstable = [] 15 | -------------------------------------------------------------------------------- /packed/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(missing_docs)] 2 | #![cfg_attr(feature = "unstable", feature(optin_builtin_traits))] 3 | 4 | //! A safe approach to using `#[repr(packed)]` data. 5 | //! 6 | //! See `nue_macros` for the automagic `#[packed]` attribute. 7 | 8 | use std::mem::{transmute, replace, uninitialized, forget}; 9 | use std::marker::PhantomData; 10 | 11 | use std::mem::align_of; 12 | 13 | /// A marker trait indicating that a type has an alignment of `1`. 14 | /// 15 | /// In general, only applies to `()`, `bool`, `i8`, `u8`, and any types that 16 | /// contain only members of these types. 17 | pub unsafe trait Unaligned { } 18 | 19 | /// A type alias that represents the unaligned type of `T`. 20 | pub type Un = ::Unaligned; 21 | 22 | /// A marker trait indicating that a type has an alignment over `1`, 23 | /// and is therefore not safe to use in an unaligned context. 24 | pub unsafe trait Aligned: Sized { 25 | /// An unaligned representation of this type. Usually a u8 array of the 26 | /// same size. 27 | type Unaligned: Unaligned + Sized + Copy; 28 | 29 | /// Determines whether an unaligned representation of this type is aligned. 30 | #[inline] 31 | fn is_aligned(u: &Self::Unaligned) -> bool { 32 | u as *const _ as usize % align_of::() == 0 33 | } 34 | 35 | /// Borrows the value as unaligned. 36 | #[inline] 37 | fn as_unaligned(&self) -> &Self::Unaligned { 38 | unsafe { transmute(self) } 39 | } 40 | 41 | /// Mutably borrows the value as unaligned. 42 | #[inline] 43 | unsafe fn as_unaligned_mut(&mut self) -> &mut Self::Unaligned { 44 | transmute(self) 45 | } 46 | 47 | /// Borrows an unaligned type as an aligned value. 48 | /// 49 | /// Returns `None` if `u` is not aligned. 50 | #[inline] 51 | fn as_aligned(u: &Self::Unaligned) -> Option<&Self> { 52 | if Self::is_aligned(u) { 53 | Some(unsafe { Self::as_aligned_unchecked(u) }) 54 | } else { 55 | None 56 | } 57 | } 58 | 59 | /// Mutably borrows an unaligned type as an aligned value. 60 | /// 61 | /// Returns `None` if `u` is not aligned. 62 | #[inline] 63 | unsafe fn as_aligned_mut(u: &mut Self::Unaligned) -> Option<&mut Self> { 64 | if Self::is_aligned(u) { 65 | Some(Self::as_aligned_mut_unchecked(u)) 66 | } else { 67 | None 68 | } 69 | } 70 | 71 | /// Borrows an unaligned type as an aligned value, without first checking the alignment. 72 | /// 73 | /// Causes undefined behaviour if used improprly! 74 | #[inline] 75 | unsafe fn as_aligned_unchecked(u: &Self::Unaligned) -> &Self { 76 | transmute(u) 77 | } 78 | 79 | /// Mutably borrows an unaligned type as an aligned value, without first checking the alignment. 80 | /// 81 | /// Causes undefined behaviour if used improprly! 82 | #[inline] 83 | unsafe fn as_aligned_mut_unchecked(u: &mut Self::Unaligned) -> &mut Self { 84 | transmute(u) 85 | } 86 | 87 | /// Converts a value to its unaligned representation. 88 | #[inline] 89 | fn unaligned(self) -> Self::Unaligned { 90 | unsafe { 91 | let mut s: Self::Unaligned = uninitialized(); 92 | forget(replace(&mut s, *transmute::<_, &Self::Unaligned>(&self))); 93 | s 94 | } 95 | } 96 | 97 | /// Copies a value from its unaligned representation. 98 | #[inline] 99 | unsafe fn from_unaligned(u: Self::Unaligned) -> Self { 100 | let mut s: Self = uninitialized(); 101 | forget(replace(s.as_unaligned_mut(), u)); 102 | s 103 | } 104 | 105 | #[doc(hidden)] 106 | unsafe fn __assert_unaligned() { } 107 | } 108 | 109 | /// A marker trait indicating that a type is `#[repr(packed)]`. 110 | /// 111 | /// This means that all its members are packed or have an alignment of `1`, 112 | /// and its memory layout is guaranteed to be in member declaration order. 113 | pub unsafe trait Packed: Unaligned { 114 | #[doc(hidden)] 115 | fn __assert_unaligned() { } 116 | } 117 | 118 | #[cfg(feature = "unstable")] 119 | mod impls { 120 | use super::Unaligned; 121 | 122 | unsafe impl Unaligned for .. { } 123 | 124 | // All primitives except the packed ones listed below 125 | impl !Unaligned for char { } 126 | impl !Unaligned for f32 { } 127 | impl !Unaligned for f64 { } 128 | impl !Unaligned for i16 { } 129 | impl !Unaligned for u16 { } 130 | impl !Unaligned for i32 { } 131 | impl !Unaligned for u32 { } 132 | impl !Unaligned for i64 { } 133 | impl !Unaligned for u64 { } 134 | impl !Unaligned for isize { } 135 | impl !Unaligned for usize { } 136 | impl !Unaligned for *const T { } 137 | impl !Unaligned for *mut T { } 138 | impl<'a, T> !Unaligned for &'a T { } 139 | impl<'a, T> !Unaligned for &'a mut T { } 140 | } 141 | 142 | #[cfg(not(feature = "unstable"))] 143 | mod impls { 144 | use super::Unaligned; 145 | 146 | unsafe impl Unaligned for () { } 147 | unsafe impl Unaligned for i8 { } 148 | unsafe impl Unaligned for u8 { } 149 | unsafe impl Unaligned for bool { } 150 | } 151 | 152 | unsafe impl Unaligned for PhantomData { } 153 | 154 | macro_rules! aligned_assert { 155 | ($t:ident) => { 156 | unsafe fn __assert_unaligned() { 157 | ::std::mem::forget(::std::mem::transmute::<$t, $t::Unaligned>(::std::mem::uninitialized())); 158 | } 159 | }; 160 | } 161 | 162 | macro_rules! aligned_self { 163 | ($t:ty) => { 164 | unsafe impl Aligned for $t { 165 | type Unaligned = $t; 166 | } 167 | }; 168 | ($($t:ty),*) => { 169 | $( 170 | aligned_self!($t); 171 | )* 172 | }; 173 | } 174 | 175 | macro_rules! aligned_impl { 176 | ($t:ident: $s:expr) => { 177 | unsafe impl Aligned for $t { 178 | type Unaligned = [u8; $s]; 179 | 180 | aligned_assert!(Self); 181 | } 182 | }; 183 | ($($t:ident: $e:expr),*) => { 184 | $( 185 | aligned_impl!($t: $e); 186 | )* 187 | }; 188 | } 189 | 190 | aligned_impl! { 191 | char: 4, 192 | f32: 4, 193 | f64: 8, 194 | i16: 2, 195 | u16: 2, 196 | i32: 4, 197 | u32: 4, 198 | i64: 8, 199 | u64: 8 200 | } 201 | 202 | aligned_self! { 203 | u8, 204 | i8, 205 | (), 206 | bool 207 | } 208 | 209 | #[cfg(target_pointer_width = "32")] 210 | mod impl32 { 211 | use super::Aligned; 212 | aligned_impl! { isize: 4, usize: 4 } 213 | unsafe impl Aligned for *const T { type Unaligned = [u8; 4]; aligned_assert!(Self); } 214 | unsafe impl Aligned for *mut T { type Unaligned = [u8; 4]; aligned_assert!(Self); } 215 | unsafe impl<'a, T: Sized> Aligned for &'a T { type Unaligned = [u8; 4]; } 216 | unsafe impl<'a, T: Sized> Aligned for &'a mut T { type Unaligned = [u8; 4]; } 217 | } 218 | 219 | #[cfg(target_pointer_width = "64")] 220 | mod impl64 { 221 | use super::Aligned; 222 | aligned_impl! { isize: 8, usize: 8 } 223 | unsafe impl Aligned for *const T { type Unaligned = [u8; 8]; aligned_assert!(Self); } 224 | unsafe impl Aligned for *mut T { type Unaligned = [u8; 8]; aligned_assert!(Self); } 225 | unsafe impl<'a, T: Sized> Aligned for &'a T { type Unaligned = [u8; 8]; } 226 | unsafe impl<'a, T: Sized> Aligned for &'a mut T { type Unaligned = [u8; 8]; } 227 | } 228 | 229 | //unsafe impl Aligned for T { type Unaligned = T; } 230 | 231 | unsafe impl Packed for () { } 232 | unsafe impl Packed for i8 { } 233 | unsafe impl Packed for u8 { } 234 | unsafe impl Packed for bool { } 235 | unsafe impl Packed for PhantomData { } 236 | 237 | macro_rules! packed_def { 238 | (=> $($($x:ident),*;)*) => { 239 | $( 240 | unsafe impl<$($x: Unaligned),*> Unaligned for ($($x),*,) { } 241 | 242 | // TODO: The language probably doesn't guarantee ordering for tuples, even when all are 1-aligned, so this is incorrect? 243 | unsafe impl<$($x: Unaligned),*> Packed for ($($x),*,) { } 244 | )* 245 | }; 246 | ($($x:expr),*) => { 247 | $( 248 | unsafe impl Unaligned for [T; $x] { } 249 | 250 | unsafe impl Packed for [T; $x] { } 251 | )* 252 | }; 253 | } 254 | 255 | unsafe impl Aligned for (T,) { 256 | type Unaligned = T::Unaligned; 257 | } 258 | 259 | unsafe impl Aligned for [T; 1] { 260 | type Unaligned = T::Unaligned; 261 | } 262 | 263 | unsafe impl Aligned for [T; 0] { 264 | type Unaligned = (); 265 | } 266 | 267 | packed_def! { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f } 268 | packed_def! { 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f } 269 | packed_def! { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f } 270 | packed_def! { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f } 271 | packed_def! { 0x40 } 272 | packed_def! { => 273 | A; 274 | A, B; 275 | A, B, C; 276 | A, B, C, D; 277 | A, B, C, D, E; 278 | A, B, C, D, E, F; 279 | A, B, C, D, E, F, G; 280 | A, B, C, D, E, F, G, H; 281 | A, B, C, D, E, F, G, H, I; 282 | A, B, C, D, E, F, G, H, I, J; 283 | A, B, C, D, E, F, G, H, I, J, K; 284 | } 285 | 286 | #[test] 287 | fn assert_packed() { 288 | fn is() { } 289 | fn is_unaligned() { } 290 | 291 | is::<()>(); 292 | is::(); 293 | is::(); 294 | is::(); 295 | is_unaligned::<(bool, u8)>(); 296 | } 297 | -------------------------------------------------------------------------------- /pod/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pod" 3 | version = "0.3.1" 4 | authors = ["arcnmx"] 5 | 6 | description = "Plain Old Data (POD) encoding and I/O" 7 | documentation = "http://arcnmx.github.io/nue/pod/" 8 | repository = "https://github.com/arcnmx/nue" 9 | readme = "../README.md" 10 | keywords = ["nue", "pod", "data", "encode", "endian"] 11 | license = "MIT" 12 | 13 | [features] 14 | unstable = ["resize-slice/unstable", "packed/unstable"] 15 | 16 | [dependencies] 17 | byteorder = "0.3" 18 | resize-slice = "0.1" 19 | 20 | [dependencies.nue-io] 21 | version = "0.3" 22 | path = "../io" 23 | 24 | [dependencies.packed] 25 | version = "0.3" 26 | path = "../packed" 27 | 28 | [dependencies.uninitialized] 29 | version = "0.0" 30 | path = "../uninitialized" 31 | -------------------------------------------------------------------------------- /pod/src/code.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, Read, Write, BufReader, BufRead, Cursor}; 2 | use std::ffi::{CString, CStr}; 3 | use ::Pod; 4 | 5 | use uninitialized::UNINITIALIZED; 6 | use nue_io::ReadExactExt; 7 | 8 | /// Encodes an value's binary representation to a `Write`. 9 | /// 10 | /// Note that this is not serialization, and some data may be lost. 11 | /// It is left up to the implementation to decide what its representation is. 12 | /// 13 | /// # Provided Implementations 14 | /// 15 | /// ## `String`, `str` 16 | /// 17 | /// `String`s are encoded as their raw UTF-8 representation. Length is not encoded, and 18 | /// must be provided when decoding (or reads to end of stream). Will error upon invalid UTF-8 19 | /// sequences, nul bytes, etc. 20 | /// 21 | /// ## `CString`, `CStr` 22 | /// 23 | /// `CString`s are encoded with a trailing nul byte. Decoding runs until the first nul byte reached. 24 | /// 25 | /// ## `Vec`, `[T]` 26 | /// 27 | /// Vectors are encoded in sequence as packed raw values. Length is not encoded. 28 | /// 29 | /// ## `Option` 30 | /// 31 | /// `Some(T)` is encoded as `T`, `None` writes 0 bytes. Decoding will always produce `Some(T)` 32 | /// 33 | /// ## `Pod` 34 | /// 35 | /// `Pod` types are encoded as their raw in-memory representation. Use `EndianPrimitive` members to 36 | /// control the byte order. 37 | pub trait Encode { 38 | /// Options that may be provided to affect how the value is encoded 39 | type Options: Default; 40 | 41 | /// Encodes to the `Write` with default options 42 | fn encode(&self, w: &mut W) -> io::Result<()> { self.encode_options(w, Default::default()) } 43 | 44 | /// Encodes to the `Write` with the provided options 45 | fn encode_options(&self, w: &mut W, _options: Self::Options) -> io::Result<()> { self.encode(w) } 46 | 47 | /// Encodes to a new byte vector 48 | fn encode_vec(&self) -> io::Result> { 49 | let data = Vec::new(); 50 | let mut cursor = Cursor::new(data); 51 | 52 | try!(self.encode(&mut cursor)); 53 | 54 | Ok(cursor.into_inner()) 55 | } 56 | 57 | /// Encodes to a new byte vector with the provided options 58 | fn encode_vec_options(&self, options: Self::Options) -> io::Result> { 59 | let data = Vec::new(); 60 | let mut cursor = Cursor::new(data); 61 | 62 | try!(self.encode_options(&mut cursor, options)); 63 | 64 | Ok(cursor.into_inner()) 65 | } 66 | } 67 | 68 | /// Decodes data from a `Read` into a new value. 69 | /// 70 | /// See `Encode` for more details. 71 | pub trait Decode: Sized { 72 | /// Options will affect how the value is decoded, and may be used to provide any data that 73 | /// is normally lost during encoding. 74 | type Options: Default; 75 | 76 | /// Decodes from the `Read` with default options 77 | fn decode(r: &mut R) -> io::Result { Self::decode_options(r, Default::default()) } 78 | 79 | /// Decodes from the `Read` with the provided options 80 | fn decode_options(r: &mut R, _options: Self::Options) -> io::Result { Self::decode(r) } 81 | 82 | /// Decodes from a byte slice 83 | fn decode_slice(data: &[u8]) -> io::Result { 84 | let mut cursor = Cursor::new(data); 85 | 86 | Self::decode(&mut cursor) 87 | } 88 | 89 | /// Decodes from a byte slice with the provided options 90 | fn decode_slice_options(data: &[u8], options: Self::Options) -> io::Result { 91 | let mut cursor = Cursor::new(data); 92 | 93 | Self::decode_options(&mut cursor, options) 94 | } 95 | 96 | /// Implement to assert that the decoded contents are valid 97 | /// 98 | /// # Warning 99 | /// 100 | /// This takes `&self`, meaning the object is still created before validation occurs. 101 | /// Therefore any `Drop` implementations must not assume that the object is valid. 102 | fn validate(&self) -> io::Result<()> { Ok(()) } 103 | } 104 | 105 | impl Encode for Option { 106 | type Options = T::Options; 107 | 108 | fn encode(&self, w: &mut W) -> io::Result<()> { 109 | self.as_ref().map(|v| v.encode(w)).unwrap_or(Ok(())) 110 | } 111 | 112 | fn encode_options(&self, w: &mut W, options: Self::Options) -> io::Result<()> { 113 | self.as_ref().map(|v| v.encode_options(w, options)).unwrap_or(Ok(())) 114 | } 115 | } 116 | 117 | impl Decode for Option { 118 | type Options = T::Options; 119 | 120 | fn decode(r: &mut R) -> io::Result { 121 | T::decode(r).map(Some) 122 | } 123 | 124 | fn decode_options(r: &mut R, options: Self::Options) -> io::Result { 125 | T::decode_options(r, options).map(Some) 126 | } 127 | } 128 | 129 | impl Encode for T { 130 | type Options = (); 131 | 132 | fn encode(&self, w: &mut W) -> io::Result<()> { 133 | w.write_all(self.as_slice()) 134 | } 135 | } 136 | 137 | impl Decode for T { 138 | type Options = (); 139 | 140 | fn decode(r: &mut R) -> io::Result { 141 | // TODO: Would be nice if we could use [0u8; size_of::()] 142 | let mut pod: Self = Pod::zeroed(); 143 | 144 | try!(r.read_exact(pod.mut_slice())); 145 | Ok(pod) 146 | } 147 | } 148 | 149 | impl Decode for String { 150 | type Options = StringDecodeOptions; 151 | 152 | fn decode_options(r: &mut R, options: Self::Options) -> io::Result { 153 | if let Some(len) = options.len { 154 | let mut vec = if UNINITIALIZED { 155 | let mut vec = Vec::with_capacity(len); 156 | unsafe { vec.set_len(len); } 157 | vec 158 | } else { 159 | vec![0; len] 160 | }; 161 | try!(r.read_exact(&mut vec[..])); 162 | String::from_utf8(vec).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e)) 163 | } else { 164 | let mut string = String::new(); 165 | try!(r.read_to_string(&mut string)); 166 | Ok(string) 167 | } 168 | } 169 | } 170 | 171 | impl Encode for String { 172 | type Options = (); 173 | 174 | fn encode(&self, w: &mut W) -> io::Result<()> { 175 | (**self).encode(w) 176 | } 177 | } 178 | 179 | impl Encode for str { 180 | type Options = (); 181 | 182 | fn encode(&self, w: &mut W) -> io::Result<()> { 183 | w.write_all(self.as_bytes()) 184 | } 185 | } 186 | 187 | impl<'a> Encode for &'a str { 188 | type Options = (); 189 | 190 | fn encode(&self, w: &mut W) -> io::Result<()> { 191 | (*self).encode(w) 192 | } 193 | } 194 | 195 | impl Decode for CString { 196 | type Options = CStringDecodeOptions; 197 | 198 | fn decode_options(r: &mut R, options: Self::Options) -> io::Result { 199 | if options.require_nul { 200 | unimplemented!() 201 | } 202 | 203 | unsafe { 204 | let mut err = Ok(()); 205 | let string = CString::from_vec_unchecked(r.bytes().map(|c| match c { 206 | Ok(c) => c, 207 | Err(e) => { 208 | err = Err(e); 209 | 0 210 | }, 211 | }).take_while(|&c| c > 0).collect()); 212 | err.map(|_| string) 213 | } 214 | } 215 | } 216 | 217 | impl Encode for CString { 218 | type Options = (); 219 | 220 | fn encode(&self, w: &mut W) -> io::Result<()> { 221 | (**self).encode(w) 222 | } 223 | } 224 | 225 | impl Encode for CStr { 226 | type Options = (); 227 | 228 | fn encode(&self, w: &mut W) -> io::Result<()> { 229 | w.write_all(self.to_bytes_with_nul()) 230 | } 231 | } 232 | 233 | impl<'a> Encode for &'a CStr { 234 | type Options = (); 235 | 236 | fn encode(&self, w: &mut W) -> io::Result<()> { 237 | (*self).encode(w) 238 | } 239 | } 240 | 241 | impl Decode for Vec where T::Options: Clone { 242 | type Options = VecDecodeOptions; 243 | 244 | fn decode_options(r: &mut R, options: Self::Options) -> io::Result { 245 | let mut vec = Vec::with_capacity(options.len.unwrap_or(0)); 246 | if let Some(len) = options.len { 247 | for _ in 0..len { 248 | vec.push(try!(T::decode_options(r, options.options.clone()))); 249 | } 250 | } else { 251 | let r = &mut BufReader::new(r); 252 | while try!(r.fill_buf()).len() > 0 { 253 | vec.push(try!(T::decode_options(r, options.options.clone()))); 254 | } 255 | } 256 | 257 | Ok(vec) 258 | } 259 | } 260 | 261 | impl Encode for Vec where T::Options: Clone { 262 | type Options = T::Options; 263 | 264 | fn encode(&self, w: &mut W) -> io::Result<()> { 265 | (**self).encode(w) 266 | } 267 | 268 | fn encode_options(&self, w: &mut W, options: Self::Options) -> io::Result<()> { 269 | (**self).encode_options(w, options) 270 | } 271 | } 272 | 273 | impl Encode for [T] where T::Options: Clone { 274 | type Options = T::Options; 275 | 276 | fn encode(&self, w: &mut W) -> io::Result<()> { 277 | for ref v in self { 278 | try!(v.encode(w)); 279 | } 280 | 281 | Ok(()) 282 | } 283 | 284 | fn encode_options(&self, w: &mut W, options: Self::Options) -> io::Result<()> { 285 | for ref v in self { 286 | try!(v.encode_options(w, options.clone())); 287 | } 288 | 289 | Ok(()) 290 | } 291 | } 292 | 293 | impl<'a, T: Encode> Encode for &'a [T] where T::Options: Clone { 294 | type Options = T::Options; 295 | 296 | fn encode(&self, w: &mut W) -> io::Result<()> { 297 | (*self).encode(w) 298 | } 299 | 300 | fn encode_options(&self, w: &mut W, options: Self::Options) -> io::Result<()> { 301 | (*self).encode_options(w, options) 302 | } 303 | } 304 | 305 | /// Describes how to decode a `Vec` 306 | #[derive(Clone, Default, Debug)] 307 | pub struct VecDecodeOptions { 308 | /// Reads `Some(len)` items, or until EOF 309 | pub len: Option, 310 | 311 | /// The options used to decode each individual item 312 | pub options: T, 313 | } 314 | 315 | /// Describes how to decode a `String` 316 | #[derive(Copy, Clone, Default, Debug)] 317 | pub struct StringDecodeOptions { 318 | /// Reads `Some(len)` bytes, or until EOF 319 | pub len: Option, 320 | } 321 | 322 | /// Describes how to decode a `CString` 323 | #[derive(Copy, Clone, Default, Debug)] 324 | pub struct CStringDecodeOptions { 325 | /// When true, errors if EOF is reached before a nul byte is found 326 | pub require_nul: bool, 327 | } 328 | -------------------------------------------------------------------------------- /pod/src/endian.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::fmt; 3 | use std::cmp::Ordering; 4 | use std::hash::{Hash, Hasher}; 5 | use byteorder::{ByteOrder, LittleEndian, BigEndian, NativeEndian}; 6 | use uninitialized::uninitialized; 7 | use packed::{Unaligned, Aligned, Packed}; 8 | use pod::Pod; 9 | 10 | /// A type alias for unaligned little endian primitives 11 | pub type Le = EndianPrimitive; 12 | 13 | /// A type alias for unaligned big endian primitives 14 | pub type Be = EndianPrimitive; 15 | 16 | /// A type alias for unaligned native endian primitives 17 | pub type Native = EndianPrimitive; 18 | 19 | /// A POD container for a primitive that stores a value in the specified endianness 20 | /// in memory, and transforms on `get`/`set` 21 | #[repr(C)] 22 | pub struct EndianPrimitive { 23 | value: T::Unaligned, 24 | _phantom: PhantomData<*const B>, 25 | } 26 | 27 | impl EndianPrimitive { 28 | /// Creates a new value 29 | #[inline] 30 | pub fn new(v: T) -> Self { 31 | EndianPrimitive { 32 | value: EndianConvert::to::(v), 33 | _phantom: PhantomData, 34 | } 35 | } 36 | 37 | /// Transforms to the native value 38 | #[inline] 39 | pub fn get(&self) -> T { 40 | EndianConvert::from::(&self.value) 41 | } 42 | 43 | /// Transforms from a native value 44 | #[inline] 45 | pub fn set(&mut self, v: T) { 46 | self.value = EndianConvert::to::(v) 47 | } 48 | 49 | /// Gets the inner untransformed value 50 | #[inline] 51 | pub fn raw(&self) -> &T::Unaligned { 52 | &self.value 53 | } 54 | 55 | /// A mutable reference to the inner untransformed value 56 | #[inline] 57 | pub fn raw_mut(&mut self) -> &mut T::Unaligned { 58 | &mut self.value 59 | } 60 | } 61 | 62 | unsafe impl Pod for EndianPrimitive { } 63 | unsafe impl Unaligned for EndianPrimitive { } 64 | unsafe impl Packed for EndianPrimitive { } 65 | 66 | impl Default for EndianPrimitive { 67 | #[inline] 68 | fn default() -> Self { 69 | Self::new(Default::default()) 70 | } 71 | } 72 | 73 | impl From for EndianPrimitive { 74 | #[inline] 75 | fn from(v: T) -> Self { 76 | Self::new(v) 77 | } 78 | } 79 | 80 | impl fmt::Debug for EndianPrimitive { 81 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 82 | ::fmt(&self.get(), f) 83 | } 84 | } 85 | 86 | impl> PartialEq> for EndianPrimitive { 87 | #[inline] 88 | fn eq(&self, other: &EndianPrimitive) -> bool { 89 | self.get().eq(&other.get()) 90 | } 91 | } 92 | 93 | impl Eq for EndianPrimitive { } 94 | 95 | impl> PartialOrd> for EndianPrimitive { 96 | #[inline] 97 | fn partial_cmp(&self, other: &EndianPrimitive) -> Option { 98 | self.get().partial_cmp(&other.get()) 99 | } 100 | } 101 | 102 | impl Ord for EndianPrimitive { 103 | #[inline] 104 | fn cmp(&self, other: &Self) -> Ordering { 105 | self.get().cmp(&other.get()) 106 | } 107 | } 108 | 109 | impl Hash for EndianPrimitive where T::Unaligned: Hash { 110 | #[inline] 111 | fn hash(&self, h: &mut H) { 112 | self.value.hash(h) 113 | } 114 | } 115 | 116 | impl Clone for EndianPrimitive { 117 | #[inline] 118 | fn clone(&self) -> Self { 119 | EndianPrimitive { 120 | value: self.value.clone(), 121 | _phantom: PhantomData, 122 | } 123 | } 124 | } 125 | 126 | impl Copy for EndianPrimitive { } 127 | 128 | /// Describes a value that can be converted to and from a specified byte order. 129 | pub trait EndianConvert: Aligned { 130 | /// Converts a value from `B` 131 | fn from(&Self::Unaligned) -> Self; 132 | 133 | /// Converts a value to `B` 134 | fn to(self) -> Self::Unaligned; 135 | } 136 | 137 | macro_rules! endian_impl { 138 | ($t:ty: $s:expr => $r:ident, $w:ident) => { 139 | impl EndianConvert for $t { 140 | #[inline] 141 | fn from(s: &Self::Unaligned) -> Self { 142 | B::$r(s) 143 | } 144 | 145 | #[inline] 146 | fn to(self) -> Self::Unaligned { 147 | let mut s: Self::Unaligned = unsafe { uninitialized() }; 148 | B::$w(&mut s, self); 149 | s 150 | } 151 | } 152 | }; 153 | } 154 | 155 | endian_impl!(u16: 2 => read_u16, write_u16); 156 | endian_impl!(i16: 2 => read_i16, write_i16); 157 | endian_impl!(i32: 4 => read_i32, write_i32); 158 | endian_impl!(u32: 4 => read_u32, write_u32); 159 | endian_impl!(i64: 8 => read_i64, write_i64); 160 | endian_impl!(u64: 8 => read_u64, write_u64); 161 | endian_impl!(f32: 4 => read_f32, write_f32); 162 | endian_impl!(f64: 8 => read_f64, write_f64); 163 | 164 | impl EndianConvert for bool { 165 | #[inline] 166 | fn from(s: &Self::Unaligned) -> Self { 167 | *s as u8 != 0 168 | } 169 | 170 | #[inline] 171 | fn to(self) -> Self::Unaligned { 172 | if self as u8 != 0 { true } else { false } 173 | } 174 | } 175 | 176 | #[test] 177 | fn endian_size() { 178 | use std::mem::size_of; 179 | use std::mem::align_of; 180 | 181 | type B = NativeEndian; 182 | 183 | assert_eq!(size_of::>(), 2); 184 | assert_eq!(size_of::>(), 4); 185 | assert_eq!(size_of::>(), 8); 186 | assert_eq!(size_of::>(), 4); 187 | assert_eq!(size_of::>(), 8); 188 | 189 | assert_eq!(align_of::>(), 1); 190 | assert_eq!(align_of::>(), 1); 191 | assert_eq!(align_of::>(), 1); 192 | assert_eq!(align_of::>(), 1); 193 | assert_eq!(align_of::>(), 1); 194 | assert_eq!(align_of::>(), 1); 195 | } 196 | -------------------------------------------------------------------------------- /pod/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(feature = "unstable", feature(box_raw))] 2 | #![deny(missing_docs)] 3 | 4 | //! Provides traits that assist with I/O and byte slice conversions involving Plain Old Data. 5 | //! 6 | //! # Safety 7 | //! 8 | //! The `nue-macros` crate can be used for safe automagic derives. 9 | //! 10 | //! # Example 11 | //! 12 | //! ``` 13 | //! use pod::{Pod, Le, Be}; 14 | //! # #[cfg(not(feature = "unstable"))] 15 | //! # mod stable { 16 | //! # use pod::packed::{Unaligned, Packed}; 17 | //! # unsafe impl Packed for super::Data { } 18 | //! # unsafe impl Unaligned for super::Data { } 19 | //! # } 20 | //! 21 | //! unsafe impl Pod for Data { } 22 | //! 23 | //! #[repr(C)] 24 | //! struct Data(u8, Le, Be); 25 | //! 26 | //! # fn main() { 27 | //! let data = Data(1, Le::new(0x2055), Be::new(0xdeadbeef)); 28 | //! 29 | //! let cmp = &[ 30 | //! 0x01, 31 | //! 0x55, 0x20, 32 | //! 0xde, 0xad, 0xbe, 0xef, 33 | //! ]; 34 | //! 35 | //! assert_eq!(cmp, data.as_slice()); 36 | //! # } 37 | //! 38 | //! ``` 39 | 40 | extern crate uninitialized; 41 | extern crate resize_slice; 42 | extern crate byteorder; 43 | extern crate packed as nue_packed; 44 | extern crate nue_io; 45 | 46 | mod pod; 47 | 48 | /// I/O traits for POD and other types. 49 | pub mod code; 50 | 51 | /// Containers for primitives 52 | pub mod endian; 53 | 54 | pub use endian::{Le, Be, Native}; 55 | pub use code::{Encode, Decode}; 56 | pub use pod::Pod; 57 | 58 | /// Re-export the `packed` crate 59 | pub use nue_packed as packed; 60 | -------------------------------------------------------------------------------- /pod/src/pod.rs: -------------------------------------------------------------------------------- 1 | use std::mem::{size_of, transmute, uninitialized}; 2 | use std::slice::{from_raw_parts, from_raw_parts_mut}; 3 | use resize_slice::SliceExt; 4 | use packed::{Unaligned, Aligned}; 5 | use uninitialized; 6 | 7 | use self::unstable::{box_from, box_into}; 8 | 9 | /// A marker trait indicating that a type is Plain Old Data. 10 | /// 11 | /// It is unsafe to `impl` this manually, use `#[derive(Pod)]` instead. 12 | pub unsafe trait Pod: Sized { 13 | #[doc(hidden)] 14 | fn __assert_pod() { } 15 | 16 | /// Safely borrows the aligned value mutably 17 | /// 18 | /// See also: `Aligned::as_aligned_mut` 19 | #[inline] 20 | fn mut_aligned>(&mut self) -> Option<&mut T> where Self: Copy + Unaligned { 21 | unsafe { T::as_aligned_mut(self) } 22 | } 23 | 24 | /// Safely borrows the unaligned value mutably 25 | /// 26 | /// See also: `Aligned::as_unaligned_mut` 27 | #[inline] 28 | fn mut_unaligned(s: &mut T) -> Option<&mut Self> where Self: Aligned { 29 | unsafe { Self::as_aligned_mut(s) } 30 | } 31 | 32 | /// Safely creates a POD value from a potentially unaligned slice 33 | /// 34 | /// # Panics 35 | /// 36 | /// Panics if `slice.len()` is not the same as the type's size 37 | #[inline] 38 | fn copy_from<'a>(slice: &'a [u8]) -> Self { 39 | assert_eq!(slice.len(), size_of::()); 40 | let mut s: Self = unsafe { uninitialized() }; 41 | s.mut_slice().copy_from(slice); 42 | s 43 | } 44 | 45 | /// Safely converts an unaligned value to its aligned equivalent 46 | /// 47 | /// See also: `Aligned::from_unaligned` 48 | #[inline] 49 | fn aligned(s: T) -> Self where Self: Aligned { 50 | unsafe { Self::from_unaligned(s) } 51 | } 52 | 53 | /// Borrows the POD as a byte slice 54 | #[inline] 55 | fn as_slice<'a>(&'a self) -> &'a [u8] { 56 | unsafe { from_raw_parts(self as *const Self as *const u8, size_of::()) } 57 | } 58 | 59 | /// Borrows the POD as a mutable byte slice 60 | #[inline] 61 | fn mut_slice<'a>(&'a mut self) -> &'a mut [u8] { 62 | unsafe { from_raw_parts_mut(self as *mut Self as *mut u8, size_of::()) } 63 | } 64 | 65 | /// Borrows a new instance of the POD from a byte slice 66 | /// 67 | /// # Panics 68 | /// 69 | /// Panics if `slice.len()` is not the same as the type's size 70 | #[inline] 71 | fn from_slice<'a>(slice: &'a [u8]) -> &'a Self where Self: Unaligned { 72 | assert_eq!(slice.len(), size_of::()); 73 | unsafe { &*(slice.as_ptr() as *const _) } 74 | } 75 | 76 | /// Borrows a mutable instance of the POD from a mutable byte slice 77 | /// 78 | /// # Panics 79 | /// 80 | /// Panics if `slice.len()` is not the same as the type's size 81 | #[inline] 82 | fn from_mut_slice<'a>(slice: &'a mut [u8]) -> &'a mut Self where Self: Unaligned { 83 | assert_eq!(slice.len(), size_of::()); 84 | unsafe { &mut *(slice.as_mut_ptr() as *mut _) } 85 | } 86 | 87 | /// Converts a byte vector to a boxed instance of the POD type 88 | /// 89 | /// # Panics 90 | /// 91 | /// Panics if `vec.len()` is not the same as the type's size 92 | #[inline] 93 | fn from_vec(vec: Vec) -> Box where Self: Unaligned { 94 | Self::from_box(vec.into_boxed_slice()) 95 | } 96 | 97 | /// Converts a boxed slice to a boxed instance of the POD type 98 | /// 99 | /// # Panics 100 | /// 101 | /// Panics if `slice.len()` is not the same as the type's size 102 | #[inline] 103 | fn from_box(slice: Box<[u8]>) -> Box where Self: Unaligned { 104 | assert!(slice.len() == size_of::()); 105 | unsafe { 106 | box_from((&mut *box_into(slice)).as_mut_ptr() as *mut _) 107 | } 108 | } 109 | 110 | /// Converts a boxed POD to a byte vector 111 | #[inline] 112 | fn to_vec(self: Box) -> Vec { 113 | self.to_boxed_slice().into_vec() 114 | } 115 | 116 | /// Converts a boxed POD to a boxed slice 117 | #[inline] 118 | fn to_boxed_slice(self: Box) -> Box<[u8]> { 119 | let ptr = box_into(self); 120 | unsafe { 121 | let ptr = from_raw_parts_mut(ptr as *mut u8, size_of::()); 122 | box_from(ptr) 123 | } 124 | } 125 | 126 | /// Converts a POD type from one to another of the same size. 127 | /// 128 | /// # Panics 129 | /// 130 | /// Panics if the two types are not the same size 131 | #[inline] 132 | fn map<'a, T: Pod + Unaligned>(&'a self) -> &'a T where Self: Unaligned { 133 | assert_eq!(size_of::(), size_of::()); 134 | unsafe { 135 | transmute(self) 136 | } 137 | } 138 | 139 | /// Converts a POD type from one to another of the same size. 140 | /// 141 | /// # Panics 142 | /// 143 | /// Panics if the two types are not the same size 144 | #[inline] 145 | fn map_mut<'a, T: Pod + Unaligned>(&'a mut self) -> &'a mut T where Self: Unaligned { 146 | assert_eq!(size_of::(), size_of::()); 147 | unsafe { 148 | transmute(self) 149 | } 150 | } 151 | 152 | /// Generates a new uninitialized instance of a POD type. 153 | #[inline] 154 | unsafe fn uninitialized() -> Self { 155 | uninitialized() 156 | } 157 | 158 | /// Creates a new zeroed instance of a POD type. 159 | #[inline] 160 | fn zeroed() -> Self { 161 | unsafe { uninitialized::uninitialized() } 162 | } 163 | 164 | /// Maps a POD slice from one type to another 165 | /// 166 | /// # Panics 167 | /// 168 | /// Will panic if the output type does not perfectly fit into the slice. 169 | #[inline] 170 | fn map_slice<'a, T: Pod + Unaligned>(s: &'a [Self]) -> &'a [T] where Self: Unaligned { 171 | let len = s.len() * size_of::(); 172 | assert_eq!(len % size_of::(), 0); 173 | unsafe { from_raw_parts(s.as_ptr() as *const T, len / size_of::()) } 174 | } 175 | 176 | /// Maps a mutable POD slice from one type to another 177 | /// 178 | /// # Panics 179 | /// 180 | /// Will panic if the output type does not perfectly fit into the slice. 181 | #[inline] 182 | fn map_mut_slice<'a, T: Pod + Unaligned>(s: &'a mut [Self]) -> &'a mut [T] where Self: Unaligned { 183 | let len = s.len() * size_of::(); 184 | assert_eq!(len % size_of::(), 0); 185 | unsafe { from_raw_parts_mut(s.as_mut_ptr() as *mut T, len / size_of::()) } 186 | } 187 | } 188 | 189 | unsafe impl Pod for () { } 190 | unsafe impl Pod for f32 { } 191 | unsafe impl Pod for f64 { } 192 | unsafe impl Pod for i8 { } 193 | unsafe impl Pod for u8 { } 194 | unsafe impl Pod for i16 { } 195 | unsafe impl Pod for u16 { } 196 | unsafe impl Pod for i32 { } 197 | unsafe impl Pod for u32 { } 198 | unsafe impl Pod for i64 { } 199 | unsafe impl Pod for u64 { } 200 | unsafe impl Pod for isize { } 201 | unsafe impl Pod for usize { } 202 | unsafe impl Pod for *const T { } 203 | unsafe impl Pod for *mut T { } 204 | 205 | macro_rules! pod_def { 206 | ($($x:expr),*) => { 207 | $( 208 | unsafe impl Pod for [T; $x] { } 209 | )* 210 | }; 211 | } 212 | 213 | unsafe impl Pod for (T,) { } 214 | pod_def! { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f } 215 | pod_def! { 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f } 216 | pod_def! { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f } 217 | pod_def! { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f } 218 | pod_def! { 0x40 } 219 | 220 | #[cfg(feature = "unstable")] 221 | mod unstable { 222 | pub unsafe fn box_from(raw: *mut T) -> Box { Box::from_raw(raw) } 223 | pub fn box_into(b: Box) -> *mut T { Box::into_raw(b) } 224 | } 225 | 226 | #[cfg(not(feature = "unstable"))] 227 | mod unstable { 228 | use std::mem::transmute; 229 | 230 | pub unsafe fn box_from(raw: *mut T) -> Box { transmute(raw) } 231 | pub fn box_into(b: Box) -> *mut T { unsafe { transmute(b) } } 232 | } 233 | -------------------------------------------------------------------------------- /pod/tests/test.rs: -------------------------------------------------------------------------------- 1 | extern crate pod; 2 | 3 | use pod::{Pod, Le, Be, Encode, Decode}; 4 | use pod::packed::{Packed, Aligned, Un}; 5 | use std::io::{Cursor, Seek, SeekFrom}; 6 | 7 | #[cfg(not(feature = "unstable"))] 8 | mod stable { 9 | use pod::packed::Unaligned; 10 | unsafe impl Unaligned for super::POD { } 11 | } 12 | 13 | unsafe impl Packed for POD { } 14 | unsafe impl Pod for POD { } 15 | 16 | #[repr(C)] 17 | #[derive(Copy, Clone, PartialEq, Debug)] 18 | struct POD { 19 | zero: u8, 20 | ffff: Un, 21 | one: Le, 22 | two: Be, 23 | } 24 | 25 | const POD_BYTES: [u8; 9] = [ 26 | 0x00, 27 | 0xff, 0xff, 28 | 0x01, 0x00, 29 | 0x00, 0x00, 0x00, 0x02, 30 | ]; 31 | 32 | fn sample() -> POD { 33 | POD { 34 | zero: 0, 35 | ffff: 0xffffu16.unaligned(), 36 | one: Le::new(1), 37 | two: Be::new(2), 38 | } 39 | } 40 | 41 | #[test] 42 | fn pod_encoding() { 43 | let pod = sample(); 44 | 45 | let buffer = Vec::new(); 46 | let mut buffer = Cursor::new(buffer); 47 | 48 | pod.encode(&mut buffer).unwrap(); 49 | 50 | buffer.seek(SeekFrom::Start(0)).unwrap(); 51 | assert_eq!(pod, POD::decode(&mut buffer).unwrap()); 52 | 53 | let buffer = buffer.into_inner(); 54 | 55 | assert_eq!(&buffer[..], &POD_BYTES[..]); 56 | } 57 | 58 | #[test] 59 | fn pod_slice() { 60 | assert_eq!(sample(), *POD::from_slice(&POD_BYTES)) 61 | } 62 | 63 | #[test] 64 | fn pod_box() { 65 | use std::iter::FromIterator; 66 | 67 | let vec: Vec = Vec::from_iter(POD_BYTES.iter().cloned()); 68 | let boxed = POD::from_vec(vec); 69 | assert_eq!(*boxed, sample()); 70 | } 71 | -------------------------------------------------------------------------------- /src.rs: -------------------------------------------------------------------------------- 1 | #![deny(missing_docs)] 2 | 3 | //! A collection of tools for working with binary data, I/O, and POD structs. 4 | //! 5 | //! Re-exports items from the `pod` and `nue_io` crates. See `nue_macros` 6 | //! for more examples and usage. 7 | 8 | extern crate nue_io; 9 | extern crate packed as nue_packed; 10 | extern crate pod; 11 | 12 | pub use pod::*; 13 | pub use nue_io::*; 14 | pub use nue_packed::*; 15 | -------------------------------------------------------------------------------- /tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nue-tests" 3 | version = "0.0.0" 4 | authors = ["arcnmx"] 5 | build = "build.rs" 6 | 7 | [features] 8 | unstable = ["pod/unstable", "nue/unstable"] 9 | 10 | [build-dependencies] 11 | syntex = "0.7" 12 | 13 | [build-dependencies.nue-codegen] 14 | path = "../codegen" 15 | 16 | [dev-dependencies.nue] 17 | path = "../" 18 | 19 | [dev-dependencies.pod] 20 | path = "../pod" 21 | 22 | [[test]] 23 | name = "test" 24 | path = "tests/test.rs" 25 | -------------------------------------------------------------------------------- /tests/build.rs: -------------------------------------------------------------------------------- 1 | extern crate syntex; 2 | extern crate nue_codegen; 3 | 4 | use std::env; 5 | use std::path::Path; 6 | 7 | fn main() { 8 | let out_dir = env::var_os("OUT_DIR").unwrap(); 9 | 10 | for &(src, dst) in &[ 11 | ("../macros/tests/code.rs", "code.rs"), 12 | ] { 13 | let src = Path::new(src); 14 | let dst = Path::new(&out_dir).join(dst); 15 | 16 | let mut registry = syntex::Registry::new(); 17 | 18 | nue_codegen::register(&mut registry); 19 | registry.expand("", &src, &dst).unwrap(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/tests/test.rs: -------------------------------------------------------------------------------- 1 | extern crate nue; 2 | extern crate pod; 3 | 4 | include!(concat!(env!("OUT_DIR"), "/code.rs")); 5 | -------------------------------------------------------------------------------- /uninitialized/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uninitialized" 3 | version = "0.0.1" 4 | authors = ["arcnmx"] 5 | 6 | description = "Opt-in unsafe uninitialized memory" 7 | documentation = "http://arcnmx.github.io/nue/uninitialized/" 8 | repository = "https://github.com/arcnmx/nue" 9 | readme = "../README.md" 10 | keywords = ["uninitialized", "zeroed", "memory", "unsafe", "nue"] 11 | license = "MIT" 12 | 13 | [features] 14 | uninitialized = [] 15 | -------------------------------------------------------------------------------- /uninitialized/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(missing_docs)] 2 | 3 | //! Provides the option to use uninitialized memory for performance improvements. 4 | //! 5 | //! `uninitialized()` is backed by `std::mem::zeroed()` unless the feature is toggled on. 6 | //! Downstream binary crates that want to take advantage of `std::mem::uninitialized()` 7 | //! should use the following in `Cargo.toml`: 8 | //! 9 | //! ```toml 10 | //! [dependencies.uninitialized] 11 | //! version = "*" 12 | //! features = ["uninitialized"] 13 | //! ``` 14 | 15 | #[cfg(feature = "uninitialized")] 16 | pub use std::mem::uninitialized as uninitialized; 17 | 18 | #[cfg(not(feature = "uninitialized"))] 19 | pub use std::mem::zeroed as uninitialized; 20 | 21 | /// A constant indicating whether the `uninitialized` feature is enabled. 22 | #[cfg(feature = "uninitialized")] 23 | pub const UNINITIALIZED: bool = true; 24 | 25 | /// A constant indicating whether the `uninitialized` feature is enabled. 26 | #[cfg(not(feature = "uninitialized"))] 27 | pub const UNINITIALIZED: bool = false; 28 | --------------------------------------------------------------------------------