├── .github └── workflows │ └── cookie-factory.yml ├── .gitignore ├── .reuse └── dep5 ├── Cargo.toml ├── LICENSES └── MIT.txt ├── README.md ├── benches ├── http.rs ├── json-functions.rs └── json-macros.rs ├── examples ├── async_http.rs ├── cursor.rs ├── http.rs └── json.rs ├── src ├── async_bufwriter.rs ├── bytes.rs ├── combinator.rs ├── gen.rs ├── internal.rs ├── io_compat.rs ├── lib.rs ├── multi.rs └── sequence.rs └── tests ├── combinators-json.rs ├── http.rs ├── http └── mod.rs ├── json └── mod.rs └── pouet.rs /.github/workflows/cookie-factory.yml: -------------------------------------------------------------------------------- 1 | name: cookie-factory 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths: 8 | - '.github/**' 9 | - 'Cargo.toml' 10 | pull_request: 11 | branches: 12 | - master 13 | paths: 14 | - 'src/**' 15 | - '.github/**' 16 | - 'Cargo.toml' 17 | 18 | jobs: 19 | reuse: 20 | runs-on: ubuntu-latest 21 | 22 | steps: 23 | - uses: actions/checkout@v4 24 | 25 | - name: REUSE Compliance Check 26 | uses: fsfe/reuse-action@v2 27 | 28 | clippy-rustfmt: 29 | strategy: 30 | matrix: 31 | platform: [ubuntu-latest] 32 | 33 | runs-on: ${{ matrix.platform }} 34 | 35 | steps: 36 | - uses: actions/checkout@v4 37 | 38 | - name: Install Rust stable 39 | uses: dtolnay/rust-toolchain@stable 40 | with: 41 | toolchain: stable 42 | components: clippy, rustfmt 43 | 44 | - name: Run rustfmt 45 | run: 46 | cargo fmt --all -- --check --verbose 47 | 48 | - name: Run cargo clippy 49 | uses: clechasseur/rs-clippy-check@v3 50 | with: 51 | args: --all -- -D warnings 52 | 53 | build: 54 | 55 | needs: [reuse, clippy-rustfmt] 56 | 57 | strategy: 58 | matrix: 59 | platform: [ubuntu-latest] 60 | 61 | runs-on: ${{ matrix.platform }} 62 | 63 | steps: 64 | - uses: actions/checkout@v4 65 | 66 | - name: Install Rust stable 67 | uses: dtolnay/rust-toolchain@stable 68 | with: 69 | toolchain: stable 70 | 71 | - name: Build 72 | run: cargo build --verbose 73 | 74 | docs: 75 | 76 | needs: [reuse, clippy-rustfmt] 77 | 78 | strategy: 79 | matrix: 80 | platform: [ubuntu-latest] 81 | 82 | runs-on: ${{ matrix.platform }} 83 | 84 | steps: 85 | - uses: actions/checkout@v4 86 | 87 | - name: Install Rust stable 88 | uses: dtolnay/rust-toolchain@stable 89 | with: 90 | toolchain: stable 91 | 92 | - name: Generate docs 93 | run: cargo doc --verbose --no-deps 94 | 95 | ################################## CODE COVERAGE LAYER ################################## 96 | 97 | code-coverage-ubuntu: 98 | needs: [build, docs] 99 | 100 | runs-on: ubuntu-latest 101 | 102 | steps: 103 | - uses: actions/checkout@v4 104 | 105 | - name: Install Rust stable 106 | uses: dtolnay/rust-toolchain@stable 107 | with: 108 | toolchain: stable 109 | 110 | - name: Install grcov 111 | env: 112 | GRCOV_LINK: https://github.com/mozilla/grcov/releases/download 113 | GRCOV_VERSION: v0.8.13 114 | run: | 115 | curl -L "$GRCOV_LINK/$GRCOV_VERSION/grcov-x86_64-unknown-linux-musl.tar.bz2" | 116 | tar xj -C $HOME/.cargo/bin 117 | 118 | - name: Install llvm-tools-preview 119 | run: | 120 | rustup component add llvm-tools-preview 121 | 122 | # Not necessary on a newly created image, but strictly advised 123 | - name: Run cargo clean 124 | run: | 125 | cargo clean 126 | 127 | - name: Run tests 128 | env: 129 | RUSTFLAGS: "-Cinstrument-coverage" 130 | LLVM_PROFILE_FILE: "cookie-factory-%p-%m.profraw" 131 | run: | 132 | cargo test --verbose 133 | 134 | - name: Get coverage data for codecov 135 | run: | 136 | grcov . --binary-path ./target/debug/ -s . -t lcov --branch \ 137 | --ignore-not-existing --ignore "/*" --ignore "../*" -o lcov.info 138 | 139 | - name: Codecov upload 140 | uses: codecov/codecov-action@v4 141 | with: 142 | files: lcov.info 143 | 144 | audit-ubuntu: 145 | runs-on: ubuntu-latest 146 | 147 | steps: 148 | - uses: actions/checkout@v4 149 | 150 | - name: Check dependencies changes 151 | uses: dorny/paths-filter@v3 152 | id: changes 153 | with: 154 | filters: | 155 | cargo: 156 | - 'Cargo.toml' 157 | 158 | - name: Run cargo-audit 159 | if: steps.changes.outputs.cargo == 'true' 160 | uses: actions-rs/audit-check@v1 161 | with: 162 | token: ${{ secrets.GITHUB_TOKEN }} 163 | 164 | deny-ubuntu: 165 | runs-on: ubuntu-latest 166 | 167 | steps: 168 | - uses: actions/checkout@v4 169 | 170 | - name: Check dependencies changes 171 | uses: dorny/paths-filter@v3 172 | id: changes 173 | with: 174 | filters: | 175 | cargo: 176 | - 'Cargo.toml' 177 | 178 | - name: Install Rust stable 179 | if: steps.changes.outputs.cargo == 'true' 180 | uses: dtolnay/rust-toolchain@stable 181 | with: 182 | toolchain: stable 183 | 184 | - name: Install cargo-deny 185 | if: steps.changes.outputs.cargo == 'true' 186 | env: 187 | DENY_LINK: https://github.com/EmbarkStudios/cargo-deny/releases/download 188 | DENY_VERSION: 0.13.7 189 | run: | 190 | curl -L "$DENY_LINK/$DENY_VERSION/cargo-deny-$DENY_VERSION-x86_64-unknown-linux-musl.tar.gz" | 191 | tar xz -C $HOME/.cargo/bin --strip-components 1 192 | 193 | - name: Run cargo-deny 194 | if: steps.changes.outputs.cargo == 'true' 195 | run: | 196 | cargo deny init 197 | cargo deny check bans 198 | # cargo deny check licenses 199 | 200 | udeps-ubuntu: 201 | runs-on: ubuntu-latest 202 | 203 | steps: 204 | - uses: actions/checkout@v4 205 | 206 | - name: Check dependencies changes 207 | uses: dorny/paths-filter@v3 208 | id: changes 209 | with: 210 | filters: | 211 | cargo: 212 | - 'Cargo.toml' 213 | 214 | - name: Install Rust nightly 215 | if: steps.changes.outputs.cargo == 'true' 216 | uses: dtolnay/rust-toolchain@nightly 217 | with: 218 | toolchain: nightly 219 | 220 | - name: Install cargo-udeps 221 | if: steps.changes.outputs.cargo == 'true' 222 | env: 223 | UDEPS_LINK: https://github.com/est31/cargo-udeps/releases/download 224 | UDEPS_VERSION: v0.1.35 225 | run: | 226 | curl -L "$UDEPS_LINK/$UDEPS_VERSION/cargo-udeps-$UDEPS_VERSION-x86_64-unknown-linux-gnu.tar.gz" | 227 | tar xz -C $HOME/.cargo/bin --strip-components 2 228 | 229 | - name: Run cargo-udeps 230 | if: steps.changes.outputs.cargo == 'true' 231 | run: | 232 | cargo +nightly udeps --all-targets 233 | 234 | careful-ubuntu: 235 | runs-on: ubuntu-latest 236 | 237 | steps: 238 | - uses: actions/checkout@v4 239 | 240 | - name: Install Rust nightly 241 | uses: dtolnay/rust-toolchain@nightly 242 | with: 243 | toolchain: nightly 244 | components: rust-src 245 | 246 | - name: Install cargo-careful 247 | env: 248 | CAREFUL_LINK: https://github.com/RalfJung/cargo-careful/releases/download 249 | CAREFUL_VERSION: 0.3.4 250 | run: | 251 | curl -L "$CAREFUL_LINK/v$CAREFUL_VERSION/cargo-careful.x86_64-unknown-linux-musl" \ 252 | --output $HOME/.cargo/bin/cargo-careful 253 | chmod +x $HOME/.cargo/bin/cargo-careful 254 | 255 | - name: Run cargo-careful 256 | run: | 257 | cargo +nightly careful test 258 | # cargo +nightly careful run 259 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.reuse/dep5: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: cookie-factory 3 | Upstream-Contact: Geoffroy Couprie 4 | Source: https://github.com/rust-bakery/cookie-factory 5 | 6 | Files: * 7 | Copyright: 2017-2023 Geoffroy Couprie 8 | License: MIT 9 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cookie-factory" 3 | version = "0.3.3" 4 | authors = ["Geoffroy Couprie ", "Pierre Chifflier "] 5 | license = "MIT" 6 | repository = "https://github.com/rust-bakery/cookie-factory" 7 | readme = "README.md" 8 | documentation = "http://docs.rs/cookie-factory" 9 | description = "nom inspired serialization library" 10 | categories = ["encoding"] 11 | keywords = ["encoding", "serialization", "nom"] 12 | edition = "2018" 13 | 14 | include = [ 15 | "Cargo.toml", 16 | "LICENSE", 17 | "README.md", 18 | ".gitignore", 19 | "src/*.rs", 20 | "src/combinator/*.rs", 21 | "example/*.rs" 22 | ] 23 | 24 | [features] 25 | default = ["std", "async"] 26 | std = [] 27 | async = ["futures-io", "futures-util"] 28 | 29 | [dev-dependencies] 30 | async-std = { version = "1.9.0", features = ["attributes"] } 31 | maplit = "^1.0" 32 | 33 | [profile.bench] 34 | debug = true 35 | lto = true 36 | codegen-units = 1 37 | 38 | #[[example]] 39 | #name = "cursor" 40 | #required-features = ["std"] 41 | #path = "cursor.rs" 42 | 43 | #[[example]] 44 | #name = "json" 45 | #path = "json.rs" 46 | 47 | [[example]] 48 | name = "http" 49 | required-features = ["std"] 50 | path = "examples/http.rs" 51 | 52 | [[example]] 53 | name = "async_http" 54 | required-features = ["std", "async"] 55 | path = "examples/async_http.rs" 56 | 57 | [[example]] 58 | name = "cursor" 59 | required-features = ["std"] 60 | path = "examples/cursor.rs" 61 | 62 | [[test]] 63 | name = "http" 64 | required-features = ["std"] 65 | path = "tests/http.rs" 66 | 67 | #[[test]] 68 | #name = "pouet" 69 | #path = "pouet.rs" 70 | 71 | #[[test]] 72 | #name = "combinators-json" 73 | #path = "combinators-json.rs" 74 | 75 | [badges] 76 | travis-ci = { repository = "Geal/cookie-factory" } 77 | maintenance = { status = "actively-developed" } 78 | 79 | [dependencies] 80 | futures-io = { version = "0.3.30", optional = true } 81 | futures-util = { version = "0.3.30", optional = true, features = ["io"]} 82 | -------------------------------------------------------------------------------- /LICENSES/MIT.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-2023 Geoffroy Couprie 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cookie-factory 2 | 3 | [![Crates.io Version][crates.io badge]][crate] 4 | [![docs.rs][docs.rs badge]][docs] 5 | [![Actions Status][actions badge]][actions] 6 | [![CodeCov][codecov badge]][codecov] 7 | [![LICENSE][license badge]][license] 8 | 9 | serialization library built with a combinator design similar to the [nom parser combinators library](https://github.com/geal/nom). 10 | 11 | Serializers are built up from single purpose serializers, like `slice` 12 | to write a raw byte slice, or `be_u16` to write a `u16` integer in big 13 | endian form. 14 | 15 | Those small serializers can then be assembled by using combinators. 16 | As an example, `all(["abcd", "efgh", "ijkl"].iter().map(string))(output)` 17 | will write `"abcdefghijkl"` to `output`. 18 | 19 | 20 | 21 | [crate]: https://crates.io/crates/cookie-factory 22 | [docs]: https://docs.rs/cookie-factory/ 23 | [actions]: https://github.com/rust-bakery/cookie-factory/actions 24 | [codecov]: https://codecov.io/gh/rust-bakery/cookie-factory 25 | [license]: LICENSES/MIT.txt 26 | 27 | 28 | 29 | [crates.io badge]: https://img.shields.io/crates/v/cookie-factory.svg 30 | [docs.rs badge]: https://img.shields.io/docsrs/cookie-factory 31 | [actions badge]: https://github.com/rust-bakery/cookie-factory/workflows/cookie-factory/badge.svg 32 | [codecov badge]: https://codecov.io/gh/rust-bakery/cookie-factory/branch/master/graph/badge.svg 33 | [license badge]: https://img.shields.io/badge/license-MIT-blue.svg 34 | -------------------------------------------------------------------------------- /benches/http.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | extern crate cookie_factory; 3 | extern crate test; 4 | 5 | use std::iter::repeat; 6 | 7 | use test::Bencher; 8 | 9 | #[path = "../tests/http/mod.rs"] 10 | mod implementation; 11 | use crate::implementation::*; 12 | 13 | #[cfg(test)] 14 | mod tests { 15 | use super::*; 16 | use std::str::from_utf8; 17 | 18 | #[test] 19 | fn macros() { 20 | let mut mem: [u8; 1024] = [0; 1024]; 21 | let s = &mut mem[..]; 22 | 23 | let request = Request { 24 | method: "GET", 25 | uri: "/hello/test/a/b/c?name=value#hash", 26 | headers: [ 27 | Header { 28 | name: "Host", 29 | value: "lolcatho.st", 30 | }, 31 | Header { 32 | name: "User-agent", 33 | value: "cookie-factory", 34 | }, 35 | Header { 36 | name: "Content-Length", 37 | value: "13", 38 | }, 39 | Header { 40 | name: "Connection", 41 | value: "Close", 42 | }, 43 | ] 44 | .iter() 45 | .cloned() 46 | .collect(), 47 | body: b"Hello, world!", 48 | }; 49 | 50 | let (_, index) = cf_request((s, 0), &request).unwrap(); 51 | println!( 52 | "request written by cf:\n{}", 53 | from_utf8(&s[..index]).unwrap() 54 | ); 55 | } 56 | } 57 | 58 | mod macros { 59 | use super::*; 60 | use std::str; 61 | 62 | #[bench] 63 | fn http(b: &mut Bencher) { 64 | let request = Request { 65 | method: "GET", 66 | uri: "/hello/test/a/b/c?name=value#hash", 67 | headers: [ 68 | Header { 69 | name: "Host", 70 | value: "lolcatho.st", 71 | }, 72 | Header { 73 | name: "User-agent", 74 | value: "cookie-factory", 75 | }, 76 | Header { 77 | name: "Content-Length", 78 | value: "13", 79 | }, 80 | Header { 81 | name: "Connection", 82 | value: "Close", 83 | }, 84 | ] 85 | .iter() 86 | .cloned() 87 | .collect(), 88 | body: b"Hello, world!", 89 | }; 90 | 91 | let mut buffer = repeat(0).take(16384).collect::>(); 92 | let index = { 93 | let (buf, index) = cf_request((&mut buffer[..], 0), &request).unwrap(); 94 | 95 | println!("result:\n{}", str::from_utf8(buf).unwrap()); 96 | 97 | index as u64 98 | }; 99 | 100 | println!("wrote {} bytes", index); 101 | b.bytes = index; 102 | b.iter(|| { 103 | let res = cf_request((&mut buffer, 0), &request).unwrap(); 104 | assert_eq!(res.1 as u64, index); 105 | }); 106 | } 107 | } 108 | 109 | mod functions { 110 | use super::*; 111 | use cookie_factory::{gen, gen_simple}; 112 | 113 | #[bench] 114 | fn http(b: &mut Bencher) { 115 | let request = Request { 116 | method: "GET", 117 | uri: "/hello/test/a/b/c?name=value#hash", 118 | headers: [ 119 | Header { 120 | name: "Host", 121 | value: "lolcatho.st", 122 | }, 123 | Header { 124 | name: "User-agent", 125 | value: "cookie-factory", 126 | }, 127 | Header { 128 | name: "Content-Length", 129 | value: "13", 130 | }, 131 | Header { 132 | name: "Connection", 133 | value: "Close", 134 | }, 135 | ] 136 | .iter() 137 | .cloned() 138 | .collect(), 139 | body: b"Hello, world!", 140 | }; 141 | 142 | let mut buffer = repeat(0).take(16384).collect::>(); 143 | let index = { 144 | let sr = fn_request(&request); 145 | let (_, pos) = gen(sr, &mut buffer[..]).unwrap(); 146 | 147 | //println!("result:\n{}", str::from_utf8(buf).unwrap()); 148 | 149 | pos as usize 150 | }; 151 | 152 | println!("wrote {} bytes", index); 153 | b.bytes = index as u64; 154 | b.iter(|| { 155 | let sr = fn_request(&request); 156 | let _ = gen_simple(sr, &mut buffer[..]).unwrap(); 157 | }); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /benches/json-functions.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | extern crate cookie_factory; 3 | extern crate test; 4 | #[macro_use] 5 | extern crate maplit; 6 | 7 | use std::collections::BTreeMap; 8 | use std::io::Write; 9 | use std::str; 10 | use test::Bencher; 11 | 12 | use cookie_factory::*; 13 | use cookie_factory::{combinator::string, multi::separated_list}; 14 | 15 | #[derive(Clone, Debug, PartialEq)] 16 | pub enum JsonValue { 17 | Str(String), 18 | Boolean(bool), 19 | Num(f64), 20 | Array(Vec), 21 | Object(BTreeMap), 22 | } 23 | 24 | #[inline(always)] 25 | pub fn gen_str<'a, 'b: 'a, W: Write>(s: &'b str) -> impl SerializeFn + 'a { 26 | move |out: WriteContext| { 27 | let out = string("\"")(out)?; 28 | let out = string(s)(out)?; 29 | string("\"")(out) 30 | } 31 | } 32 | 33 | #[inline(always)] 34 | pub fn gen_bool<'a, W: Write>(b: bool) -> impl SerializeFn { 35 | if b { 36 | string("true") 37 | } else { 38 | string("false") 39 | } 40 | } 41 | 42 | #[inline(always)] 43 | pub fn gen_num<'a, W: Write>(_b: f64) -> impl SerializeFn { 44 | /*move |out: &'a mut [u8]| { 45 | let s = format!("{}", b); 46 | string(s)(out) 47 | }*/ 48 | string("1234.56") 49 | } 50 | 51 | pub fn gen_array<'a, 'b: 'a, W: Write>(arr: &'b [JsonValue]) -> impl SerializeFn + 'a { 52 | move |out: WriteContext| { 53 | let out = string("[")(out)?; 54 | let out = separated_list(string(","), arr.iter().map(gen_json_value))(out)?; 55 | string("]")(out) 56 | } 57 | } 58 | 59 | pub fn gen_key_value<'a, 'b: 'a, W: Write>( 60 | kv: (&'b String, &'b JsonValue), 61 | ) -> impl SerializeFn + 'a { 62 | move |out: WriteContext| { 63 | let out = gen_str(kv.0)(out)?; 64 | let out = string(":")(out)?; 65 | gen_json_value(&kv.1)(out) 66 | } 67 | } 68 | 69 | pub fn gen_object<'a, 'b: 'a, W: Write>( 70 | o: &'b BTreeMap, 71 | ) -> impl SerializeFn + 'a { 72 | move |out: WriteContext| { 73 | let out = string("{")(out)?; 74 | 75 | let out = separated_list(string(","), o.iter().map(gen_key_value))(out)?; 76 | string("}")(out) 77 | } 78 | } 79 | 80 | pub fn gen_json_value<'a, W: Write>(g: &'a JsonValue) -> impl SerializeFn + 'a { 81 | move |out: WriteContext| match g { 82 | JsonValue::Str(ref s) => gen_str(s)(out), 83 | JsonValue::Boolean(ref b) => gen_bool(*b)(out), 84 | JsonValue::Num(ref n) => gen_num(*n)(out), 85 | JsonValue::Array(ref v) => gen_array(v)(out), 86 | JsonValue::Object(ref o) => gen_object(o)(out), 87 | } 88 | } 89 | 90 | use std::iter::repeat; 91 | 92 | #[test] 93 | fn json_test() { 94 | let value = JsonValue::Object(btreemap! { 95 | String::from("arr") => JsonValue::Array(vec![JsonValue::Num(1.0), JsonValue::Num(12.3), JsonValue::Num(42.0)]), 96 | String::from("b") => JsonValue::Boolean(true), 97 | String::from("o") => JsonValue::Object(btreemap!{ 98 | String::from("x") => JsonValue::Str(String::from("abcd")), 99 | String::from("y") => JsonValue::Str(String::from("efgh")), 100 | String::from("empty") => JsonValue::Array(vec![]), 101 | }), 102 | }); 103 | 104 | let mut buffer = repeat(0).take(16384).collect::>(); 105 | let index = { 106 | let sr = gen_json_value(&value); 107 | 108 | let (_, res) = gen(sr, &mut buffer[..]).unwrap(); 109 | res as usize 110 | }; 111 | 112 | println!("result:\n{}", str::from_utf8(&buffer[..index]).unwrap()); 113 | assert_eq!(str::from_utf8(&buffer[..index]).unwrap(), 114 | "{\"arr\":[1234.56,1234.56,1234.56],\"b\":true,\"o\":{\"empty\":[],\"x\":\"abcd\",\"y\":\"efgh\"}}"); 115 | panic!(); 116 | } 117 | 118 | #[bench] 119 | fn functions_json(b: &mut Bencher) { 120 | let element = JsonValue::Object(btreemap! { 121 | String::from("arr") => JsonValue::Array(vec![JsonValue::Num(1.0), JsonValue::Num(12.3), JsonValue::Num(42.0)]), 122 | String::from("b") => JsonValue::Boolean(true), 123 | String::from("o") => JsonValue::Object(btreemap!{ 124 | String::from("x") => JsonValue::Str(String::from("abcd")), 125 | String::from("y") => JsonValue::Str(String::from("efgh")), 126 | String::from("empty") => JsonValue::Array(vec![]), 127 | }), 128 | }); 129 | 130 | let value = JsonValue::Array(repeat(element).take(10).collect::>()); 131 | 132 | let mut buffer = repeat(0u8).take(16384).collect::>(); 133 | let index = { 134 | let sr = gen_json_value(&value); 135 | 136 | let (_, res) = gen(sr, &mut buffer[..]).unwrap(); 137 | res as usize 138 | }; 139 | 140 | b.bytes = index as u64; 141 | b.iter(|| { 142 | let sr = gen_json_value(&value); 143 | let _ = gen_simple(sr, &mut buffer[..]).unwrap(); 144 | }); 145 | } 146 | 147 | #[bench] 148 | fn functions_gen_str(b: &mut Bencher) { 149 | let mut buffer = repeat(0).take(16384).collect::>(); 150 | 151 | let index = { 152 | let sr = gen_str(&"hello"); 153 | 154 | let (_, res) = gen(sr, &mut buffer[..]).unwrap(); 155 | res as usize 156 | }; 157 | 158 | b.bytes = index as u64; 159 | b.iter(|| { 160 | let sr = gen_str(&"hello"); 161 | let _ = gen_simple(sr, &mut buffer[..]).unwrap(); 162 | }); 163 | } 164 | -------------------------------------------------------------------------------- /benches/json-macros.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | extern crate test; 3 | #[macro_use] 4 | extern crate cookie_factory; 5 | #[macro_use] 6 | extern crate maplit; 7 | 8 | use std::collections::BTreeMap; 9 | use std::iter::repeat; 10 | use std::str; 11 | 12 | use cookie_factory::*; 13 | use test::Bencher; 14 | 15 | #[derive(Clone, Debug, PartialEq)] 16 | pub enum JsonValue { 17 | Str(String), 18 | Boolean(bool), 19 | Num(f64), 20 | Array(Vec), 21 | Object(BTreeMap), 22 | } 23 | 24 | pub fn gen_json_value<'a>( 25 | x: (&'a mut [u8], usize), 26 | g: &JsonValue, 27 | ) -> Result<(&'a mut [u8], usize), GenError> { 28 | match g { 29 | JsonValue::Str(ref s) => gen_str(x, s), 30 | JsonValue::Boolean(b) => gen_bool(x, b), 31 | JsonValue::Num(ref f) => gen_num(x, f), 32 | JsonValue::Array(ref v) => gen_array(x, v), 33 | JsonValue::Object(ref o) => gen_object(x, o), 34 | } 35 | } 36 | 37 | pub fn gen_str<'a>( 38 | x: (&'a mut [u8], usize), 39 | s: &String, 40 | ) -> Result<(&'a mut [u8], usize), GenError> { 41 | do_gen!( 42 | x, 43 | gen_slice!(&b"\""[..]) >> gen_slice!(s.as_bytes()) >> gen_slice!(&b"\""[..]) 44 | ) 45 | } 46 | 47 | pub fn gen_bool<'a>(x: (&'a mut [u8], usize), b: &bool) -> Result<(&'a mut [u8], usize), GenError> { 48 | let sl = match b { 49 | true => &b"true"[..], 50 | false => &b"false"[..], 51 | }; 52 | 53 | gen_slice!(x, sl) 54 | } 55 | 56 | pub fn gen_num<'a>(x: (&'a mut [u8], usize), _b: &f64) -> Result<(&'a mut [u8], usize), GenError> { 57 | //TODO 58 | gen_slice!(x, &b"1234.56"[..]) 59 | } 60 | 61 | pub fn gen_array<'a>( 62 | x: (&'a mut [u8], usize), 63 | arr: &[JsonValue], 64 | ) -> Result<(&'a mut [u8], usize), GenError> { 65 | let mut output = gen_slice!(x, &b"["[..])?; 66 | if arr.len() > 0 { 67 | output = gen_json_value(output, &arr[0])?; 68 | 69 | if arr.len() > 1 { 70 | output = gen_many!((output.0, output.1), &arr[1..], gen_array_element)?; 71 | } 72 | } 73 | gen_slice!(output, &b"]"[..]) 74 | } 75 | 76 | pub fn gen_array_element<'a>( 77 | x: (&'a mut [u8], usize), 78 | val: &JsonValue, 79 | ) -> Result<(&'a mut [u8], usize), GenError> { 80 | do_gen!(x, gen_slice!(&b","[..]) >> gen_call!(gen_json_value, val)) 81 | } 82 | 83 | pub fn gen_object<'a>( 84 | x: (&'a mut [u8], usize), 85 | o: &BTreeMap, 86 | ) -> Result<(&'a mut [u8], usize), GenError> { 87 | let mut output = gen_slice!(x, &b"{"[..])?; 88 | let mut it = o.iter(); 89 | 90 | if let Some((key, value)) = it.next() { 91 | output = gen_object_element(output, key, value, true)?; 92 | } 93 | 94 | for (key, value) in it { 95 | output = gen_object_element(output, key, value, false)?; 96 | } 97 | 98 | gen_slice!(output, &b"]"[..]) 99 | } 100 | 101 | pub fn gen_object_element<'a>( 102 | x: (&'a mut [u8], usize), 103 | key: &String, 104 | value: &JsonValue, 105 | is_first: bool, 106 | ) -> Result<(&'a mut [u8], usize), GenError> { 107 | let mut output = x; 108 | if !is_first { 109 | output = gen_slice!(output, &b","[..])?; 110 | } 111 | 112 | do_gen!( 113 | output, 114 | gen_call!(gen_str, key) >> gen_slice!(&b":"[..]) >> gen_call!(gen_json_value, value) 115 | ) 116 | } 117 | 118 | #[bench] 119 | fn macros_json(b: &mut Bencher) { 120 | let element = JsonValue::Object(btreemap! { 121 | String::from("arr") => JsonValue::Array(vec![JsonValue::Num(1.0), JsonValue::Num(12.3), JsonValue::Num(42.0)]), 122 | String::from("b") => JsonValue::Boolean(true), 123 | String::from("o") => JsonValue::Object(btreemap!{ 124 | String::from("x") => JsonValue::Str(String::from("abcd")), 125 | String::from("y") => JsonValue::Str(String::from("efgh")), 126 | String::from("empty") => JsonValue::Array(vec![]), 127 | }), 128 | }); 129 | 130 | let value = JsonValue::Array(repeat(element).take(10).collect::>()); 131 | 132 | let mut buffer = repeat(0).take(16384).collect::>(); 133 | let index = { 134 | let (buf, index) = gen_json_value((&mut buffer, 0), &value).unwrap(); 135 | 136 | println!("result:\n{}", str::from_utf8(buf).unwrap()); 137 | //panic!(); 138 | 139 | index as u64 140 | }; 141 | 142 | println!("wrote {} bytes", index); 143 | b.bytes = index; 144 | b.iter(|| { 145 | let res = gen_json_value((&mut buffer, 0), &value).unwrap(); 146 | res.1 147 | }); 148 | } 149 | 150 | #[bench] 151 | fn macros_gen_str(b: &mut Bencher) { 152 | let value = String::from("hello"); 153 | let mut buffer = repeat(0).take(16384).collect::>(); 154 | let index = { 155 | let (buf, index) = gen_str((&mut buffer, 0), &value).unwrap(); 156 | 157 | println!("result:\n{}", str::from_utf8(buf).unwrap()); 158 | //panic!(); 159 | 160 | index as u64 161 | }; 162 | 163 | println!("wrote {} bytes", index); 164 | b.bytes = index; 165 | b.iter(|| { 166 | let res = gen_str((&mut buffer, 0), &value).unwrap(); 167 | res.1 168 | }); 169 | } 170 | -------------------------------------------------------------------------------- /examples/async_http.rs: -------------------------------------------------------------------------------- 1 | extern crate cookie_factory; 2 | 3 | #[path = "../tests/http/mod.rs"] 4 | mod implementation; 5 | use crate::implementation::*; 6 | use async_std::net::TcpStream; 7 | 8 | #[async_std::main] 9 | async fn main() { 10 | use cookie_factory::async_bufwriter::{gen, AsyncBufWriter}; 11 | 12 | let socket = TcpStream::connect("127.0.0.1:8080").await.unwrap(); 13 | let mut stream = AsyncBufWriter::new(socket); 14 | 15 | let request = Request { 16 | method: "GET", 17 | uri: "/hello/test/a/b/c?name=value#hash", 18 | headers: [ 19 | Header { 20 | name: "Host", 21 | value: "lolcatho.st", 22 | }, 23 | Header { 24 | name: "User-agent", 25 | value: "cookie-factory", 26 | }, 27 | Header { 28 | name: "Content-Length", 29 | value: "13", 30 | }, 31 | Header { 32 | name: "Connection", 33 | value: "Close", 34 | }, 35 | ] 36 | .iter() 37 | .cloned() 38 | .collect(), 39 | body: b"Hello, world!", 40 | }; 41 | 42 | let sr = fn_request(&request); 43 | let (stream, size) = gen(sr, stream).await.unwrap(); 44 | 45 | println!( 46 | "wrote:\n{} bytes (remaining in buffer: {})", 47 | size, 48 | stream.remaining() 49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /examples/cursor.rs: -------------------------------------------------------------------------------- 1 | extern crate cookie_factory; 2 | 3 | use cookie_factory::{combinator::string, gen, gen_simple, sequence::pair, SerializeFn}; 4 | use std::io::{sink, Write}; 5 | 6 | fn serializer() -> impl SerializeFn { 7 | pair(string("Hello "), string("World!")) 8 | } 9 | 10 | fn main() { 11 | let s = { 12 | let c = sink(); 13 | let ser = serializer(); 14 | match gen(ser, c) { 15 | Err(e) => { 16 | panic!("error calculating the length to serialize: {:?}", e); 17 | } 18 | Ok((_, pos)) => pos as usize, 19 | } 20 | }; 21 | 22 | println!("length: {}", s); 23 | 24 | let v = { 25 | let v = Vec::with_capacity(s); 26 | let ser = serializer(); 27 | match gen_simple(ser, v) { 28 | Err(e) => { 29 | panic!("error serializing: {:?}", e); 30 | } 31 | Ok(v) => { 32 | assert_eq!(v.len(), s); 33 | v 34 | } 35 | } 36 | }; 37 | 38 | println!("wrote '{}'", std::str::from_utf8(&v[..s]).unwrap()); 39 | } 40 | -------------------------------------------------------------------------------- /examples/http.rs: -------------------------------------------------------------------------------- 1 | extern crate cookie_factory; 2 | 3 | #[path = "../tests/http/mod.rs"] 4 | mod implementation; 5 | use crate::implementation::*; 6 | 7 | fn main() { 8 | use cookie_factory::gen; 9 | let request = Request { 10 | method: "GET", 11 | uri: "/hello/test/a/b/c?name=value#hash", 12 | headers: [ 13 | Header { 14 | name: "Host", 15 | value: "lolcatho.st", 16 | }, 17 | Header { 18 | name: "User-agent", 19 | value: "cookie-factory", 20 | }, 21 | Header { 22 | name: "Content-Length", 23 | value: "13", 24 | }, 25 | Header { 26 | name: "Connection", 27 | value: "Close", 28 | }, 29 | ] 30 | .iter() 31 | .cloned() 32 | .collect(), 33 | body: b"Hello, world!", 34 | }; 35 | 36 | let sr = fn_request(&request); 37 | let writer = vec![]; 38 | let (buffer, size) = gen(sr, writer).unwrap(); 39 | 40 | println!( 41 | "result:\n{}", 42 | std::str::from_utf8(&buffer[..(size as usize)]).unwrap() 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /examples/json.rs: -------------------------------------------------------------------------------- 1 | extern crate cookie_factory; 2 | #[macro_use] 3 | extern crate maplit; 4 | 5 | use std::{iter::repeat, str}; 6 | 7 | #[path = "../tests/json/mod.rs"] 8 | mod implementation; 9 | use crate::implementation::*; 10 | 11 | fn main() { 12 | use cookie_factory::gen_simple; 13 | use cookie_factory::lib::std::io::Cursor; 14 | 15 | let element = JsonValue::Object(btreemap! { 16 | String::from("arr") => JsonValue::Array(vec![JsonValue::Num(1.0), JsonValue::Num(12.3), JsonValue::Num(42.0)]), 17 | String::from("b") => JsonValue::Boolean(true), 18 | String::from("o") => JsonValue::Object(btreemap!{ 19 | String::from("x") => JsonValue::Str(String::from("abcd")), 20 | String::from("y") => JsonValue::Str(String::from("efgh")), 21 | String::from("empty") => JsonValue::Array(vec![]), 22 | }), 23 | }); 24 | 25 | let value = JsonValue::Array(repeat(element).take(10).collect::>()); 26 | 27 | let mut buffer = [0u8; 8192]; 28 | let sr = gen_json_value(&value); 29 | let writer = Cursor::new(&mut buffer[..]); 30 | let writer = gen_simple(sr, writer).unwrap(); 31 | let size = writer.position() as usize; 32 | let buffer = writer.into_inner(); 33 | 34 | println!("result:\n{}", str::from_utf8(&buffer[..size]).unwrap()); 35 | } 36 | -------------------------------------------------------------------------------- /src/async_bufwriter.rs: -------------------------------------------------------------------------------- 1 | use futures_io::AsyncWrite; 2 | use futures_util::io::AsyncWriteExt; 3 | 4 | pub struct AsyncBufWriter { 5 | inner: W, 6 | buf: Vec, 7 | pos: usize, 8 | } 9 | 10 | const DEFAULT_BUF_SIZE: usize = 8 * 1024; 11 | 12 | impl AsyncBufWriter { 13 | pub fn new(inner: W) -> AsyncBufWriter { 14 | AsyncBufWriter::with_capacity(DEFAULT_BUF_SIZE, inner) 15 | } 16 | 17 | pub fn with_capacity(capacity: usize, inner: W) -> AsyncBufWriter { 18 | let buf = vec![0; capacity]; 19 | AsyncBufWriter { inner, buf, pos: 0 } 20 | } 21 | 22 | pub async fn flush(&mut self) -> std::io::Result { 23 | let sz = self.inner.write(&self.buf[..self.pos]).await?; 24 | 25 | // if we did not write everything, move data to the beginning of the 26 | // buffer 27 | if sz < self.pos { 28 | for i in 0..(self.pos - sz) { 29 | self.buf[i] = self.buf[sz + i] 30 | } 31 | } 32 | 33 | self.pos -= sz; 34 | 35 | Ok(sz) 36 | } 37 | 38 | pub fn remaining(&self) -> usize { 39 | self.pos 40 | } 41 | 42 | pub fn into_parts(mut self) -> (W, Vec) { 43 | self.buf.truncate(self.pos); 44 | (self.inner, self.buf) 45 | } 46 | } 47 | 48 | impl std::io::Write for AsyncBufWriter { 49 | fn write(&mut self, buf: &[u8]) -> std::io::Result { 50 | let sz = (&mut self.buf[self.pos..]).write(buf)?; 51 | self.pos += sz; 52 | Ok(sz) 53 | } 54 | 55 | fn flush(&mut self) -> std::io::Result<()> { 56 | Ok(()) 57 | } 58 | } 59 | 60 | pub async fn gen>>( 61 | f: F, 62 | w: AsyncBufWriter, 63 | ) -> Result<(AsyncBufWriter, u64), crate::internal::GenError> { 64 | match f(crate::internal::WriteContext::from(w)).map(|ctx| ctx.into_inner()) { 65 | Err(e) => Err(e), 66 | Ok((mut w, _)) => { 67 | let sz = w.flush().await?; 68 | Ok((w, sz as u64)) 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/bytes.rs: -------------------------------------------------------------------------------- 1 | //! bytes and numbers related serialization functions 2 | use crate::internal::{GenError, SerializeFn, WriteContext}; 3 | use crate::lib::std::io::Write; 4 | 5 | macro_rules! try_write(($out:ident, $len:ident, $data:expr) => ( 6 | match $out.write($data) { 7 | Err(io) => Err(GenError::IoError(io)), 8 | Ok(n) if n < $len => Err(GenError::BufferTooSmall($len - n)), 9 | Ok(_) => Ok($out) 10 | } 11 | )); 12 | 13 | /// Writes an `u8` to the output 14 | /// 15 | /// ```rust 16 | /// use cookie_factory::{gen, bytes::be_u8}; 17 | /// 18 | /// let mut buf = [0u8; 100]; 19 | /// 20 | /// { 21 | /// let (buf, pos) = gen(be_u8(1u8), &mut buf[..]).unwrap(); 22 | /// assert_eq!(pos, 1); 23 | /// assert_eq!(buf.len(), 100 - 1); 24 | /// } 25 | /// 26 | /// assert_eq!(&buf[..1], &[1u8][..]); 27 | /// ``` 28 | pub fn be_u8(i: u8) -> impl SerializeFn { 29 | let len = 1; 30 | 31 | move |mut out: WriteContext| try_write!(out, len, &i.to_be_bytes()[..]) 32 | } 33 | 34 | /// Writes an `u16` in big endian byte order to the output 35 | /// 36 | /// ```rust 37 | /// use cookie_factory::{gen, bytes::be_u16}; 38 | /// 39 | /// let mut buf = [0u8; 100]; 40 | /// 41 | /// { 42 | /// let (buf, pos) = gen(be_u16(1u16), &mut buf[..]).unwrap(); 43 | /// assert_eq!(pos, 2); 44 | /// assert_eq!(buf.len(), 100 - 2); 45 | /// } 46 | /// 47 | /// assert_eq!(&buf[..2], &[0u8, 1u8][..]); 48 | /// ``` 49 | pub fn be_u16(i: u16) -> impl SerializeFn { 50 | let len = 2; 51 | 52 | move |mut out: WriteContext| try_write!(out, len, &i.to_be_bytes()[..]) 53 | } 54 | 55 | /// Writes the lower 24 bit of an `u32` in big endian byte order to the output 56 | /// 57 | /// ```rust 58 | /// use cookie_factory::{gen, bytes::be_u24}; 59 | /// 60 | /// let mut buf = [0u8; 100]; 61 | /// 62 | /// { 63 | /// let (buf, pos) = gen(be_u24(1u32), &mut buf[..]).unwrap(); 64 | /// assert_eq!(pos, 3); 65 | /// assert_eq!(buf.len(), 100 - 3); 66 | /// } 67 | /// 68 | /// assert_eq!(&buf[..3], &[0u8, 0u8, 1u8][..]); 69 | /// ``` 70 | pub fn be_u24(i: u32) -> impl SerializeFn { 71 | let len = 3; 72 | 73 | move |mut out: WriteContext| try_write!(out, len, &i.to_be_bytes()[1..]) 74 | } 75 | 76 | /// Writes an `u32` in big endian byte order to the output 77 | /// 78 | /// ```rust 79 | /// use cookie_factory::{gen, bytes::be_u32}; 80 | /// 81 | /// let mut buf = [0u8; 100]; 82 | /// 83 | /// { 84 | /// let (buf, pos) = gen(be_u32(1u32), &mut buf[..]).unwrap(); 85 | /// assert_eq!(pos, 4); 86 | /// assert_eq!(buf.len(), 100 - 4); 87 | /// } 88 | /// 89 | /// assert_eq!(&buf[..4], &[0u8, 0u8, 0u8, 1u8][..]); 90 | /// ``` 91 | pub fn be_u32(i: u32) -> impl SerializeFn { 92 | let len = 4; 93 | 94 | move |mut out: WriteContext| try_write!(out, len, &i.to_be_bytes()[..]) 95 | } 96 | 97 | /// Writes an `u64` in big endian byte order to the output 98 | /// 99 | /// ```rust 100 | /// use cookie_factory::{gen, bytes::be_u64}; 101 | /// 102 | /// let mut buf = [0u8; 100]; 103 | /// 104 | /// { 105 | /// let (buf, pos) = gen(be_u64(1u64), &mut buf[..]).unwrap(); 106 | /// assert_eq!(pos, 8); 107 | /// assert_eq!(buf.len(), 100 - 8); 108 | /// } 109 | /// 110 | /// assert_eq!(&buf[..8], &[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8][..]); 111 | /// ``` 112 | pub fn be_u64(i: u64) -> impl SerializeFn { 113 | let len = 8; 114 | 115 | move |mut out: WriteContext| try_write!(out, len, &i.to_be_bytes()[..]) 116 | } 117 | 118 | /// Writes an `i8` to the output 119 | /// 120 | /// ```rust 121 | /// use cookie_factory::{gen, bytes::be_i8}; 122 | /// 123 | /// let mut buf = [0u8; 100]; 124 | /// 125 | /// { 126 | /// let (buf, pos) = gen(be_i8(1i8), &mut buf[..]).unwrap(); 127 | /// assert_eq!(pos, 1); 128 | /// assert_eq!(buf.len(), 100 - 1); 129 | /// } 130 | /// 131 | /// assert_eq!(&buf[..1], &[1u8][..]); 132 | /// ``` 133 | pub fn be_i8(i: i8) -> impl SerializeFn { 134 | be_u8(i as u8) 135 | } 136 | 137 | /// Writes an `i16` in big endian byte order to the output 138 | /// 139 | /// ```rust 140 | /// use cookie_factory::{gen, bytes::be_i16}; 141 | /// 142 | /// let mut buf = [0u8; 100]; 143 | /// 144 | /// { 145 | /// let (buf, pos) = gen(be_i16(1i16), &mut buf[..]).unwrap(); 146 | /// assert_eq!(pos, 2); 147 | /// assert_eq!(buf.len(), 100 - 2); 148 | /// } 149 | /// 150 | /// assert_eq!(&buf[..2], &[0u8, 1u8][..]); 151 | /// ``` 152 | pub fn be_i16(i: i16) -> impl SerializeFn { 153 | be_u16(i as u16) 154 | } 155 | 156 | /// Writes the lower 24 bit of an `i32` in big endian byte order to the output 157 | /// 158 | /// ```rust 159 | /// use cookie_factory::{gen, bytes::be_i24}; 160 | /// 161 | /// let mut buf = [0u8; 100]; 162 | /// 163 | /// { 164 | /// let (buf, pos) = gen(be_i24(1i32), &mut buf[..]).unwrap(); 165 | /// assert_eq!(pos, 3); 166 | /// assert_eq!(buf.len(), 100 - 3); 167 | /// } 168 | /// 169 | /// assert_eq!(&buf[..3], &[0u8, 0u8, 1u8][..]); 170 | /// ``` 171 | pub fn be_i24(i: i32) -> impl SerializeFn { 172 | be_u24(i as u32) 173 | } 174 | 175 | /// Writes an `i32` in big endian byte order to the output 176 | /// 177 | /// ```rust 178 | /// use cookie_factory::{gen, bytes::be_i32}; 179 | /// 180 | /// let mut buf = [0u8; 100]; 181 | /// 182 | /// { 183 | /// let (buf, pos) = gen(be_i32(1i32), &mut buf[..]).unwrap(); 184 | /// assert_eq!(pos, 4); 185 | /// assert_eq!(buf.len(), 100 - 4); 186 | /// } 187 | /// 188 | /// assert_eq!(&buf[..4], &[0u8, 0u8, 0u8, 1u8][..]); 189 | /// ``` 190 | pub fn be_i32(i: i32) -> impl SerializeFn { 191 | be_u32(i as u32) 192 | } 193 | 194 | /// Writes an `i64` in big endian byte order to the output 195 | /// 196 | /// ```rust 197 | /// use cookie_factory::{gen, bytes::be_i64}; 198 | /// 199 | /// let mut buf = [0u8; 100]; 200 | /// 201 | /// { 202 | /// let (buf, pos) = gen(be_i64(1i64), &mut buf[..]).unwrap(); 203 | /// assert_eq!(pos, 8); 204 | /// assert_eq!(buf.len(), 100 - 8); 205 | /// } 206 | /// 207 | /// assert_eq!(&buf[..8], &[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8][..]); 208 | /// ``` 209 | pub fn be_i64(i: i64) -> impl SerializeFn { 210 | be_u64(i as u64) 211 | } 212 | 213 | /// Writes an `f32` in big endian byte order to the output 214 | /// 215 | /// ```rust 216 | /// use cookie_factory::{gen, bytes::be_f32}; 217 | /// 218 | /// let mut buf = [0u8; 100]; 219 | /// 220 | /// { 221 | /// let (buf, pos) = gen(be_f32(1.0f32), &mut buf[..]).unwrap(); 222 | /// assert_eq!(pos, 4); 223 | /// assert_eq!(buf.len(), 100 - 4); 224 | /// } 225 | /// 226 | /// assert_eq!(&buf[..4], &[63u8, 128u8, 0u8, 0u8][..]); 227 | /// ``` 228 | pub fn be_f32(i: f32) -> impl SerializeFn { 229 | be_u32(i.to_bits()) 230 | } 231 | 232 | /// Writes an `f64` in big endian byte order to the output 233 | /// 234 | /// ```rust 235 | /// use cookie_factory::{gen, bytes::be_f64}; 236 | /// 237 | /// let mut buf = [0u8; 100]; 238 | /// 239 | /// { 240 | /// let (buf, pos) = gen(be_f64(1.0f64), &mut buf[..]).unwrap(); 241 | /// assert_eq!(pos, 8); 242 | /// assert_eq!(buf.len(), 100 - 8); 243 | /// } 244 | /// 245 | /// assert_eq!(&buf[..8], &[63u8, 240u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8][..]); 246 | /// ``` 247 | pub fn be_f64(i: f64) -> impl SerializeFn { 248 | be_u64(i.to_bits()) 249 | } 250 | 251 | /// Writes an `u8` to the output 252 | /// 253 | /// ```rust 254 | /// use cookie_factory::{gen, bytes::le_u8}; 255 | /// 256 | /// let mut buf = [0u8; 100]; 257 | /// 258 | /// { 259 | /// let (buf, pos) = gen(le_u8(1u8), &mut buf[..]).unwrap(); 260 | /// assert_eq!(pos, 1); 261 | /// assert_eq!(buf.len(), 100 - 1); 262 | /// } 263 | /// 264 | /// assert_eq!(&buf[..1], &[1u8][..]); 265 | /// ``` 266 | pub fn le_u8(i: u8) -> impl SerializeFn { 267 | let len = 1; 268 | 269 | move |mut out: WriteContext| try_write!(out, len, &i.to_le_bytes()[..]) 270 | } 271 | 272 | /// Writes an `u16` in little endian byte order to the output 273 | /// 274 | /// ```rust 275 | /// use cookie_factory::{gen, bytes::le_u16}; 276 | /// 277 | /// let mut buf = [0u8; 100]; 278 | /// 279 | /// { 280 | /// let (buf, pos) = gen(le_u16(1u16), &mut buf[..]).unwrap(); 281 | /// assert_eq!(pos, 2); 282 | /// assert_eq!(buf.len(), 100 - 2); 283 | /// } 284 | /// 285 | /// assert_eq!(&buf[..2], &[1u8, 0u8][..]); 286 | /// ``` 287 | pub fn le_u16(i: u16) -> impl SerializeFn { 288 | let len = 2; 289 | 290 | move |mut out: WriteContext| try_write!(out, len, &i.to_le_bytes()[..]) 291 | } 292 | 293 | /// Writes the lower 24 bit of an `u32` in little endian byte order to the output 294 | /// 295 | /// ```rust 296 | /// use cookie_factory::{gen, bytes::le_u24}; 297 | /// 298 | /// let mut buf = [0u8; 100]; 299 | /// 300 | /// { 301 | /// let (buf, pos) = gen(le_u24(1u32), &mut buf[..]).unwrap(); 302 | /// assert_eq!(pos, 3); 303 | /// assert_eq!(buf.len(), 100 - 3); 304 | /// } 305 | /// 306 | /// assert_eq!(&buf[..3], &[1u8, 0u8, 0u8][..]); 307 | /// ``` 308 | pub fn le_u24(i: u32) -> impl SerializeFn { 309 | let len = 3; 310 | 311 | move |mut out: WriteContext| try_write!(out, len, &i.to_le_bytes()[0..3]) 312 | } 313 | 314 | /// Writes an `u32` in little endian byte order to the output 315 | /// 316 | /// ```rust 317 | /// use cookie_factory::{gen, bytes::le_u32}; 318 | /// 319 | /// let mut buf = [0u8; 100]; 320 | /// 321 | /// { 322 | /// let (buf, pos) = gen(le_u32(1u32), &mut buf[..]).unwrap(); 323 | /// assert_eq!(pos, 4); 324 | /// assert_eq!(buf.len(), 100 - 4); 325 | /// } 326 | /// 327 | /// assert_eq!(&buf[..4], &[1u8, 0u8, 0u8, 0u8][..]); 328 | /// ``` 329 | pub fn le_u32(i: u32) -> impl SerializeFn { 330 | let len = 4; 331 | 332 | move |mut out: WriteContext| try_write!(out, len, &i.to_le_bytes()[..]) 333 | } 334 | 335 | /// Writes an `u64` in little endian byte order to the output 336 | /// 337 | /// ```rust 338 | /// use cookie_factory::{gen, bytes::le_u64}; 339 | /// 340 | /// let mut buf = [0u8; 100]; 341 | /// 342 | /// { 343 | /// let (buf, pos) = gen(le_u64(1u64), &mut buf[..]).unwrap(); 344 | /// assert_eq!(pos, 8); 345 | /// assert_eq!(buf.len(), 100 - 8); 346 | /// } 347 | /// 348 | /// assert_eq!(&buf[..8], &[1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8][..]); 349 | /// ``` 350 | pub fn le_u64(i: u64) -> impl SerializeFn { 351 | let len = 8; 352 | 353 | move |mut out: WriteContext| try_write!(out, len, &i.to_le_bytes()[..]) 354 | } 355 | 356 | /// Writes an `i8` to the output 357 | /// 358 | /// ```rust 359 | /// use cookie_factory::{gen, bytes::le_i8}; 360 | /// 361 | /// let mut buf = [0u8; 100]; 362 | /// 363 | /// { 364 | /// let (buf, pos) = gen(le_i8(1i8), &mut buf[..]).unwrap(); 365 | /// assert_eq!(pos, 1); 366 | /// assert_eq!(buf.len(), 100 - 1); 367 | /// } 368 | /// 369 | /// assert_eq!(&buf[..1], &[1u8][..]); 370 | /// ``` 371 | pub fn le_i8(i: i8) -> impl SerializeFn { 372 | le_u8(i as u8) 373 | } 374 | 375 | /// Writes an `o16` in little endian byte order to the output 376 | /// 377 | /// ```rust 378 | /// use cookie_factory::{gen, bytes::le_i16}; 379 | /// 380 | /// let mut buf = [0u8; 100]; 381 | /// 382 | /// { 383 | /// let (buf, pos) = gen(le_i16(1i16), &mut buf[..]).unwrap(); 384 | /// assert_eq!(pos, 2); 385 | /// assert_eq!(buf.len(), 100 - 2); 386 | /// } 387 | /// 388 | /// assert_eq!(&buf[..2], &[1u8, 0u8][..]); 389 | /// ``` 390 | pub fn le_i16(i: i16) -> impl SerializeFn { 391 | le_u16(i as u16) 392 | } 393 | 394 | /// Writes the lower 24 bit of an `i32` in little endian byte order to the output 395 | /// 396 | /// ```rust 397 | /// use cookie_factory::{gen, bytes::le_i24}; 398 | /// 399 | /// let mut buf = [0u8; 100]; 400 | /// 401 | /// { 402 | /// let (buf, pos) = gen(le_i24(1i32), &mut buf[..]).unwrap(); 403 | /// assert_eq!(pos, 3); 404 | /// assert_eq!(buf.len(), 100 - 3); 405 | /// } 406 | /// 407 | /// assert_eq!(&buf[..3], &[1u8, 0u8, 0u8][..]); 408 | /// ``` 409 | pub fn le_i24(i: i32) -> impl SerializeFn { 410 | le_u24(i as u32) 411 | } 412 | 413 | /// Writes an `i32` in little endian byte order to the output 414 | /// 415 | /// ```rust 416 | /// use cookie_factory::{gen, bytes::le_i32}; 417 | /// 418 | /// let mut buf = [0u8; 100]; 419 | /// 420 | /// { 421 | /// let (buf, pos) = gen(le_i32(1i32), &mut buf[..]).unwrap(); 422 | /// assert_eq!(pos, 4); 423 | /// assert_eq!(buf.len(), 100 - 4); 424 | /// } 425 | /// 426 | /// assert_eq!(&buf[..4], &[1u8, 0u8, 0u8, 0u8][..]); 427 | /// ``` 428 | pub fn le_i32(i: i32) -> impl SerializeFn { 429 | le_u32(i as u32) 430 | } 431 | 432 | /// Writes an `i64` in little endian byte order to the output 433 | /// 434 | /// ```rust 435 | /// use cookie_factory::{gen, bytes::le_i64}; 436 | /// 437 | /// let mut buf = [0u8; 100]; 438 | /// 439 | /// { 440 | /// let (buf, pos) = gen(le_i64(1i64), &mut buf[..]).unwrap(); 441 | /// assert_eq!(pos, 8); 442 | /// assert_eq!(buf.len(), 100 - 8); 443 | /// } 444 | /// 445 | /// assert_eq!(&buf[..8], &[1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8][..]); 446 | /// ``` 447 | pub fn le_i64(i: i64) -> impl SerializeFn { 448 | le_u64(i as u64) 449 | } 450 | 451 | /// Writes an `f32` in little endian byte order to the output 452 | /// 453 | /// ```rust 454 | /// use cookie_factory::{gen, bytes::le_f32}; 455 | /// 456 | /// let mut buf = [0u8; 100]; 457 | /// 458 | /// { 459 | /// let (buf, pos) = gen(le_f32(1.0f32), &mut buf[..]).unwrap(); 460 | /// assert_eq!(pos, 4); 461 | /// assert_eq!(buf.len(), 100 - 4); 462 | /// } 463 | /// 464 | /// assert_eq!(&buf[..4], &[0u8, 0u8, 128u8, 63u8][..]); 465 | /// ``` 466 | pub fn le_f32(i: f32) -> impl SerializeFn { 467 | le_u32(i.to_bits()) 468 | } 469 | 470 | /// Writes an `f64` in little endian byte order to the output 471 | /// 472 | /// ```rust 473 | /// use cookie_factory::{gen, bytes::le_f64}; 474 | /// 475 | /// let mut buf = [0u8; 100]; 476 | /// 477 | /// { 478 | /// let (buf, pos) = gen(le_f64(1.0f64), &mut buf[..]).unwrap(); 479 | /// assert_eq!(pos, 8); 480 | /// assert_eq!(buf.len(), 100 - 8); 481 | /// } 482 | /// 483 | /// assert_eq!(&buf[..8], &[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 240u8, 63u8][..]); 484 | /// ``` 485 | pub fn le_f64(i: f64) -> impl SerializeFn { 486 | le_u64(i.to_bits()) 487 | } 488 | 489 | /// Writes an `u8` to the output 490 | /// 491 | /// ```rust 492 | /// use cookie_factory::{gen, bytes::ne_u8}; 493 | /// 494 | /// let mut buf = [0u8; 100]; 495 | /// 496 | /// { 497 | /// let (buf, pos) = gen(ne_u8(1u8), &mut buf[..]).unwrap(); 498 | /// assert_eq!(pos, 1); 499 | /// assert_eq!(buf.len(), 100 - 1); 500 | /// } 501 | /// 502 | /// assert_eq!(&buf[..1], &[1u8][..]); 503 | /// ``` 504 | pub fn ne_u8(i: u8) -> impl SerializeFn { 505 | let len = 1; 506 | 507 | move |mut out: WriteContext| try_write!(out, len, &i.to_ne_bytes()[..]) 508 | } 509 | 510 | /// Writes an `u16` in native byte order to the output 511 | /// 512 | /// ```rust 513 | /// use cookie_factory::{gen, bytes::ne_u16}; 514 | /// 515 | /// let mut buf = [0u8; 100]; 516 | /// 517 | /// { 518 | /// let (buf, pos) = gen(ne_u16(1u16), &mut buf[..]).unwrap(); 519 | /// assert_eq!(pos, 2); 520 | /// assert_eq!(buf.len(), 100 - 2); 521 | /// } 522 | /// 523 | /// #[cfg(target_endian = "big")] 524 | /// assert_eq!(&buf[..2], &[0u8, 1u8][..]); 525 | /// #[cfg(target_endian = "litte")] 526 | /// assert_eq!(&buf[..2], &[1u8, 0u8][..]); 527 | /// ``` 528 | pub fn ne_u16(i: u16) -> impl SerializeFn { 529 | let len = 2; 530 | 531 | move |mut out: WriteContext| try_write!(out, len, &i.to_ne_bytes()[..]) 532 | } 533 | 534 | /// Writes the lower 24 bit of an `u32` in native byte order to the output 535 | /// 536 | /// ```rust 537 | /// use cookie_factory::{gen, bytes::ne_u24}; 538 | /// 539 | /// let mut buf = [0u8; 100]; 540 | /// 541 | /// { 542 | /// let (buf, pos) = gen(ne_u24(1u32), &mut buf[..]).unwrap(); 543 | /// assert_eq!(pos, 3); 544 | /// assert_eq!(buf.len(), 100 - 3); 545 | /// } 546 | /// 547 | /// #[cfg(target_endian = "big")] 548 | /// assert_eq!(&buf[..3], &[0u8, 0u8, 1u8][..]); 549 | /// #[cfg(target_endian = "litte")] 550 | /// assert_eq!(&buf[..3], &[1u8, 0u8, 0u8][..]); 551 | /// ``` 552 | pub fn ne_u24(i: u32) -> impl SerializeFn { 553 | let len = 3; 554 | 555 | move |mut out: WriteContext| try_write!(out, len, &i.to_ne_bytes()[1..]) 556 | } 557 | 558 | /// Writes an `u32` in native byte order to the output 559 | /// 560 | /// ```rust 561 | /// use cookie_factory::{gen, bytes::ne_u32}; 562 | /// 563 | /// let mut buf = [0u8; 100]; 564 | /// 565 | /// { 566 | /// let (buf, pos) = gen(ne_u32(1u32), &mut buf[..]).unwrap(); 567 | /// assert_eq!(pos, 4); 568 | /// assert_eq!(buf.len(), 100 - 4); 569 | /// } 570 | /// 571 | /// #[cfg(target_endian = "big")] 572 | /// assert_eq!(&buf[..4], &[0u8, 0u8, 0u8, 1u8][..]); 573 | /// #[cfg(target_endian = "litte")] 574 | /// assert_eq!(&buf[..4], &[1u8, 0u8, 0u8, 0u8][..]); 575 | /// ``` 576 | pub fn ne_u32(i: u32) -> impl SerializeFn { 577 | let len = 4; 578 | 579 | move |mut out: WriteContext| try_write!(out, len, &i.to_ne_bytes()[..]) 580 | } 581 | 582 | /// Writes an `u64` in native byte order to the output 583 | /// 584 | /// ```rust 585 | /// use cookie_factory::{gen, bytes::ne_u64}; 586 | /// 587 | /// let mut buf = [0u8; 100]; 588 | /// 589 | /// { 590 | /// let (buf, pos) = gen(ne_u64(1u64), &mut buf[..]).unwrap(); 591 | /// assert_eq!(pos, 8); 592 | /// assert_eq!(buf.len(), 100 - 8); 593 | /// } 594 | /// 595 | /// #[cfg(target_endian = "big")] 596 | /// assert_eq!(&buf[..8], &[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8][..]); 597 | /// #[cfg(target_endian = "litte")] 598 | /// assert_eq!(&buf[..8], &[1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8][..]); 599 | /// ``` 600 | pub fn ne_u64(i: u64) -> impl SerializeFn { 601 | let len = 8; 602 | 603 | move |mut out: WriteContext| try_write!(out, len, &i.to_ne_bytes()[..]) 604 | } 605 | 606 | /// Writes an `i8` to the output 607 | /// 608 | /// ```rust 609 | /// use cookie_factory::{gen, bytes::ne_i8}; 610 | /// 611 | /// let mut buf = [0u8; 100]; 612 | /// 613 | /// { 614 | /// let (buf, pos) = gen(ne_i8(1i8), &mut buf[..]).unwrap(); 615 | /// assert_eq!(pos, 1); 616 | /// assert_eq!(buf.len(), 100 - 1); 617 | /// } 618 | /// 619 | /// assert_eq!(&buf[..1], &[1u8][..]); 620 | /// ``` 621 | pub fn ne_i8(i: i8) -> impl SerializeFn { 622 | ne_u8(i as u8) 623 | } 624 | 625 | /// Writes an `i16` in native byte order to the output 626 | /// 627 | /// ```rust 628 | /// use cookie_factory::{gen, bytes::ne_i16}; 629 | /// 630 | /// let mut buf = [0u8; 100]; 631 | /// 632 | /// { 633 | /// let (buf, pos) = gen(ne_i16(1i16), &mut buf[..]).unwrap(); 634 | /// assert_eq!(pos, 2); 635 | /// assert_eq!(buf.len(), 100 - 2); 636 | /// } 637 | /// 638 | /// #[cfg(target_endian = "big")] 639 | /// assert_eq!(&buf[..2], &[0u8, 1u8][..]); 640 | /// #[cfg(target_endian = "litte")] 641 | /// assert_eq!(&buf[..2], &[1u8, 0u8][..]); 642 | /// ``` 643 | pub fn ne_i16(i: i16) -> impl SerializeFn { 644 | ne_u16(i as u16) 645 | } 646 | 647 | /// Writes the lower 24 bit of an `i32` in native byte order to the output 648 | /// 649 | /// ```rust 650 | /// use cookie_factory::{gen, bytes::ne_i24}; 651 | /// 652 | /// let mut buf = [0u8; 100]; 653 | /// 654 | /// { 655 | /// let (buf, pos) = gen(ne_i24(1i32), &mut buf[..]).unwrap(); 656 | /// assert_eq!(pos, 3); 657 | /// assert_eq!(buf.len(), 100 - 3); 658 | /// } 659 | /// 660 | /// #[cfg(target_endian = "big")] 661 | /// assert_eq!(&buf[..3], &[0u8, 0u8, 1u8][..]); 662 | /// #[cfg(target_endian = "litte")] 663 | /// assert_eq!(&buf[..3], &[1u8, 0u8, 0u8][..]); 664 | /// ``` 665 | pub fn ne_i24(i: i32) -> impl SerializeFn { 666 | ne_u24(i as u32) 667 | } 668 | 669 | /// Writes an `i32` in native byte order to the output 670 | /// 671 | /// ```rust 672 | /// use cookie_factory::{gen, bytes::ne_i32}; 673 | /// 674 | /// let mut buf = [0u8; 100]; 675 | /// 676 | /// { 677 | /// let (buf, pos) = gen(ne_i32(1i32), &mut buf[..]).unwrap(); 678 | /// assert_eq!(pos, 4); 679 | /// assert_eq!(buf.len(), 100 - 4); 680 | /// } 681 | /// 682 | /// #[cfg(target_endian = "big")] 683 | /// assert_eq!(&buf[..4], &[0u8, 0u8, 0u8, 1u8][..]); 684 | /// #[cfg(target_endian = "litte")] 685 | /// assert_eq!(&buf[..4], &[1u8, 0u8, 0u8, 0u8][..]); 686 | /// ``` 687 | pub fn ne_i32(i: i32) -> impl SerializeFn { 688 | ne_u32(i as u32) 689 | } 690 | 691 | /// Writes an `i64` in native byte order to the output 692 | /// 693 | /// ```rust 694 | /// use cookie_factory::{gen, bytes::ne_i64}; 695 | /// 696 | /// let mut buf = [0u8; 100]; 697 | /// 698 | /// { 699 | /// let (buf, pos) = gen(ne_i64(1i64), &mut buf[..]).unwrap(); 700 | /// assert_eq!(pos, 8); 701 | /// assert_eq!(buf.len(), 100 - 8); 702 | /// } 703 | /// 704 | /// #[cfg(target_endian = "big")] 705 | /// assert_eq!(&buf[..8], &[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8][..]); 706 | /// #[cfg(target_endian = "litte")] 707 | /// assert_eq!(&buf[..8], &[1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8][..]); 708 | /// ``` 709 | pub fn ne_i64(i: i64) -> impl SerializeFn { 710 | ne_u64(i as u64) 711 | } 712 | 713 | /// Writes an `f32` in native byte order to the output 714 | /// 715 | /// ```rust 716 | /// use cookie_factory::{gen, bytes::ne_f32}; 717 | /// 718 | /// let mut buf = [0u8; 100]; 719 | /// 720 | /// { 721 | /// let (buf, pos) = gen(ne_f32(1.0f32), &mut buf[..]).unwrap(); 722 | /// assert_eq!(pos, 4); 723 | /// assert_eq!(buf.len(), 100 - 4); 724 | /// } 725 | /// 726 | /// #[cfg(target_endian = "big")] 727 | /// assert_eq!(&buf[..4], &[63u8, 128u8, 0u8, 0u8][..]); 728 | /// #[cfg(target_endian = "little")] 729 | /// assert_eq!(&buf[..4], &[0u8, 0u8, 128u8, 63u8][..]); 730 | /// ``` 731 | pub fn ne_f32(i: f32) -> impl SerializeFn { 732 | ne_u32(i.to_bits()) 733 | } 734 | 735 | /// Writes an `f64` in native byte order to the output 736 | /// 737 | /// ```rust 738 | /// use cookie_factory::{gen, bytes::ne_f64}; 739 | /// 740 | /// let mut buf = [0u8; 100]; 741 | /// 742 | /// { 743 | /// let (buf, pos) = gen(ne_f64(1.0f64), &mut buf[..]).unwrap(); 744 | /// assert_eq!(pos, 8); 745 | /// assert_eq!(buf.len(), 100 - 8); 746 | /// } 747 | /// 748 | /// #[cfg(target_endian = "big")] 749 | /// assert_eq!(&buf[..8], &[63u8, 240u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8][..]); 750 | /// #[cfg(target_endian = "little")] 751 | /// assert_eq!(&buf[..8], &[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 240u8, 63u8][..]); 752 | /// ``` 753 | pub fn ne_f64(i: f64) -> impl SerializeFn { 754 | ne_u64(i.to_bits()) 755 | } 756 | -------------------------------------------------------------------------------- /src/combinator.rs: -------------------------------------------------------------------------------- 1 | //! basic serializers 2 | use crate::internal::*; 3 | use crate::lib::std::io::Write; 4 | 5 | macro_rules! try_write(($out:ident, $len:ident, $data:expr) => ( 6 | match $out.write($data) { 7 | Err(io) => Err(GenError::IoError(io)), 8 | Ok(n) if n < $len => Err(GenError::BufferTooSmall($len - n)), 9 | Ok(_) => Ok($out) 10 | } 11 | )); 12 | 13 | /// Writes a byte slice to the output 14 | /// 15 | /// ```rust 16 | /// use cookie_factory::{gen, combinator::slice}; 17 | /// 18 | /// let mut buf = [0u8; 100]; 19 | /// 20 | /// { 21 | /// let (buf, pos) = gen(slice(&b"abcd"[..]), &mut buf[..]).unwrap(); 22 | /// assert_eq!(pos, 4); 23 | /// assert_eq!(buf.len(), 100 - 4); 24 | /// } 25 | /// 26 | /// assert_eq!(&buf[..4], &b"abcd"[..]); 27 | /// ``` 28 | pub fn slice, W: Write>(data: S) -> impl SerializeFn { 29 | let len = data.as_ref().len(); 30 | 31 | move |mut out: WriteContext| try_write!(out, len, data.as_ref()) 32 | } 33 | 34 | /// Writes a string slice to the output 35 | /// 36 | /// ```rust 37 | /// use cookie_factory::{gen, combinator::string}; 38 | /// 39 | /// let mut buf = [0u8; 100]; 40 | /// 41 | /// { 42 | /// let (buf, pos) = gen(string("abcd"), &mut buf[..]).unwrap(); 43 | /// assert_eq!(pos, 4); 44 | /// assert_eq!(buf.len(), 100 - 4); 45 | /// } 46 | /// 47 | /// assert_eq!(&buf[..4], &b"abcd"[..]); 48 | /// ``` 49 | pub fn string, W: Write>(data: S) -> impl SerializeFn { 50 | let len = data.as_ref().len(); 51 | 52 | move |mut out: WriteContext| try_write!(out, len, data.as_ref().as_bytes()) 53 | } 54 | 55 | /// Writes an hex string to the output 56 | #[cfg(feature = "std")] 57 | /// ```rust 58 | /// use cookie_factory::{gen, combinator::hex}; 59 | /// 60 | /// let mut buf = [0u8; 100]; 61 | /// 62 | /// { 63 | /// let (buf, pos) = gen(hex(0x2A), &mut buf[..]).unwrap(); 64 | /// assert_eq!(pos, 2); 65 | /// assert_eq!(buf.len(), 100 - 2); 66 | /// } 67 | /// 68 | /// assert_eq!(&buf[..2], &b"2A"[..]); 69 | /// ``` 70 | pub fn hex(data: S) -> impl SerializeFn { 71 | move |mut out: WriteContext| match write!(out, "{:X}", data) { 72 | Err(io) => Err(GenError::IoError(io)), 73 | Ok(()) => Ok(out), 74 | } 75 | } 76 | 77 | /// Skips over some input bytes without writing anything 78 | /// 79 | /// ```rust 80 | /// use cookie_factory::{gen, combinator::skip}; 81 | /// 82 | /// let mut buf = [0u8; 100]; 83 | /// 84 | /// let (out, pos) = gen(skip(2), &mut buf[..]).unwrap(); 85 | /// 86 | /// assert_eq!(pos, 2); 87 | /// assert_eq!(out.len(), 98); 88 | /// ``` 89 | pub fn skip(len: usize) -> impl SerializeFn { 90 | move |out: WriteContext| W::skip(out, len) 91 | } 92 | 93 | /// Applies a serializer if the condition is true 94 | /// 95 | /// ```rust 96 | /// use cookie_factory::{gen, combinator::{cond, string}}; 97 | /// 98 | /// let mut buf = [0u8; 100]; 99 | /// 100 | /// { 101 | /// let (buf, pos) = gen(cond(true, string("abcd")), &mut buf[..]).unwrap(); 102 | /// assert_eq!(pos, 4); 103 | /// assert_eq!(buf.len(), 100 - 4); 104 | /// } 105 | /// 106 | /// assert_eq!(&buf[..4], &b"abcd"[..]); 107 | /// ``` 108 | pub fn cond(condition: bool, f: F) -> impl SerializeFn 109 | where 110 | F: SerializeFn, 111 | { 112 | move |out: WriteContext| { 113 | if condition { 114 | f(out) 115 | } else { 116 | Ok(out) 117 | } 118 | } 119 | } 120 | 121 | /// Reserves space for the `Before` combinator, applies the `Gen` combinator, 122 | /// then applies the `Before` combinator with the output from `Gen` onto the 123 | /// reserved space. 124 | /// 125 | /// ```rust 126 | /// use cookie_factory::{gen, gen_simple, sequence::tuple, combinator::{back_to_the_buffer, string}, bytes::be_u8, bytes::be_u32}; 127 | /// 128 | /// let mut buf = [0; 9]; 129 | /// gen_simple(tuple(( 130 | /// back_to_the_buffer( 131 | /// 4, 132 | /// move |buf| gen(string("test"), buf), 133 | /// move |buf, len| gen_simple(be_u32(len as u32), buf) 134 | /// ), 135 | /// be_u8(42) 136 | /// )), &mut buf[..]).unwrap(); 137 | /// assert_eq!(&buf, &[0, 0, 0, 4, 't' as u8, 'e' as u8, 's' as u8, 't' as u8, 42]); 138 | /// ``` 139 | pub fn back_to_the_buffer( 140 | reserved: usize, 141 | gen: Gen, 142 | before: Before, 143 | ) -> impl SerializeFn 144 | where 145 | Gen: Fn(WriteContext) -> Result<(WriteContext, Tmp), GenError>, 146 | Before: Fn(WriteContext, Tmp) -> GenResult, 147 | { 148 | move |w: WriteContext| W::reserve_write_use(w, reserved, &gen, &before) 149 | } 150 | 151 | //missing combinators: 152 | //or 153 | //empty 154 | //then 155 | //stream 156 | //length_value 157 | //text print 158 | //text upperhex 159 | //text lowerhex 160 | 161 | #[cfg(test)] 162 | mod test { 163 | use super::*; 164 | use crate::bytes::{be_u32, be_u8}; 165 | use crate::sequence::tuple; 166 | 167 | #[test] 168 | fn test_gen_with_length() { 169 | let mut buf = [0; 8]; 170 | { 171 | let (len_buf, buf) = buf.split_at_mut(4); 172 | let (_, pos) = gen(string("test"), buf).unwrap(); 173 | gen(be_u32(pos as u32), len_buf).unwrap(); 174 | } 175 | assert_eq!(&buf, &[0, 0, 0, 4, b't', b'e', b's', b't']); 176 | } 177 | 178 | #[test] 179 | fn test_back_to_the_buffer() { 180 | let mut buf = [0; 9]; 181 | 182 | let new_buf = gen_simple( 183 | tuple(( 184 | back_to_the_buffer( 185 | 4, 186 | move |buf| gen(string("test"), buf), 187 | move |buf, len| gen_simple(be_u32(len as u32), buf), 188 | ), 189 | be_u8(42), 190 | )), 191 | &mut buf[..], 192 | ) 193 | .unwrap(); 194 | 195 | assert!(new_buf.is_empty()); 196 | assert_eq!(&buf, &[0, 0, 0, 4, b't', b'e', b's', b't', 42]); 197 | } 198 | 199 | #[cfg(feature = "std")] 200 | #[test] 201 | fn test_back_to_the_buffer_vec() { 202 | let buf = Vec::new(); 203 | 204 | let buf = gen_simple( 205 | tuple(( 206 | back_to_the_buffer( 207 | 4, 208 | move |buf| gen(string("test"), buf), 209 | move |buf, len| gen_simple(be_u32(len as u32), buf), 210 | ), 211 | be_u8(42), 212 | )), 213 | buf, 214 | ) 215 | .unwrap(); 216 | 217 | assert_eq!(&buf[..], &[0, 0, 0, 4, b't', b'e', b's', b't', 42]); 218 | } 219 | 220 | #[test] 221 | fn test_back_to_the_buffer_cursor() { 222 | let mut buf = [0; 9]; 223 | { 224 | let cursor = crate::lib::std::io::Cursor::new(&mut buf[..]); 225 | let cursor = gen_simple( 226 | tuple(( 227 | back_to_the_buffer( 228 | 4, 229 | move |buf| gen(string("test"), buf), 230 | move |buf, len| gen_simple(be_u32(len as u32), buf), 231 | ), 232 | be_u8(42), 233 | )), 234 | cursor, 235 | ) 236 | .unwrap(); 237 | assert_eq!(cursor.position(), 9); 238 | } 239 | assert_eq!(&buf, &[0, 0, 0, 4, b't', b'e', b's', b't', 42]); 240 | } 241 | 242 | #[test] 243 | fn test_back_to_the_buffer_cursor_counter() { 244 | let mut buf = [0; 10]; 245 | { 246 | let cursor = crate::lib::std::io::Cursor::new(&mut buf[..]); 247 | let (cursor, pos) = gen( 248 | tuple(( 249 | be_u8(64), 250 | back_to_the_buffer( 251 | 4, 252 | &move |buf| gen(string("test"), buf), 253 | &move |buf, len| gen_simple(be_u32(len as u32), buf), 254 | ), 255 | be_u8(42), 256 | )), 257 | cursor, 258 | ) 259 | .unwrap(); 260 | assert_eq!(pos, 10); 261 | assert_eq!(cursor.position(), 10); 262 | } 263 | assert_eq!(&buf, &[64, 0, 0, 0, 4, b't', b'e', b's', b't', 42]); 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /src/gen.rs: -------------------------------------------------------------------------------- 1 | //! legacy serializers, kept for backwards compatibility from previous cookie factory versions 2 | 3 | use crate::bytes::*; 4 | use crate::internal::*; 5 | use crate::lib::std::io; 6 | 7 | pub fn legacy_wrap<'a, G>( 8 | gen: G, 9 | x: (&'a mut [u8], usize), 10 | ) -> Result<(&'a mut [u8], usize), GenError> 11 | where 12 | G: SerializeFn>, 13 | { 14 | let (buf, offset) = x; 15 | let (buf, offset) = { 16 | let mut cursor = io::Cursor::new(buf); 17 | cursor.set_position(offset as u64); 18 | let cursor = gen_simple(gen, cursor)?; 19 | let position = cursor.position(); 20 | (cursor.into_inner(), position) 21 | }; 22 | Ok((buf, offset as usize)) 23 | } 24 | 25 | /// Write an unsigned 1 byte integer. Equivalent to `gen_be_u8!(v)` 26 | #[inline] 27 | pub fn set_be_u8(x: (&mut [u8], usize), v: u8) -> Result<(&mut [u8], usize), GenError> { 28 | legacy_wrap(be_u8(v), x) 29 | } 30 | 31 | /// Write an unsigned 2 bytes integer (big-endian order). Equivalent to `gen_be_u16!(v)` 32 | #[inline] 33 | pub fn set_be_u16(x: (&mut [u8], usize), v: u16) -> Result<(&mut [u8], usize), GenError> { 34 | legacy_wrap(be_u16(v), x) 35 | } 36 | 37 | /// Write an unsigned 4 bytes integer (big-endian order). Equivalent to `gen_be_u32!(v)` 38 | #[inline] 39 | pub fn set_be_u32(x: (&mut [u8], usize), v: u32) -> Result<(&mut [u8], usize), GenError> { 40 | legacy_wrap(be_u32(v), x) 41 | } 42 | 43 | /// Write an unsigned 8 bytes integer (big-endian order). Equivalent to `gen_be_u64!(v)` 44 | #[inline] 45 | pub fn set_be_u64(x: (&mut [u8], usize), v: u64) -> Result<(&mut [u8], usize), GenError> { 46 | legacy_wrap(be_u64(v), x) 47 | } 48 | 49 | /// Write an unsigned 1 byte integer. Equivalent to `gen_le_u8!(v)` 50 | #[inline] 51 | pub fn set_le_u8(x: (&mut [u8], usize), v: u8) -> Result<(&mut [u8], usize), GenError> { 52 | legacy_wrap(le_u8(v), x) 53 | } 54 | 55 | /// Write an unsigned 2 bytes integer (little-endian order). Equivalent to `gen_le_u16!(v)` 56 | #[inline] 57 | pub fn set_le_u16(x: (&mut [u8], usize), v: u16) -> Result<(&mut [u8], usize), GenError> { 58 | legacy_wrap(le_u16(v), x) 59 | } 60 | 61 | /// Write an unsigned 4 bytes integer (little-endian order). Equivalent to `gen_le_u32!(v)` 62 | #[inline] 63 | pub fn set_le_u32(x: (&mut [u8], usize), v: u32) -> Result<(&mut [u8], usize), GenError> { 64 | legacy_wrap(le_u32(v), x) 65 | } 66 | 67 | /// Write an unsigned 8 bytes integer (little-endian order). Equivalent to `gen_le_u64!(v)` 68 | #[inline] 69 | pub fn set_le_u64(x: (&mut [u8], usize), v: u64) -> Result<(&mut [u8], usize), GenError> { 70 | legacy_wrap(le_u64(v), x) 71 | } 72 | 73 | /// `gen_align!(I, u8) => I -> Result` 74 | /// Align the output buffer to the next multiple of specified value. 75 | /// 76 | /// Does not modify the output buffer, but increments the output index. 77 | #[macro_export] 78 | macro_rules! gen_align( 79 | (($i:expr, $idx:expr), $val:expr) => ( 80 | { 81 | let aligned = $val - ($idx % $val); 82 | match $i.len() <= $idx+aligned { 83 | true => Err(GenError::BufferTooSmall($idx+aligned - $i.len())), 84 | false => { Ok(($i,($idx+aligned))) }, 85 | } 86 | } 87 | ); 88 | ($i:expr, $val:expr) => ( gen_align!(($i.0, $i.1), $val) ); 89 | ); 90 | 91 | /// `gen_skip!(I, u8) => I -> Result` 92 | /// Skip the specified number of bytes. 93 | /// 94 | /// Does not modify the output buffer, but increments the output index. 95 | #[macro_export] 96 | macro_rules! gen_skip( 97 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::combinator::skip($val as usize), $i) ); 98 | ); 99 | 100 | /// `gen_be_u8!(I, u8) => I -> Result` 101 | /// Write an unsigned 1 byte integer. 102 | #[macro_export] 103 | macro_rules! gen_be_u8( 104 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::be_u8($val), $i) ); 105 | ); 106 | 107 | /// `gen_be_u16!(I, u8) => I -> Result` 108 | /// Write an unsigned 2 bytes integer (using big-endian order). 109 | #[macro_export] 110 | macro_rules! gen_be_u16( 111 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::be_u16($val), $i) ); 112 | ); 113 | 114 | /// `gen_be_u24!(I, u8) => I -> Result` 115 | /// Write an unsigned 3 bytes integer (using big-endian order). 116 | #[macro_export] 117 | macro_rules! gen_be_u24( 118 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::be_u24($val), $i) ); 119 | ); 120 | 121 | /// `gen_be_u32!(I, u8) => I -> Result` 122 | /// Write an unsigned 4 bytes integer (using big-endian order). 123 | #[macro_export] 124 | macro_rules! gen_be_u32( 125 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::be_u32($val), $i) ); 126 | ); 127 | 128 | /// `gen_be_u64!(I, u8) => I -> Result` 129 | /// Write an unsigned 8 bytes integer (using big-endian order). 130 | /// ```rust,ignore 131 | /// let r = gen_be_u64!((&mut mem,0),0x0102030405060708u64); 132 | /// ``` 133 | #[macro_export] 134 | macro_rules! gen_be_u64( 135 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::be_u64($val), $i) ); 136 | ); 137 | 138 | /// `gen_be_i8!(I, i8) => I -> Result` 139 | /// Write a signed 1 byte integer. 140 | #[macro_export] 141 | macro_rules! gen_be_i8( 142 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::be_i8($val), $i) ); 143 | ); 144 | 145 | /// `gen_be_i16!(I, i16) => I -> Result` 146 | /// Write a signed 2 byte integer. 147 | #[macro_export] 148 | macro_rules! gen_be_i16( 149 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::be_i16($val), $i) ); 150 | ); 151 | 152 | /// `gen_be_i24!(I, i24) => I -> Result` 153 | /// Write a signed 3 byte integer. 154 | #[macro_export] 155 | macro_rules! gen_be_i24( 156 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::be_i24($val), $i) ); 157 | ); 158 | 159 | /// `gen_be_i32!(I, i32) => I -> Result` 160 | /// Write a signed 4 byte integer. 161 | #[macro_export] 162 | macro_rules! gen_be_i32( 163 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::be_i32($val), $i) ); 164 | ); 165 | 166 | /// `gen_be_i64!(I, i64) => I -> Result` 167 | /// Write a signed 8 byte integer. 168 | #[macro_export] 169 | macro_rules! gen_be_i64( 170 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::be_i64($val), $i) ); 171 | ); 172 | 173 | /// `gen_be_f32!(I, f32) => I -> Result` 174 | /// Write a 4 byte float. 175 | #[macro_export] 176 | macro_rules! gen_be_f32( 177 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::be_f32($val), $i) ); 178 | ); 179 | 180 | /// `gen_be_f64!(I, f64) => I -> Result` 181 | /// Write a 8 byte float. 182 | #[macro_export] 183 | macro_rules! gen_be_f64( 184 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::be_f64($val), $i) ); 185 | ); 186 | 187 | /// `gen_le_u8!(I, u8) => I -> Result` 188 | /// Write an unsigned 1 byte integer. 189 | #[macro_export] 190 | macro_rules! gen_le_u8( 191 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::le_u8($val), $i) ); 192 | ); 193 | 194 | /// `gen_le_u16!(I, u8) => I -> Result` 195 | /// Write an unsigned 2 bytes integer (using little-endian order). 196 | #[macro_export] 197 | macro_rules! gen_le_u16( 198 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::le_u16($val), $i) ); 199 | ); 200 | 201 | /// `gen_le_u24!(I, u8) => I -> Result` 202 | /// Write an unsigned 3 bytes integer (using little-endian order). 203 | #[macro_export] 204 | macro_rules! gen_le_u24( 205 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::le_u24($val), $i) ); 206 | ); 207 | 208 | /// `gen_le_u32!(I, u8) => I -> Result` 209 | /// Write an unsigned 4 bytes integer (using little-endian order). 210 | #[macro_export] 211 | macro_rules! gen_le_u32( 212 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::le_u32($val), $i) ); 213 | ); 214 | 215 | /// `gen_le_u64!(I, u8) => I -> Result` 216 | /// Write an unsigned 8 bytes integer (using little-endian order). 217 | /// ```rust,ignore 218 | /// let r = gen_le_u64!((&mut mem,0),0x0102030405060708u64); 219 | /// ``` 220 | #[macro_export] 221 | macro_rules! gen_le_u64( 222 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::le_u64($val), $i) ); 223 | ); 224 | 225 | /// `gen_le_i8!(I, i8) => I -> Result` 226 | /// Write a signed 1 byte integer. 227 | #[macro_export] 228 | macro_rules! gen_le_i8( 229 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::le_i8($val), $i) ); 230 | ); 231 | 232 | /// `gen_le_i16!(I, i16) => I -> Result` 233 | /// Write a signed 2 byte integer. 234 | #[macro_export] 235 | macro_rules! gen_le_i16( 236 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::le_i16($val), $i) ); 237 | ); 238 | 239 | /// `gen_le_i24!(I, i24) => I -> Result` 240 | /// Write a signed 3 byte integer. 241 | #[macro_export] 242 | macro_rules! gen_le_i24( 243 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::le_i24($val), $i) ); 244 | ); 245 | 246 | /// `gen_le_i32!(I, i32) => I -> Result` 247 | /// Write a signed 4 byte integer. 248 | #[macro_export] 249 | macro_rules! gen_le_i32( 250 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::le_i32($val), $i) ); 251 | ); 252 | 253 | /// `gen_le_i64!(I, i64) => I -> Result` 254 | /// Write a signed 8 byte integer. 255 | #[macro_export] 256 | macro_rules! gen_le_i64( 257 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::le_i64($val), $i) ); 258 | ); 259 | 260 | /// `gen_le_f32!(I, f32) => I -> Result` 261 | /// Write a 4 byte float. 262 | #[macro_export] 263 | macro_rules! gen_le_f32( 264 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::le_f32($val), $i) ); 265 | ); 266 | 267 | /// `gen_le_f64!(I, f64) => I -> Result` 268 | /// Write a 8 byte float. 269 | #[macro_export] 270 | macro_rules! gen_le_f64( 271 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::bytes::le_f64($val), $i) ); 272 | ); 273 | 274 | /// `gen_copy!(I, &[u8], u8) => I -> Result` 275 | /// Writes a slice, copying only the specified number of bytes to the output buffer. 276 | #[macro_export] 277 | macro_rules! gen_copy( 278 | (($i:expr, $idx:expr), $val:expr, $l:expr) => ( 279 | match $i.len() < $idx+$l { 280 | true => Err(GenError::BufferTooSmall($idx+$l - $i.len())), 281 | false => { 282 | $i[$idx..$idx+$l].clone_from_slice(&$val[0..$l]); 283 | Ok(($i,($idx+$l))) 284 | } 285 | } 286 | ); 287 | ($i:expr, $val:expr, $l:expr) => ( gen_copy!(($i.0,$i.1), $val, $l) ); 288 | ); 289 | 290 | /// `gen_slice!(I, &[u8]) => I -> Result` 291 | /// Writes a slice, copying it entirely to the output buffer. 292 | #[macro_export] 293 | macro_rules! gen_slice( 294 | ($i:expr, $val:expr) => ( $crate::gen::legacy_wrap($crate::combinator::slice($val), $i) ); 295 | ); 296 | 297 | #[macro_export] 298 | macro_rules! gen_length_slice( 299 | (($i:expr, $idx:expr), $lf:ident, $val:expr) => ( 300 | do_gen!(($i,$idx), 301 | $lf($val.len()) >> 302 | gen_slice!($val) 303 | ) 304 | ); 305 | (($i:expr, $idx:expr), $lsubmac:ident >> $val:expr) => ( 306 | do_gen!(($i,$idx), 307 | $lsubmac!($val.len()) >> 308 | gen_slice!($val) 309 | ) 310 | ); 311 | ); 312 | 313 | /// Used to wrap common expressions and function as macros 314 | /// 315 | /// ```rust,no_run 316 | /// # #[macro_use] extern crate cookie_factory; 317 | /// # use cookie_factory::{*, gen::set_be_u8}; 318 | /// # fn main() { 319 | /// // will make a generator setting an u8 320 | /// fn gen0(x:(&mut [u8],usize),v:u8) -> Result<(&mut [u8],usize),GenError> { 321 | /// gen_call!((x.0,x.1), set_be_u8, v) 322 | /// } 323 | /// # } 324 | /// ``` 325 | #[macro_export] 326 | macro_rules! gen_call( 327 | (($i:expr, $idx:expr), $fun:expr) => ( $fun( ($i,$idx) ) ); 328 | (($i:expr, $idx:expr), $fun:expr, $($args:expr),* ) => ( $fun( ($i,$idx), $($args),* ) ); 329 | ); 330 | 331 | /// Applies sub-generators in a sequence. 332 | /// 333 | /// `do_gen!( I, 334 | /// I -> Result >> I-> Result >> ... >> I->Result) 335 | /// => I -> Result 336 | /// with I = (&[u8],usize) and E = GenError 337 | /// ` 338 | /// 339 | /// The input type is a tuple (slice,index). The index is incremented by each generator, to reflect 340 | /// the number of bytes written. 341 | /// 342 | /// If the input slice is not big enough, an error `GenError::BufferTooSmall(n)` is returned, `n` 343 | /// being the index that was required. 344 | /// 345 | /// ```rust,no_run 346 | /// # #[macro_use] extern crate cookie_factory; 347 | /// # use cookie_factory::*; 348 | /// 349 | /// fn gen0(x:(&mut [u8],usize),v:u8,w:u8) -> Result<(&mut [u8],usize),GenError> { 350 | /// do_gen!((x.0,x.1), gen_be_u8!(v) >> gen_be_u8!(w)) 351 | /// } 352 | /// 353 | /// # fn main() { 354 | /// let mut mem : [u8; 2] = [0; 2]; 355 | /// let s = &mut mem[..]; 356 | /// let expected = [1, 2]; 357 | /// 358 | /// match gen0((s,0), 1, 2) { 359 | /// Ok((b,idx)) => { 360 | /// assert_eq!(idx,expected.len()); 361 | /// assert_eq!(&b[..],&expected[..]); 362 | /// }, 363 | /// Err(e) => panic!("error {:?}",e), 364 | /// } 365 | /// # } 366 | /// ``` 367 | #[macro_export] 368 | macro_rules! do_gen( 369 | (__impl $i:expr, $idx:expr, ( $($rest:expr),* )) => ( 370 | { 371 | $($rest)*; 372 | Ok(($i,$idx)) 373 | } 374 | ); 375 | (__impl $i:expr, $idx:expr, $e:ident( $($args:tt)* )) => ( 376 | do_gen!(__impl $i, $idx, gen_call!($e,$($args)*)) 377 | ); 378 | (__impl $i:expr, $idx:expr, $submac:ident!( $($args:tt)* )) => ( 379 | $submac!(($i,$idx), $($args)*) 380 | ); 381 | 382 | (__impl $i:expr, $idx:expr, $e:ident >> $($rest:tt)*) => ( 383 | do_gen!(__impl $i, $idx, gen_call!($e) >> $($rest)*) 384 | ); 385 | (__impl $i:expr, $idx:expr, $e:ident( $($args:tt)* ) >> $($rest:tt)*) => ( 386 | do_gen!(__impl $i, $idx, gen_call!($e,$($args)*) >> $($rest)*) 387 | ); 388 | (__impl $i:expr, $idx:expr, $submac:ident!( $($args:tt)* ) >> $($rest:tt)*) => ( 389 | { 390 | match $submac!(($i,$idx), $($args)*) { 391 | Ok((j,idx2)) => { 392 | do_gen!(__impl j, idx2, $($rest)*) 393 | }, 394 | Err(e) => Err(e), 395 | } 396 | } 397 | ); 398 | 399 | (__impl $i:expr, $idx:expr, $e:ident : $($rest:tt)*) => ( 400 | { 401 | let $e = $idx; 402 | do_gen!(__impl $i, $idx, $($rest)*) 403 | } 404 | ); 405 | 406 | ( ($i:expr, $idx:expr), $($rest:tt)*) => ( 407 | do_gen!(__impl $i, $idx, $($rest)*) 408 | ); 409 | ( $i:expr, $($rest:tt)*) => ( 410 | do_gen!(__impl $i.0, $i.1, $($rest)*) 411 | ); 412 | ); 413 | 414 | /// `gen_cond!(bool, I -> Result) => I -> Result` 415 | /// Conditional combinator 416 | /// 417 | /// Wraps another generator and calls it if the 418 | /// condition is met. This combinator returns 419 | /// the return value of the child generator. 420 | /// 421 | #[macro_export] 422 | macro_rules! gen_cond( 423 | (($i:expr, $idx:expr), $cond:expr, $submac:ident!( $($args:tt)* )) => ( 424 | { 425 | if $cond { 426 | $submac!(($i,$idx), $($args)*) 427 | } else { 428 | Ok(($i,$idx)) 429 | } 430 | } 431 | ); 432 | (($i:expr, $idx:expr), $cond:expr, $f:expr) => ( 433 | gen_cond!(($i,$idx), $cond, gen_call!($f)) 434 | ); 435 | ); 436 | 437 | /// `gen_if_else!(bool, I -> Result, I -> Result) => I -> Result` 438 | /// Conditional combinator, with alternate generator. 439 | /// 440 | /// Wraps another generator and calls it if the 441 | /// condition is met. This combinator returns 442 | /// the return value of the child generator. 443 | /// 444 | /// If the condition is not satisfied, calls the alternate generator. 445 | /// 446 | #[macro_export] 447 | macro_rules! gen_if_else( 448 | (($i:expr, $idx:expr), $cond:expr, $submac_if:ident!( $($args_if:tt)* ), $submac_else:ident!( $($args_else:tt)* )) => ( 449 | { 450 | if $cond { 451 | $submac_if!(($i,$idx), $($args_if)*) 452 | } else { 453 | $submac_else!(($i,$idx), $($args_else)*) 454 | } 455 | } 456 | ); 457 | (($i:expr, $idx:expr), $cond:expr, $f:expr, $g:expr) => ( 458 | gen_cond!(($i,$idx), $cond, gen_call!($f), gen_call!($g)) 459 | ); 460 | ); 461 | 462 | /// `gen_many_ref!(I, Iterable, Fn(I,V)) => I -> Result` 463 | /// Applies the generator `$f` to every element of `$l`, passing arguments by reference. 464 | #[macro_export] 465 | macro_rules! gen_many_ref( 466 | (($i:expr, $idx:expr), $l:expr, $f:expr) => ( 467 | $l.into_iter().fold( 468 | Ok(($i,$idx)), 469 | |r,ref v| { 470 | match r { 471 | Err(e) => Err(e), 472 | Ok(x) => { $f(x, v) }, 473 | } 474 | } 475 | ) 476 | ); 477 | ); 478 | 479 | /// `gen_many_byref!(I, Iterable, Fn(I,V)) => I -> Result` 480 | /// Applies the generator `$f` to every element of `$l`, where arguments of $l are references 481 | #[macro_export] 482 | macro_rules! gen_many_byref( 483 | (($i:expr, $idx:expr), $l:expr, $f:expr) => ( 484 | $l.into_iter().fold( 485 | Ok(($i,$idx)), 486 | |r,&v| { 487 | match r { 488 | Err(e) => Err(e), 489 | Ok(x) => { $f(x, v) }, 490 | } 491 | } 492 | ) 493 | ); 494 | ); 495 | 496 | /// `gen_many!(I, Iterable, Fn(I,V)) => I -> Result` 497 | /// Applies the generator `$f` to every element of `$l`, passing arguments by value. 498 | #[macro_export] 499 | macro_rules! gen_many( 500 | (($i:expr, $idx:expr), $l:expr, $f:expr) => ( 501 | $l.into_iter().fold( 502 | Ok(($i,$idx)), 503 | |r,v| { 504 | match r { 505 | Err(e) => Err(e), 506 | Ok(x) => { $f(x, v) }, 507 | } 508 | } 509 | ) 510 | ); 511 | ); 512 | 513 | /// `gen_at_offset!(usize, I -> Result) => I -> Result` 514 | /// Combinator to call generator at an absolute offset. 515 | /// 516 | /// Wraps another generator and calls it using a different index 517 | /// from the current position. If this combinator succeeds, it returns 518 | /// the current index (instead of the one returned by the child generator). 519 | /// If the child generator fails, returns the error. 520 | /// 521 | #[macro_export] 522 | macro_rules! gen_at_offset( 523 | (($i:expr, $idx:expr), $offset:expr, $f:ident( $($args:tt)* )) => ( 524 | match $i.len() < $offset { 525 | false => { 526 | match $f(($i,$offset), $($args)*) { 527 | Ok((r,_)) => Ok((r,($idx))), 528 | Err(e) => Err(e), 529 | } 530 | }, 531 | true => Err(GenError::BufferTooSmall($offset - $i.len())), 532 | } 533 | ); 534 | (($i:expr, $idx:expr), $offset:expr, $submac:ident!( $($args:tt)* )) => ( 535 | match $i.len() < $offset { 536 | false => { 537 | match $submac!(($i,$offset), $($args)*) { 538 | Ok((r,_)) => Ok((r,($idx))), 539 | Err(e) => Err(e), 540 | } 541 | }, 542 | true => Err(GenError::BufferTooSmall($offset - $i.len())), 543 | } 544 | ); 545 | ); 546 | 547 | /// `gen_at_offset!(usize, I -> Result) => I -> Result` 548 | /// Combinator to call generator at a relative offset. 549 | /// 550 | /// Wraps another generator and calls it using a different index 551 | /// from the current position. If this combinator succeeds, it returns 552 | /// the current index (instead of the one returned by the child generator). 553 | /// If the child generator fails, returns the error. 554 | /// 555 | #[macro_export] 556 | macro_rules! gen_at_rel_offset( 557 | (($i:expr, $idx:expr), $rel_offset:expr, $f:ident( $($args:tt)* )) => ( 558 | match ($rel_offset as i32).overflowing_add($idx as i32).1 { 559 | (s,false) if s > 0 => { gen_at_offset!(($i,$idx),s as usize,$f($($args)*)) }, 560 | _ => Err(GenError::InvalidOffset), 561 | } 562 | ); 563 | (($i:expr, $idx:expr), $rel_offset:expr, $submac:ident!( $($args:tt)* )) => ( 564 | match ($rel_offset as i32).overflowing_add($idx as i32) { 565 | (s,false) if s > 0 => { gen_at_offset!(($i,$idx),s as usize,$submac!($($args)*)) }, 566 | _ => Err(GenError::InvalidOffset), 567 | } 568 | ); 569 | ); 570 | 571 | #[cfg(test)] 572 | mod tests { 573 | use super::*; 574 | 575 | #[test] 576 | fn test_do_gen() { 577 | let mut mem: [u8; 8] = [0; 8]; 578 | let s = &mut mem[..]; 579 | let expected = [1, 2, 3, 4, 5, 6, 7, 8]; 580 | let r = do_gen!( 581 | (s, 0), 582 | gen_be_u8!(1) >> gen_be_u8!(2) >> gen_be_u16!(0x0304) >> gen_be_u32!(0x05060708) 583 | ); 584 | match r { 585 | Ok((b, idx)) => { 586 | assert_eq!(idx, 8); 587 | assert_eq!(b, &expected); 588 | } 589 | Err(e) => panic!("error {:?}", e), 590 | } 591 | } 592 | 593 | #[test] 594 | fn test_do_gen_vector() { 595 | let mut data = [0; 8]; 596 | let expected = [1, 2, 3, 4, 5, 6, 7, 8]; 597 | let r = do_gen!( 598 | (&mut data, 0), 599 | gen_be_u8!(1) >> gen_be_u8!(2) >> gen_be_u16!(0x0304) >> gen_be_u32!(0x05060708) 600 | ); 601 | match r { 602 | Ok((b, idx)) => { 603 | assert_eq!(idx, 8); 604 | assert_eq!(b, &expected); 605 | } 606 | Err(e) => panic!("error {:?}", e), 607 | } 608 | } 609 | 610 | #[test] 611 | fn test_gen_skip() { 612 | let mut mem: [u8; 8] = [0; 8]; 613 | let s = &mut mem[..]; 614 | let expected = [0, 0, 0, 0, 0, 0, 0, 0]; 615 | let r = gen_skip!((s, 0), 5); 616 | match r { 617 | Ok((b, idx)) => { 618 | assert_eq!(idx, 5); 619 | assert_eq!(b, &expected); 620 | } 621 | Err(e) => panic!("error {:?}", e), 622 | } 623 | } 624 | 625 | #[test] 626 | fn test_gen_be_u8() { 627 | let mut mem: [u8; 8] = [0; 8]; 628 | let s = &mut mem[..]; 629 | let expected = [1, 2, 0, 0, 0, 0, 0, 0]; 630 | let r = do_gen!((s, 0), gen_be_u8!(1) >> gen_be_u8!(2)); 631 | match r { 632 | Ok((b, idx)) => { 633 | assert_eq!(idx, 2); 634 | assert_eq!(b, &expected); 635 | } 636 | Err(e) => panic!("error {:?}", e), 637 | } 638 | } 639 | 640 | #[test] 641 | fn test_gen_le_u8() { 642 | let mut mem: [u8; 8] = [0; 8]; 643 | let s = &mut mem[..]; 644 | let expected = [1, 2, 0, 0, 0, 0, 0, 0]; 645 | let r = do_gen!((s, 0), gen_le_u8!(1) >> gen_le_u8!(2)); 646 | match r { 647 | Ok((b, idx)) => { 648 | assert_eq!(idx, 2); 649 | assert_eq!(b, &expected); 650 | } 651 | Err(e) => panic!("error {:?}", e), 652 | } 653 | } 654 | 655 | #[test] 656 | fn test_gen_be_i32() { 657 | let mut mem: [u8; 8] = [0; 8]; 658 | let expected = [0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0]; 659 | let r = gen_be_i32!((&mut mem, 0), -1i32); 660 | match r { 661 | Ok((b, idx)) => { 662 | assert_eq!(idx, 4); 663 | assert_eq!(b, &expected); 664 | } 665 | Err(e) => panic!("error {:?}", e), 666 | } 667 | } 668 | 669 | #[test] 670 | fn test_gen_be_u64() { 671 | let mut mem: [u8; 8] = [0; 8]; 672 | let expected = [1, 2, 3, 4, 5, 6, 7, 8]; 673 | let r = gen_be_u64!((&mut mem, 0), 0x0102030405060708u64); 674 | match r { 675 | Ok((b, idx)) => { 676 | assert_eq!(idx, 8); 677 | assert_eq!(b, &expected); 678 | } 679 | Err(e) => panic!("error {:?}", e), 680 | } 681 | } 682 | 683 | #[test] 684 | fn test_gen_be_u64_very_short_buffer() { 685 | let mut mem = [0; 3]; 686 | 687 | let r = gen_be_u64!((&mut mem, 0), 0x0102030405060708u64); 688 | match r { 689 | Ok((b, idx)) => panic!("should have failed, but wrote {} bytes: {:?}", idx, b), 690 | Err(GenError::BufferTooSmall(sz)) => assert_eq!(sz, 5), 691 | Err(e) => panic!("error {:?}", e), 692 | } 693 | } 694 | 695 | #[test] 696 | fn test_gen_be_u64_slightly_short_buffer() { 697 | let mut mem = [0; 7]; 698 | let r = gen_be_u64!((&mut mem, 0), 0x0102030405060708u64); 699 | match r { 700 | Ok((b, idx)) => panic!("should have failed, but wrote {} bytes: {:?}", idx, b), 701 | Err(GenError::BufferTooSmall(sz)) => assert_eq!(sz, 1), 702 | Err(e) => panic!("error {:?}", e), 703 | } 704 | } 705 | 706 | #[test] 707 | fn test_gen_le_u64() { 708 | let mut mem: [u8; 8] = [0; 8]; 709 | let expected = [8, 7, 6, 5, 4, 3, 2, 1]; 710 | let r = gen_le_u64!((&mut mem, 0), 0x0102030405060708u64); 711 | match r { 712 | Ok((b, idx)) => { 713 | assert_eq!(idx, 8); 714 | assert_eq!(b, &expected); 715 | } 716 | Err(e) => panic!("error {:?}", e), 717 | } 718 | } 719 | 720 | #[test] 721 | fn test_set_be_u8() { 722 | let mut mem: [u8; 8] = [0; 8]; 723 | let s = &mut mem[..]; 724 | let expected = [1, 2, 0, 0, 0, 0, 0, 0]; 725 | let r = do_gen!((s, 0), set_be_u8(1) >> set_be_u8(2)); 726 | match r { 727 | Ok((b, idx)) => { 728 | assert_eq!(idx, 2); 729 | assert_eq!(b, &expected); 730 | } 731 | Err(e) => panic!("error {:?}", e), 732 | } 733 | } 734 | 735 | #[test] 736 | fn test_gen_align() { 737 | let mut mem: [u8; 8] = [0; 8]; 738 | let s = &mut mem[..]; 739 | let expected = [1, 0, 0, 0, 1, 0, 0, 0]; 740 | let r = do_gen!((s, 0), gen_be_u8!(1) >> gen_align!(4) >> gen_be_u8!(1)); 741 | match r { 742 | Ok((b, idx)) => { 743 | assert_eq!(idx, 5); 744 | assert_eq!(b, &expected); 745 | } 746 | Err(e) => panic!("error {:?}", e), 747 | } 748 | } 749 | 750 | #[test] 751 | #[cfg(feature = "std")] 752 | fn test_gen_many() { 753 | let mut mem: [u8; 8] = [0; 8]; 754 | let s = &mut mem[..]; 755 | let v: Vec = vec![1, 2, 3, 4]; 756 | let expected = [0, 1, 0, 2, 0, 3, 0, 4]; 757 | let r = gen_many!((s, 0), v, set_be_u16); 758 | match r { 759 | Ok((b, idx)) => { 760 | assert_eq!(idx, 8); 761 | assert_eq!(b, &expected); 762 | } 763 | Err(e) => panic!("error {:?}", e), 764 | } 765 | } 766 | 767 | #[test] 768 | fn test_gen_copy() { 769 | let mut mem: [u8; 8] = [0; 8]; 770 | let s = &mut mem[..]; 771 | let v = [1, 2, 3, 4]; 772 | let expected = [1, 2, 3, 4, 0, 0, 0, 0]; 773 | let r = gen_copy!((s, 0), v, v.len()); 774 | match r { 775 | Ok((b, idx)) => { 776 | assert_eq!(idx, 4); 777 | assert_eq!(b, &expected); 778 | } 779 | Err(e) => panic!("error {:?}", e), 780 | } 781 | } 782 | 783 | #[test] 784 | fn test_gen_copy_buffer_too_small() { 785 | let mut mem: [u8; 7] = [0; 7]; 786 | let s = &mut mem[..]; 787 | let v = [0, 1, 2, 3, 4, 5, 6, 7]; 788 | let r = gen_copy!((s, 0), v, v.len()); 789 | match r { 790 | Ok(_) => { 791 | panic!("buffer shouldn't have had enough space"); 792 | } 793 | Err(GenError::BufferTooSmall(sz)) => { 794 | if sz != 1 { 795 | panic!("invalid max index returned, expected {} got {}", 1, sz); 796 | } 797 | } 798 | Err(e) => { 799 | panic!("error {:?}", e); 800 | } 801 | } 802 | } 803 | 804 | #[test] 805 | fn test_gen_slice() { 806 | let mut mem: [u8; 0] = [0; 0]; 807 | let s = &mut mem[..]; 808 | let v = []; 809 | let expected = []; 810 | let r = gen_slice!((s, 0), v); 811 | match r { 812 | Ok((b, idx)) => { 813 | assert_eq!(idx, 0); 814 | assert_eq!(b, &expected); 815 | } 816 | Err(e) => panic!("error {:?}", e), 817 | } 818 | } 819 | 820 | #[test] 821 | fn test_gen_slice_buffer_too_small() { 822 | let mut mem: [u8; 7] = [0; 7]; 823 | let s = &mut mem[..]; 824 | let v = [0, 1, 2, 3, 4, 5, 6, 7]; 825 | let r = gen_slice!((s, 0), v); 826 | match r { 827 | Ok(_) => { 828 | panic!("buffer shouldn't have had enough space"); 829 | } 830 | Err(GenError::BufferTooSmall(sz)) => { 831 | if sz != 1 { 832 | panic!("invalid max index returned, expected {} got {}", 1, sz); 833 | } 834 | } 835 | Err(e) => { 836 | panic!("error {:?}", e); 837 | } 838 | } 839 | } 840 | 841 | #[test] 842 | fn test_gen_length_slice() { 843 | let mut mem: [u8; 8] = [0; 8]; 844 | let s = &mut mem[..]; 845 | let v = [1, 2, 3, 4]; 846 | let expected = [0, 4, 1, 2, 3, 4, 0, 0]; 847 | macro_rules! gen_be_usize_as_u16( 848 | ($i:expr, $val:expr) => ( set_be_u16($i, $val as u16) ); 849 | ); 850 | let r = do_gen!((s, 0), gen_length_slice!(gen_be_usize_as_u16 >> v)); 851 | match r { 852 | Ok((b, idx)) => { 853 | assert_eq!(idx, 6); 854 | assert_eq!(b, &expected); 855 | } 856 | Err(e) => panic!("error {:?}", e), 857 | } 858 | } 859 | 860 | #[test] 861 | fn test_gen_checkpoint() { 862 | let mut mem: [u8; 8] = [0; 8]; 863 | let s = &mut mem[..]; 864 | let expected = [1, 0, 0, 0, 0, 4, 0, 0]; 865 | let r = do_gen!( 866 | (s, 0), 867 | start: gen_be_u8!(1) >> gen_align!(4) >> end: gen_be_u16!((end - start) as u16) 868 | ); 869 | match r { 870 | Ok((b, idx)) => { 871 | assert_eq!(idx, 6); 872 | assert_eq!(b, &expected); 873 | } 874 | Err(e) => panic!("error {:?}", e), 875 | } 876 | } 877 | 878 | #[test] 879 | fn test_gen_at_offset() { 880 | let mut mem: [u8; 8] = [0; 8]; 881 | let s = &mut mem[..]; 882 | let expected = [0, 0, 0, 0, 0, 4, 0, 0]; 883 | let r = do_gen!((s, 0), gen_skip!(2) >> gen_at_offset!(4, gen_be_u16!(4))); 884 | match r { 885 | Ok((b, idx)) => { 886 | assert_eq!(idx, 2); 887 | assert_eq!(b, &expected); 888 | } 889 | Err(e) => panic!("error {:?}", e), 890 | } 891 | } 892 | 893 | #[test] 894 | fn test_gen_at_rel_offset() { 895 | let mut mem: [u8; 8] = [0; 8]; 896 | let s = &mut mem[..]; 897 | let expected = [0, 0, 0, 0, 0, 0, 0, 4]; 898 | let r = do_gen!( 899 | (s, 0), 900 | gen_skip!(2) >> gen_at_rel_offset!(4, gen_be_u16!(4)) 901 | ); 902 | match r { 903 | Ok((b, idx)) => { 904 | assert_eq!(idx, 2); 905 | assert_eq!(b, &expected); 906 | } 907 | Err(e) => panic!("error {:?}", e), 908 | } 909 | } 910 | 911 | #[test] 912 | fn test_gen_at_rel_offset_fail() { 913 | let mut mem: [u8; 8] = [0; 8]; 914 | let s = &mut mem[..]; 915 | let r = do_gen!( 916 | (s, 0), 917 | gen_skip!(2) >> gen_at_rel_offset!(-4, gen_be_u16!(4)) 918 | ); 919 | if let Err(GenError::InvalidOffset) = r { 920 | } else { 921 | panic!("unexpected result {:?}", r) 922 | }; 923 | } 924 | } 925 | -------------------------------------------------------------------------------- /src/internal.rs: -------------------------------------------------------------------------------- 1 | //! main structures and traits used to build serializers 2 | use crate::lib::std::{ 3 | fmt, 4 | io::{self, Seek as _, SeekFrom, Write}, 5 | }; 6 | 7 | /// Holds the result of serializing functions 8 | /// 9 | /// The `Ok` case returns the `Write` used for writing, in the `Err` case an instance of 10 | /// `cookie_factory::GenError` is returned. 11 | pub type GenResult = Result, GenError>; 12 | 13 | /// Base type for generator errors 14 | #[derive(Debug)] 15 | pub enum GenError { 16 | /// Input buffer is too small. Argument is the maximum index that is required 17 | BufferTooSmall(usize), 18 | /// We expected to fill the whole buffer but there is some space left 19 | BufferTooBig(usize), 20 | /// Operation asked for accessing an invalid index 21 | InvalidOffset, 22 | /// IoError returned by Write 23 | IoError(io::Error), 24 | 25 | /// Allocated for custom errors 26 | CustomError(u32), 27 | /// Generator or function not yet implemented 28 | NotYetImplemented, 29 | } 30 | 31 | impl fmt::Display for GenError { 32 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 33 | write!(f, "{:?}", self) 34 | } 35 | } 36 | 37 | #[cfg(feature = "std")] 38 | impl std::error::Error for GenError {} 39 | 40 | impl From for GenError { 41 | fn from(err: io::Error) -> Self { 42 | GenError::IoError(err) 43 | } 44 | } 45 | 46 | /// Trait for serializing functions 47 | /// 48 | /// Serializing functions take one input `W` that is the target of writing and return an instance 49 | /// of `cookie_factory::GenResult`. 50 | /// 51 | /// This trait is implemented for all `Fn(W) -> GenResult`. 52 | pub trait SerializeFn: Fn(WriteContext) -> GenResult {} 53 | 54 | impl) -> GenResult> SerializeFn for F {} 55 | 56 | /// Context around a `Write` impl that is passed through serializing functions 57 | /// 58 | /// Currently this only keeps track of the current write position since the start of serialization. 59 | pub struct WriteContext { 60 | pub write: W, 61 | pub position: u64, 62 | } 63 | 64 | impl From for WriteContext { 65 | fn from(write: W) -> Self { 66 | Self { write, position: 0 } 67 | } 68 | } 69 | 70 | impl WriteContext { 71 | /// Returns the contained `Write` and the current position 72 | pub fn into_inner(self) -> (W, u64) { 73 | (self.write, self.position) 74 | } 75 | } 76 | 77 | impl Write for WriteContext { 78 | fn write(&mut self, data: &[u8]) -> crate::lib::std::io::Result { 79 | let amt = self.write.write(data)?; 80 | self.position += amt as u64; 81 | Ok(amt) 82 | } 83 | 84 | #[cfg(feature = "std")] 85 | fn flush(&mut self) -> io::Result<()> { 86 | self.write.flush() 87 | } 88 | } 89 | 90 | impl io::Seek for WriteContext { 91 | fn seek(&mut self, pos: SeekFrom) -> io::Result { 92 | let old_pos = self.write.stream_position()?; 93 | let new_pos = self.write.seek(pos)?; 94 | if new_pos >= old_pos { 95 | self.position += new_pos - old_pos; 96 | } else { 97 | self.position -= old_pos - new_pos; 98 | } 99 | Ok(new_pos) 100 | } 101 | } 102 | 103 | /// Runs the given serializer `f` with the `Write` impl `w` and returns the result 104 | /// 105 | /// This internally wraps `w` in a `WriteContext`, starting at position 0. 106 | /// 107 | /// ```rust 108 | /// use cookie_factory::{gen, combinator::slice}; 109 | /// 110 | /// let mut buf = [0u8; 100]; 111 | /// 112 | /// { 113 | /// let (buf, pos) = gen(slice(&b"abcd"[..]), &mut buf[..]).unwrap(); 114 | /// assert_eq!(buf.len(), 100 - 4); 115 | /// assert_eq!(pos, 4); 116 | /// } 117 | /// 118 | /// assert_eq!(&buf[..4], &b"abcd"[..]); 119 | /// ``` 120 | pub fn gen>(f: F, w: W) -> Result<(W, u64), GenError> { 121 | f(WriteContext::from(w)).map(|ctx| ctx.into_inner()) 122 | } 123 | 124 | /// Runs the given serializer `f` with the `Write` impl `w` and returns the updated `w` 125 | /// 126 | /// This internally wraps `w` in a `WriteContext`, starting at position 0. 127 | /// 128 | /// ```rust 129 | /// use cookie_factory::{gen_simple, combinator::slice}; 130 | /// 131 | /// let mut buf = [0u8; 100]; 132 | /// 133 | /// { 134 | /// let buf = gen_simple(slice(&b"abcd"[..]), &mut buf[..]).unwrap(); 135 | /// assert_eq!(buf.len(), 100 - 4); 136 | /// } 137 | /// 138 | /// assert_eq!(&buf[..4], &b"abcd"[..]); 139 | /// ``` 140 | pub fn gen_simple>(f: F, w: W) -> Result { 141 | f(WriteContext::from(w)).map(|ctx| ctx.into_inner().0) 142 | } 143 | 144 | /// Trait for `Write` types that allow skipping over the data 145 | pub trait Skip: Write { 146 | fn skip(s: WriteContext, sz: usize) -> GenResult 147 | where 148 | Self: Sized; 149 | } 150 | 151 | /// Trait for `Write` types that allow skipping and reserving a slice, then writing some data, 152 | /// then write something in the slice we reserved using the return for our data write. 153 | pub trait BackToTheBuffer: Write { 154 | fn reserve_write_use< 155 | Tmp, 156 | Gen: Fn(WriteContext) -> Result<(WriteContext, Tmp), GenError>, 157 | Before: Fn(WriteContext, Tmp) -> GenResult, 158 | >( 159 | s: WriteContext, 160 | reserved: usize, 161 | gen: &Gen, 162 | before: &Before, 163 | ) -> Result, GenError> 164 | where 165 | Self: Sized; 166 | } 167 | 168 | /// Trait for `Seek` types that want to automatically implement `BackToTheBuffer` 169 | pub trait Seek: Write + io::Seek {} 170 | impl Seek for io::Cursor<&mut [u8]> {} 171 | 172 | impl BackToTheBuffer for W { 173 | fn reserve_write_use< 174 | Tmp, 175 | Gen: Fn(WriteContext) -> Result<(WriteContext, Tmp), GenError>, 176 | Before: Fn(WriteContext, Tmp) -> GenResult, 177 | >( 178 | mut s: WriteContext, 179 | reserved: usize, 180 | gen: &Gen, 181 | before: &Before, 182 | ) -> Result, GenError> { 183 | let start = s.stream_position()?; 184 | let begin = s.seek(SeekFrom::Current(reserved as i64))?; 185 | let (mut buf, tmp) = gen(s)?; 186 | let end = buf.stream_position()?; 187 | buf.seek(SeekFrom::Start(start))?; 188 | let mut buf = before(buf, tmp)?; 189 | let pos = buf.stream_position()?; 190 | if pos != begin { 191 | return Err(GenError::BufferTooBig((begin - pos) as usize)); 192 | } 193 | buf.seek(SeekFrom::Start(end))?; 194 | Ok(buf) 195 | } 196 | } 197 | 198 | impl Skip for &mut [u8] { 199 | fn skip(s: WriteContext, len: usize) -> Result, GenError> { 200 | if s.write.len() < len { 201 | Err(GenError::BufferTooSmall(len - s.write.len())) 202 | } else { 203 | Ok(WriteContext { 204 | write: &mut s.write[len..], 205 | position: s.position + len as u64, 206 | }) 207 | } 208 | } 209 | } 210 | 211 | impl Skip for io::Cursor<&mut [u8]> { 212 | fn skip(mut s: WriteContext, len: usize) -> GenResult { 213 | let remaining = s 214 | .write 215 | .get_ref() 216 | .len() 217 | .saturating_sub(s.write.position() as usize); 218 | if remaining < len { 219 | Err(GenError::BufferTooSmall(len - remaining)) 220 | } else { 221 | let cursor_position = s.write.position(); 222 | s.write.set_position(cursor_position + len as u64); 223 | s.position += len as u64; 224 | Ok(s) 225 | } 226 | } 227 | } 228 | 229 | impl BackToTheBuffer for &mut [u8] { 230 | fn reserve_write_use< 231 | Tmp, 232 | Gen: Fn(WriteContext) -> Result<(WriteContext, Tmp), GenError>, 233 | Before: Fn(WriteContext, Tmp) -> GenResult, 234 | >( 235 | s: WriteContext, 236 | reserved: usize, 237 | gen: &Gen, 238 | before: &Before, 239 | ) -> Result, GenError> { 240 | let WriteContext { 241 | write: slice, 242 | position: original_position, 243 | } = s; 244 | 245 | let (res, buf) = slice.split_at_mut(reserved); 246 | let (new_context, tmp) = gen(WriteContext { 247 | write: buf, 248 | position: original_position + reserved as u64, 249 | })?; 250 | 251 | let res = before( 252 | WriteContext { 253 | write: res, 254 | position: original_position, 255 | }, 256 | tmp, 257 | )?; 258 | 259 | if !res.write.is_empty() { 260 | return Err(GenError::BufferTooBig(res.write.len())); 261 | } 262 | 263 | Ok(new_context) 264 | } 265 | } 266 | 267 | #[cfg(feature = "std")] 268 | impl BackToTheBuffer for Vec { 269 | fn reserve_write_use< 270 | Tmp, 271 | Gen: Fn(WriteContext) -> Result<(WriteContext, Tmp), GenError>, 272 | Before: Fn(WriteContext, Tmp) -> GenResult, 273 | >( 274 | s: WriteContext, 275 | reserved: usize, 276 | gen: &Gen, 277 | before: &Before, 278 | ) -> Result, GenError> { 279 | let WriteContext { 280 | write: mut vec, 281 | position: original_position, 282 | } = s; 283 | 284 | let start_len = vec.len(); 285 | vec.extend(std::iter::repeat(0).take(reserved)); 286 | 287 | let (mut new_context, tmp) = gen(WriteContext { 288 | write: vec, 289 | position: original_position + reserved as u64, 290 | })?; 291 | 292 | let tmp_context = before( 293 | WriteContext { 294 | write: Vec::new(), 295 | position: original_position, 296 | }, 297 | tmp, 298 | )?; 299 | 300 | let tmp_written = tmp_context.write.len(); 301 | if tmp_written != reserved { 302 | return Err(GenError::BufferTooBig(reserved - tmp_written)); 303 | } 304 | 305 | // FIXME?: find a way to do that without copying 306 | // Vec::from_raw_parts + core::mem::forget makes it work, but 307 | // if `before` writes more than `reserved`, realloc will cause troubles 308 | new_context.write[start_len..(start_len + reserved)] 309 | .copy_from_slice(&tmp_context.write[..]); 310 | 311 | Ok(new_context) 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /src/io_compat.rs: -------------------------------------------------------------------------------- 1 | // there's no real io error on a byte slice 2 | pub type Error = (); 3 | pub type Result = core::result::Result; 4 | 5 | pub trait Write { 6 | fn write(&mut self, buf: &[u8]) -> Result; 7 | } 8 | 9 | impl Write for &mut [u8] { 10 | fn write(&mut self, data: &[u8]) -> Result { 11 | let amt = core::cmp::min(data.len(), self.len()); 12 | let (a, b) = core::mem::replace(self, &mut []).split_at_mut(amt); 13 | a.copy_from_slice(&data[..amt]); 14 | *self = b; 15 | Ok(amt) 16 | } 17 | } 18 | 19 | pub enum SeekFrom { 20 | Start(u64), 21 | Current(i64), 22 | } 23 | 24 | pub trait Seek { 25 | fn seek(&mut self, pos: SeekFrom) -> Result; 26 | } 27 | 28 | // Minimal re-implementation of std::io::Cursor so it 29 | // also works in non-std environments 30 | pub struct Cursor(T, u64); 31 | 32 | impl<'a> Cursor<&'a mut [u8]> { 33 | pub fn new(inner: &'a mut [u8]) -> Self { 34 | Self(inner, 0) 35 | } 36 | 37 | pub fn into_inner(self) -> &'a mut [u8] { 38 | self.0 39 | } 40 | 41 | pub fn position(&self) -> u64 { 42 | self.1 as u64 43 | } 44 | 45 | pub fn set_position(&mut self, pos: u64) { 46 | self.1 = pos; 47 | } 48 | 49 | pub fn get_mut(&mut self) -> &mut [u8] { 50 | self.0 51 | } 52 | 53 | pub fn get_ref(&self) -> &[u8] { 54 | self.0 55 | } 56 | } 57 | 58 | impl<'a> Write for Cursor<&'a mut [u8]> { 59 | fn write(&mut self, data: &[u8]) -> Result { 60 | let amt = (&mut self.0[(self.1 as usize)..]).write(data)?; 61 | self.1 += amt as u64; 62 | 63 | Ok(amt) 64 | } 65 | } 66 | 67 | impl<'a> Seek for Cursor<&'a mut [u8]> { 68 | fn seek(&mut self, pos: SeekFrom) -> Result { 69 | let (start, offset) = match pos { 70 | SeekFrom::Start(n) => { 71 | self.1 = n; 72 | return Ok(n); 73 | } 74 | SeekFrom::Current(n) => (self.1 as u64, n), 75 | }; 76 | let new_pos = if offset >= 0 { 77 | start.checked_add(offset as u64) 78 | } else { 79 | start.checked_sub((offset.wrapping_neg()) as u64) 80 | }; 81 | match new_pos { 82 | Some(n) => { 83 | self.1 = n; 84 | Ok(n) 85 | } 86 | None => panic!("invalid seek to a negative or overflowing position"), 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! serialization library built with a combinator design similar to nom. 2 | //! 3 | //! Serializers are built up from single purpose serializers, like `slice` 4 | //! to write a raw byte slice, or `be_u16` to write a `u16` integer in big 5 | //! endian form. 6 | //! 7 | //! Those small serializers can then be assembled by using combinators. 8 | //! As an example, `all(["abcd", "efgh", "ijkl"].iter().map(string))(output)` 9 | //! will write `"abcdefghijkl"` to `output`. 10 | #![cfg_attr(not(feature = "std"), no_std)] 11 | 12 | #[cfg(not(feature = "std"))] 13 | mod io_compat; 14 | 15 | /// lib module necessary to reexport what we need from std in `no_std` mode 16 | pub mod lib { 17 | #[cfg(feature = "std")] 18 | pub mod std { 19 | pub mod io { 20 | pub use std::io::{Cursor, Error, Result, Seek, SeekFrom, Write}; 21 | } 22 | pub use std::{cmp, fmt, iter, mem, result, slice}; 23 | } 24 | 25 | #[cfg(not(feature = "std"))] 26 | pub mod std { 27 | pub use core::{cmp, iter, mem, result, slice}; 28 | #[macro_use] 29 | pub use core::fmt; 30 | 31 | pub mod io { 32 | pub use crate::io_compat::*; 33 | } 34 | } 35 | } 36 | 37 | #[macro_use] 38 | pub mod gen; 39 | 40 | mod internal; 41 | pub use internal::*; 42 | #[cfg(feature = "async")] 43 | pub mod async_bufwriter; 44 | pub mod bytes; 45 | pub mod combinator; 46 | pub mod multi; 47 | pub mod sequence; 48 | -------------------------------------------------------------------------------- /src/multi.rs: -------------------------------------------------------------------------------- 1 | //! serializers working on a list of elements (vectors, iterators, etc) 2 | use crate::internal::{SerializeFn, WriteContext}; 3 | use crate::lib::std::io::Write; 4 | 5 | /// Applies an iterator of serializers of the same type 6 | /// 7 | /// ```rust 8 | /// use cookie_factory::{gen, multi::all, combinator::string}; 9 | /// 10 | /// let mut buf = [0u8; 100]; 11 | /// 12 | /// let data = vec!["abcd", "efgh", "ijkl"]; 13 | /// { 14 | /// let (buf, pos) = gen(all(data.iter().map(string)), &mut buf[..]).unwrap(); 15 | /// assert_eq!(pos, 12); 16 | /// assert_eq!(buf.len(), 100 - 12); 17 | /// } 18 | /// 19 | /// assert_eq!(&buf[..12], &b"abcdefghijkl"[..]); 20 | /// ``` 21 | pub fn all(values: It) -> impl SerializeFn 22 | where 23 | G: SerializeFn, 24 | It: Clone + Iterator, 25 | { 26 | move |mut out: WriteContext| { 27 | let it = values.clone(); 28 | 29 | for v in it { 30 | out = v(out)?; 31 | } 32 | 33 | Ok(out) 34 | } 35 | } 36 | 37 | /// Applies an iterator of serializers of the same type with a separator between each serializer 38 | /// 39 | /// ```rust 40 | /// use cookie_factory::{gen, multi::separated_list, combinator::string}; 41 | /// 42 | /// let mut buf = [0u8; 100]; 43 | /// 44 | /// let data = vec!["abcd", "efgh", "ijkl"]; 45 | /// { 46 | /// let (buf, pos) = gen(separated_list(string(","), data.iter().map(string)), &mut buf[..]).unwrap(); 47 | /// assert_eq!(pos, 14); 48 | /// assert_eq!(buf.len(), 100 - 14); 49 | /// } 50 | /// 51 | /// assert_eq!(&buf[..14], &b"abcd,efgh,ijkl"[..]); 52 | /// ``` 53 | pub fn separated_list(sep: F, values: It) -> impl SerializeFn 54 | where 55 | F: SerializeFn, 56 | G: SerializeFn, 57 | It: Clone + Iterator, 58 | { 59 | move |mut out: WriteContext| { 60 | let mut it = values.clone(); 61 | 62 | match it.next() { 63 | None => return Ok(out), 64 | Some(first) => { 65 | out = first(out)?; 66 | } 67 | } 68 | 69 | for v in it { 70 | out = sep(out).and_then(v)?; 71 | } 72 | 73 | Ok(out) 74 | } 75 | } 76 | 77 | /// Applies a generator over an iterator of values, and applies the serializers generated 78 | /// 79 | /// ```rust 80 | /// use cookie_factory::{gen, multi::many_ref, combinator::string}; 81 | /// 82 | /// let mut buf = [0u8; 100]; 83 | /// 84 | /// let data = vec!["abcd", "efgh", "ijkl"]; 85 | /// { 86 | /// let (buf, pos) = gen(many_ref(&data, string), &mut buf[..]).unwrap(); 87 | /// assert_eq!(pos, 12); 88 | /// assert_eq!(buf.len(), 100 - 12); 89 | /// } 90 | /// 91 | /// assert_eq!(&buf[..12], &b"abcdefghijkl"[..]); 92 | /// ``` 93 | pub fn many_ref(items: I, generator: F) -> impl SerializeFn 94 | where 95 | It: Iterator + Clone, 96 | I: IntoIterator, 97 | F: Fn(E) -> G, 98 | G: SerializeFn, 99 | { 100 | let items = items.into_iter(); 101 | move |mut out: WriteContext| { 102 | for item in items.clone() { 103 | out = generator(item)(out)?; 104 | } 105 | Ok(out) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/sequence.rs: -------------------------------------------------------------------------------- 1 | //! serializers working a sequence of objects (pairs, tuples, etc) 2 | use crate::internal::{GenResult, SerializeFn, WriteContext}; 3 | use crate::lib::std::io::Write; 4 | 5 | /// Applies 2 serializers in sequence 6 | /// 7 | /// ```rust 8 | /// use cookie_factory::{gen, sequence::pair, combinator::string}; 9 | /// 10 | /// let mut buf = [0u8; 100]; 11 | /// 12 | /// { 13 | /// let (buf, pos) = gen(pair(string("abcd"), string("efgh")), &mut buf[..]).unwrap(); 14 | /// assert_eq!(pos, 8); 15 | /// assert_eq!(buf.len(), 100 - 8); 16 | /// } 17 | /// 18 | /// assert_eq!(&buf[..8], &b"abcdefgh"[..]); 19 | /// ``` 20 | pub fn pair(first: F, second: G) -> impl SerializeFn 21 | where 22 | F: SerializeFn, 23 | G: SerializeFn, 24 | { 25 | move |out: WriteContext| first(out).and_then(&second) 26 | } 27 | 28 | /// Helper trait for the `tuple` combinator 29 | pub trait Tuple { 30 | fn serialize(&self, w: WriteContext) -> GenResult; 31 | } 32 | 33 | impl> Tuple for (A,) { 34 | fn serialize(&self, w: WriteContext) -> GenResult { 35 | self.0(w) 36 | } 37 | } 38 | 39 | // Generates all the Tuple impls for tuples of arbitrary sizes based on a list of type 40 | // parameters like FnA FnB FnC. It would generate the impl then for (FnA, FnB) 41 | // and (FnA, FnB, FnC). 42 | macro_rules! tuple_trait( 43 | ($name1:ident, $name2: ident, $($name:ident),*) => ( 44 | tuple_trait!(__impl $name1, $name2; $($name),*); 45 | ); 46 | (__impl $($name:ident),+; $name1:ident, $($name2:ident),*) => ( 47 | tuple_trait_impl!($($name),+); 48 | tuple_trait!(__impl $($name),+ , $name1; $($name2),*); 49 | ); 50 | (__impl $($name:ident),+; $name1:ident) => ( 51 | tuple_trait_impl!($($name),+); 52 | tuple_trait_impl!($($name),+, $name1); 53 | ); 54 | ); 55 | 56 | // Generates the impl block for Tuple on tuples or arbitrary sizes based on its 57 | // arguments. Takes a list of type parameters as parameters, e.g. FnA FnB FnC 58 | // and then implements the trait on (FnA, FnB, FnC). 59 | macro_rules! tuple_trait_impl( 60 | ($($name:ident),+) => ( 61 | impl),+> Tuple for ( $($name),+ ) { 62 | fn serialize(&self, w: WriteContext) -> GenResult { 63 | tuple_trait_inner!(0, self, w, $($name)+) 64 | } 65 | } 66 | ); 67 | ); 68 | 69 | // Generates the inner part of the Tuple::serialize() implementation, which will 70 | // basically look as follows: 71 | // 72 | // let w = self.0(w)?; 73 | // let w = self.1(w)?; 74 | // [...] 75 | // let w = self.N(w)?; 76 | // 77 | // Ok(w) 78 | macro_rules! tuple_trait_inner( 79 | ($it:tt, $self:expr, $w:ident, $head:ident $($id:ident)+) => ({ 80 | let w = $self.$it($w)?; 81 | 82 | succ!($it, tuple_trait_inner!($self, w, $($id)+)) 83 | }); 84 | ($it:tt, $self:expr, $w:ident, $head:ident) => ({ 85 | let w = $self.$it($w)?; 86 | 87 | Ok(w) 88 | }); 89 | ); 90 | 91 | // Takes an integer and a macro invocation, and changes the macro invocation 92 | // to take the incremented integer as the first argument 93 | // 94 | // Works for integers between 0 and 19. 95 | #[doc(hidden)] 96 | macro_rules! succ ( 97 | (0, $submac:ident ! ($($rest:tt)*)) => ($submac!(1, $($rest)*)); 98 | (1, $submac:ident ! ($($rest:tt)*)) => ($submac!(2, $($rest)*)); 99 | (2, $submac:ident ! ($($rest:tt)*)) => ($submac!(3, $($rest)*)); 100 | (3, $submac:ident ! ($($rest:tt)*)) => ($submac!(4, $($rest)*)); 101 | (4, $submac:ident ! ($($rest:tt)*)) => ($submac!(5, $($rest)*)); 102 | (5, $submac:ident ! ($($rest:tt)*)) => ($submac!(6, $($rest)*)); 103 | (6, $submac:ident ! ($($rest:tt)*)) => ($submac!(7, $($rest)*)); 104 | (7, $submac:ident ! ($($rest:tt)*)) => ($submac!(8, $($rest)*)); 105 | (8, $submac:ident ! ($($rest:tt)*)) => ($submac!(9, $($rest)*)); 106 | (9, $submac:ident ! ($($rest:tt)*)) => ($submac!(10, $($rest)*)); 107 | (10, $submac:ident ! ($($rest:tt)*)) => ($submac!(11, $($rest)*)); 108 | (11, $submac:ident ! ($($rest:tt)*)) => ($submac!(12, $($rest)*)); 109 | (12, $submac:ident ! ($($rest:tt)*)) => ($submac!(13, $($rest)*)); 110 | (13, $submac:ident ! ($($rest:tt)*)) => ($submac!(14, $($rest)*)); 111 | (14, $submac:ident ! ($($rest:tt)*)) => ($submac!(15, $($rest)*)); 112 | (15, $submac:ident ! ($($rest:tt)*)) => ($submac!(16, $($rest)*)); 113 | (16, $submac:ident ! ($($rest:tt)*)) => ($submac!(17, $($rest)*)); 114 | (17, $submac:ident ! ($($rest:tt)*)) => ($submac!(18, $($rest)*)); 115 | (18, $submac:ident ! ($($rest:tt)*)) => ($submac!(19, $($rest)*)); 116 | (19, $submac:ident ! ($($rest:tt)*)) => ($submac!(20, $($rest)*)); 117 | ); 118 | 119 | tuple_trait!( 120 | FnA, FnB, FnC, FnD, FnE, FnF, FnG, FnH, FnI, FnJ, FnK, FnL, FnM, FnN, FnO, FnP, FnQ, FnR, FnS, 121 | FnT, FnU 122 | ); 123 | 124 | /// Applies multiple serializers in sequence 125 | /// 126 | /// Currently tuples up to 20 elements are supported. 127 | /// 128 | /// ```rust 129 | /// use cookie_factory::{gen, sequence::tuple, combinator::string, bytes::be_u16}; 130 | /// 131 | /// let mut buf = [0u8; 100]; 132 | /// 133 | /// { 134 | /// let (buf, pos) = gen( 135 | /// tuple(( 136 | /// string("abcd"), 137 | /// be_u16(0x20), 138 | /// string("efgh"), 139 | /// )), 140 | /// &mut buf[..] 141 | /// ).unwrap(); 142 | /// assert_eq!(pos, 10); 143 | /// assert_eq!(buf.len(), 100 - 10); 144 | /// } 145 | /// 146 | /// assert_eq!(&buf[..10], &b"abcd\x00\x20efgh"[..]); 147 | /// ``` 148 | pub fn tuple>(l: List) -> impl SerializeFn { 149 | move |w: WriteContext| l.serialize(w) 150 | } 151 | 152 | #[cfg(test)] 153 | mod test { 154 | use super::*; 155 | use crate::combinator::string; 156 | use crate::internal::gen_simple; 157 | 158 | #[test] 159 | fn test_pair_with_cursor() { 160 | let mut buf = [0u8; 8]; 161 | 162 | { 163 | use crate::lib::std::io::Cursor; 164 | 165 | let cursor = Cursor::new(&mut buf[..]); 166 | let serializer = pair(string("1234"), string("5678")); 167 | 168 | let cursor = gen_simple(serializer, cursor).unwrap(); 169 | assert_eq!(cursor.position(), 8); 170 | } 171 | 172 | assert_eq!(&buf[..], b"12345678"); 173 | } 174 | 175 | #[test] 176 | fn test_tuple() { 177 | let mut buf = [0u8; 12]; 178 | 179 | { 180 | use crate::lib::std::io::Cursor; 181 | 182 | let cursor = Cursor::new(&mut buf[..]); 183 | let serializer = tuple((string("1234"), string("5678"), tuple((string("0123"),)))); 184 | 185 | let cursor = gen_simple(serializer, cursor).unwrap(); 186 | assert_eq!(cursor.position(), 12); 187 | } 188 | 189 | assert_eq!(&buf[..], b"123456780123"); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /tests/combinators-json.rs: -------------------------------------------------------------------------------- 1 | extern crate cookie_factory; 2 | #[macro_use] 3 | extern crate maplit; 4 | 5 | #[path = "./json/mod.rs"] 6 | mod implementation; 7 | use crate::implementation::*; 8 | 9 | #[test] 10 | fn json_test() { 11 | use cookie_factory::gen_simple; 12 | use cookie_factory::lib::std::io::Cursor; 13 | use std::str; 14 | 15 | let value = JsonValue::Object(btreemap! { 16 | String::from("arr") => JsonValue::Array(vec![JsonValue::Num(1.0), JsonValue::Num(12.3), JsonValue::Num(42.0)]), 17 | String::from("b") => JsonValue::Boolean(true), 18 | String::from("o") => JsonValue::Object(btreemap!{ 19 | String::from("x") => JsonValue::Str(String::from("abcd")), 20 | String::from("y") => JsonValue::Str(String::from("efgh")), 21 | String::from("empty") => JsonValue::Array(vec![]), 22 | }), 23 | }); 24 | 25 | //let value = JsonValue::Array(repeat(element).take(10).collect::>()); 26 | 27 | let mut buffer = [0u8; 8192]; 28 | let sr = gen_json_value(&value); 29 | let writer = Cursor::new(&mut buffer[..]); 30 | let writer = gen_simple(sr, writer).unwrap(); 31 | let size = writer.position() as usize; 32 | let buffer = writer.into_inner(); 33 | 34 | println!("result:\n{}", str::from_utf8(&buffer[..size]).unwrap()); 35 | assert_eq!(str::from_utf8(&buffer[..size]).unwrap(), 36 | "{\"arr\":[1234.56,1234.56,1234.56],\"b\":true,\"o\":{\"empty\":[],\"x\":\"abcd\",\"y\":\"efgh\"}}"); 37 | } 38 | -------------------------------------------------------------------------------- /tests/http.rs: -------------------------------------------------------------------------------- 1 | extern crate cookie_factory; 2 | 3 | #[path = "../tests/http/mod.rs"] 4 | mod implementation; 5 | use crate::implementation::*; 6 | 7 | #[cfg(test)] 8 | mod tests { 9 | use super::*; 10 | use cookie_factory::gen_simple; 11 | use std::io::Cursor; 12 | use std::str::from_utf8; 13 | 14 | #[test] 15 | fn request() { 16 | let mut mem: [u8; 1024] = [0; 1024]; 17 | let s = &mut mem[..]; 18 | 19 | let request = Request { 20 | method: "GET", 21 | uri: "/hello/test/a/b/c?name=value#hash", 22 | headers: [ 23 | Header { 24 | name: "Host", 25 | value: "lolcatho.st", 26 | }, 27 | Header { 28 | name: "User-agent", 29 | value: "cookie-factory", 30 | }, 31 | Header { 32 | name: "Content-Length", 33 | value: "13", 34 | }, 35 | Header { 36 | name: "Connection", 37 | value: "Close", 38 | }, 39 | ] 40 | .to_vec(), 41 | body: b"Hello, world!", 42 | }; 43 | 44 | let (_, index) = cf_request((s, 0), &request).unwrap(); 45 | println!( 46 | "request written by cf:\n{}", 47 | from_utf8(&s[..index]).unwrap() 48 | ); 49 | 50 | let mut mem2: [u8; 1024] = [0; 1024]; 51 | let (mem2, index2) = { 52 | let sr = fn_request(&request); 53 | let writer = Cursor::new(&mut mem2[..]); 54 | let writer = gen_simple(sr, writer).unwrap(); 55 | let index2 = writer.position() as usize; 56 | (writer.into_inner(), index2) 57 | }; 58 | println!( 59 | "request written by fn:\n{}", 60 | from_utf8(&mem2[..index2]).unwrap() 61 | ); 62 | println!("wrote {} bytes", index2); 63 | 64 | assert_eq!(index, index2); 65 | assert_eq!( 66 | from_utf8(&s[..index]).unwrap(), 67 | from_utf8(&mem2[..index2]).unwrap() 68 | ); 69 | } 70 | 71 | /* 72 | #[test] 73 | fn chunked_http() { 74 | let mut mem: [u8; 1024] = [0; 1024]; 75 | let s = &mut mem[..]; 76 | 77 | let request = RequestHeaders { 78 | method: "GET", 79 | uri: "/hello/test/a/b/c?name=value#hash", 80 | headers: [ 81 | Header { name: "Host", value: "lolcatho.st" }, 82 | Header { name: "User-agent", value: "cookie-factory" }, 83 | Header { name: "Content-Length", value: "13" }, 84 | Header { name: "Connection", value: "Close" }, 85 | ].iter().cloned().collect(), 86 | }; 87 | 88 | let mut sr = chunked_request(&request); 89 | assert_eq!(sr.serialize(&mut s[..132]), Ok((132, Serialized::Continue))); 90 | 91 | // add chunk 92 | sr.second.push(chunk(&b"Hello "[..])); 93 | assert_eq!(sr.serialize(&mut s[132..145]), Ok((13, Serialized::Continue))); 94 | assert_eq!(from_utf8(&s[132..145]).unwrap(), "\r\n\r\n6\r\nHello "); 95 | 96 | // add chunk 97 | sr.second.push(chunk(&b"world !"[..])); 98 | // add last chunk 99 | sr.second.push(chunk(&[])); 100 | 101 | assert_eq!(sr.serialize(&mut s[145..]), Ok((19, Serialized::Done))); 102 | assert_eq!(from_utf8(&s[136..164]).unwrap(), "6\r\nHello \r\n7\r\nworld !\r\n0\r\n\r\n"); 103 | 104 | }*/ 105 | } 106 | -------------------------------------------------------------------------------- /tests/http/mod.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | use std::str; 3 | 4 | use cookie_factory::combinator::{hex, slice, string}; 5 | use cookie_factory::multi::all; 6 | use cookie_factory::sequence::tuple; 7 | use cookie_factory::*; 8 | 9 | #[derive(Debug, Clone, PartialEq)] 10 | pub struct Request<'a> { 11 | pub method: &'a str, 12 | pub uri: &'a str, 13 | pub headers: Vec>, 14 | pub body: &'a [u8], 15 | } 16 | 17 | #[derive(Debug, Clone, PartialEq)] 18 | pub struct Header<'a> { 19 | pub name: &'a str, 20 | pub value: &'a str, 21 | } 22 | 23 | pub fn cf_request<'a, 'b, 'c>( 24 | i: (&'a mut [u8], usize), 25 | r: &'c Request<'b>, 26 | ) -> Result<(&'a mut [u8], usize), GenError> { 27 | do_gen!( 28 | (i.0, i.1), 29 | gen_call!(cf_request_line, r.method, r.uri) 30 | >> gen_many!(&r.headers, cf_header) 31 | >> gen_slice!(b"\r\n") 32 | >> gen_slice!(r.body) 33 | ) 34 | } 35 | 36 | pub fn cf_request_line<'a, 'b>( 37 | i: (&'a mut [u8], usize), 38 | method: &'b str, 39 | uri: &'b str, 40 | ) -> Result<(&'a mut [u8], usize), GenError> { 41 | do_gen!( 42 | (i.0, i.1), 43 | gen_slice!(method.as_bytes()) 44 | >> gen_slice!(b" ") 45 | >> gen_slice!(uri.as_bytes()) 46 | >> gen_slice!(b" HTTP/1.1\r\n") 47 | ) 48 | } 49 | 50 | pub fn cf_header<'a, 'b, 'c>( 51 | i: (&'a mut [u8], usize), 52 | h: &'c Header<'b>, 53 | ) -> Result<(&'a mut [u8], usize), GenError> { 54 | do_gen!( 55 | (i.0, i.1), 56 | gen_slice!(h.name.as_bytes()) 57 | >> gen_slice!(b": ") 58 | >> gen_slice!(h.value.as_bytes()) 59 | >> gen_slice!(b"\r\n") 60 | ) 61 | } 62 | 63 | #[derive(Debug, Clone, PartialEq)] 64 | pub struct RequestHeaders<'a> { 65 | pub method: &'a str, 66 | pub uri: &'a str, 67 | pub headers: Vec>, 68 | } 69 | 70 | pub fn fn_request<'a: 'c, 'b: 'a, 'c, W: Write + 'c>( 71 | r: &'b Request<'a>, 72 | ) -> impl SerializeFn + 'c { 73 | tuple(( 74 | fn_request_line(&r.method, &r.uri), 75 | all(r.headers.iter().map(fn_header)), 76 | string("\r\n"), 77 | slice(r.body), 78 | )) 79 | } 80 | 81 | pub fn fn_request_line<'a: 'c, 'c, S: AsRef, W: Write + 'c>( 82 | method: &'a S, 83 | uri: &'a S, 84 | ) -> impl SerializeFn + 'c { 85 | tuple(( 86 | string(method), 87 | string(" "), 88 | string(uri), 89 | string(" HTTP/1.1\r\n"), 90 | )) 91 | } 92 | 93 | pub fn fn_header<'a: 'c, 'c, W: Write + 'c>(h: &'a Header) -> impl SerializeFn + 'c { 94 | tuple(( 95 | string(h.name), 96 | string(": "), 97 | string(h.value), 98 | string("\r\n"), 99 | )) 100 | } 101 | 102 | pub fn fn_request_headers<'a: 'c, 'c, 'b: 'a, W: Write + 'c>( 103 | r: &'b RequestHeaders<'a>, 104 | ) -> impl SerializeFn + 'c { 105 | tuple(( 106 | fn_request_line(&r.method, &r.uri), 107 | all(r.headers.iter().map(fn_header)), 108 | string("\r\n"), 109 | )) 110 | } 111 | 112 | pub fn fn_chunk<'a: 'c, 'c, W: Write + 'c>(sl: &'a [u8]) -> impl SerializeFn + 'c { 113 | tuple((hex(sl.len()), string("\r\n"), slice(sl), string("\r\n"))) 114 | } 115 | 116 | /* 117 | pub fn chunked_request<'a, 'b: 'a, 'c, T: Serializer + 'a>(r: &'b RequestHeaders<'a>) -> Then> { 118 | let s: Stream = Stream::new(); 119 | rw_request_headers(r).then(s) 120 | }*/ 121 | -------------------------------------------------------------------------------- /tests/json/mod.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | use std::str; 3 | 4 | use cookie_factory::{ 5 | combinator::string, lib::std::io::Write, multi::separated_list, sequence::tuple, *, 6 | }; 7 | 8 | #[derive(Clone, Debug, PartialEq)] 9 | pub enum JsonValue { 10 | Str(String), 11 | Boolean(bool), 12 | Num(f64), 13 | Array(Vec), 14 | Object(BTreeMap), 15 | } 16 | 17 | #[inline(always)] 18 | pub fn gen_str<'a, 'b: 'a, W: Write + 'a>(s: &'b str) -> impl SerializeFn + 'a { 19 | tuple((string("\""), string(s), string("\""))) 20 | } 21 | 22 | #[inline(always)] 23 | pub fn gen_bool(b: bool) -> impl SerializeFn { 24 | if b { 25 | string("true") 26 | } else { 27 | string("false") 28 | } 29 | } 30 | 31 | #[inline(always)] 32 | pub fn gen_num(_b: f64) -> impl SerializeFn { 33 | string("1234.56") 34 | } 35 | 36 | pub fn gen_array<'a, 'b: 'a, W: Write + 'a>(arr: &'b [JsonValue]) -> impl SerializeFn + 'a { 37 | tuple(( 38 | string("["), 39 | separated_list(string(","), arr.iter().map(gen_json_value)), 40 | string("]"), 41 | )) 42 | } 43 | 44 | pub fn gen_key_value<'a, 'b: 'a, W: Write + 'a>( 45 | kv: (&'b String, &'b JsonValue), 46 | ) -> impl SerializeFn + 'a { 47 | tuple((gen_str(kv.0), string(":"), gen_json_value(kv.1))) 48 | } 49 | 50 | pub fn gen_object<'a, 'b: 'a, W: Write + 'a>( 51 | o: &'b BTreeMap, 52 | ) -> impl SerializeFn + 'a { 53 | tuple(( 54 | string("{"), 55 | separated_list(string(","), o.iter().map(gen_key_value)), 56 | string("}"), 57 | )) 58 | } 59 | 60 | pub fn gen_json_value(g: &JsonValue) -> impl SerializeFn + '_ { 61 | move |out: WriteContext| match g { 62 | JsonValue::Str(ref s) => gen_str(s)(out), 63 | JsonValue::Boolean(ref b) => gen_bool(*b)(out), 64 | JsonValue::Num(ref n) => gen_num(*n)(out), 65 | JsonValue::Array(ref v) => gen_array(v)(out), 66 | JsonValue::Object(ref o) => gen_object(o)(out), 67 | } 68 | } 69 | 70 | #[test] 71 | fn json_test() { 72 | use cookie_factory::lib::std::io::Cursor; 73 | use std::str; 74 | 75 | let value = JsonValue::Object(btreemap! { 76 | String::from("arr") => JsonValue::Array(vec![JsonValue::Num(1.0), JsonValue::Num(12.3), JsonValue::Num(42.0)]), 77 | String::from("b") => JsonValue::Boolean(true), 78 | String::from("o") => JsonValue::Object(btreemap!{ 79 | String::from("x") => JsonValue::Str(String::from("abcd")), 80 | String::from("y") => JsonValue::Str(String::from("efgh")), 81 | String::from("empty") => JsonValue::Array(vec![]), 82 | }), 83 | }); 84 | 85 | let mut buffer = [0u8; 8192]; 86 | let sr = gen_json_value(&value); 87 | let cursor = Cursor::new(&mut buffer[..]); 88 | let cursor = gen_simple(sr, cursor).unwrap(); 89 | let size = cursor.position() as usize; 90 | let buffer = cursor.into_inner(); 91 | 92 | println!("result:\n{}", str::from_utf8(&buffer[..size]).unwrap()); 93 | assert_eq!(str::from_utf8(&buffer[..size]).unwrap(), 94 | "{\"arr\":[1234.56,1234.56,1234.56],\"b\":true,\"o\":{\"empty\":[],\"x\":\"abcd\",\"y\":\"efgh\"}}"); 95 | } 96 | -------------------------------------------------------------------------------- /tests/pouet.rs: -------------------------------------------------------------------------------- 1 | use std::iter::repeat; 2 | 3 | fn slice1(out: &mut [u8]) -> Result<&mut [u8], u32> { 4 | let data = &b"pouet"[..]; 5 | if out.len() < data.len() { 6 | Err(42) 7 | } else { 8 | out[..data.len()].copy_from_slice(data); 9 | Ok(&mut out[data.len()..]) 10 | } 11 | } 12 | 13 | fn main() { 14 | let mut v1 = repeat(0u8).take(4).collect::>(); 15 | let mut v2 = repeat(0u8).take(10).collect::>(); 16 | 17 | println!("res1: {:?}", slice1(&mut v1[..])); 18 | println!("v1: {:?}", v1); 19 | 20 | println!("res2: {:?}", slice1(&mut v2[..])); 21 | println!("v2: {:?}", v2); 22 | } 23 | --------------------------------------------------------------------------------