bendy-0.6.1/.cargo_vcs_info.json0000644000000001470000000000100121430ustar { "git": { "sha1": "aa55b38a86ebb1dad1e6390fe002f66159714713" }, "path_in_vcs": "bendy-lib" }bendy-0.6.1/Cargo.lock0000644000000127620000000000100101240ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "aho-corasick" version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" dependencies = [ "memchr", ] [[package]] name = "bendy" version = "0.6.1" dependencies = [ "doc-comment", "regex", "rustversion", "serde", "serde_bytes", "serde_derive", "smallvec", "thiserror", "timeit", ] [[package]] name = "doc-comment" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "libc" version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "memchr" version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "proc-macro2" version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "regex" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "rustversion" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24c8ad4f0c00e1eb5bc7614d236a7f1300e3dbd76b68cac8e06fb00b015ad8d8" [[package]] name = "serde" version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03" [[package]] name = "serde_bytes" version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212e73464ebcde48d723aa02eb270ba62eff38a9b732df31f33f1b4e145f3a54" dependencies = [ "serde", ] [[package]] name = "serde_derive" version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da" dependencies = [ "proc-macro2", "quote", "syn 1.0.98", ] [[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "syn" version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "thiserror" version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "time" version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" dependencies = [ "libc", "wasi", "winapi", ] [[package]] name = "timeit" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b2942f5edfee7facb569dd9965b649370397d82fe2511383175f15f26eebfa5" dependencies = [ "time", ] [[package]] name = "unicode-ident" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" bendy-0.6.1/Cargo.toml0000644000000045320000000000100101430ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2024" name = "bendy" version = "0.6.1" authors = [ "P3KI ", "TQ Hirsch ", "Bruno Kirschner ", "Oliver Uvman ", ] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = """ A rust library for encoding and decoding bencode with enforced canonicalization rules. """ readme = "README.md" keywords = [ "bencode", "serialization", "deserialization", "bittorent", ] categories = [ "encoding", "no-std", ] license = "BSD-3-Clause" repository = "https://github.com/P3KI/bendy" [package.metadata.docs.rs] all-features = true [badges.maintenance] status = "actively-developed" [badges.travis-ci] repository = "P3KI/bendy" [features] debug = [] default = ["std"] inspect = ["smallvec"] serde = [ "serde_", "serde_bytes", ] std = ["thiserror/std"] [lib] name = "bendy" path = "src/lib.rs" [[example]] name = "decode_torrent" path = "examples/decode_torrent.rs" [[example]] name = "encode_torrent" path = "examples/encode_torrent.rs" required-features = ["std"] [[test]] name = "core_test" path = "tests/core_test.rs" required-features = ["std"] [[test]] name = "performance_test" path = "tests/performance_test.rs" [[test]] name = "struct_codec" path = "tests/struct_codec.rs" [dependencies.rustversion] version = "1.0.4" [dependencies.serde_] version = "^1.0" optional = true package = "serde" [dependencies.serde_bytes] version = "^0.11.3" optional = true [dependencies.smallvec] version = "^1.15.1" optional = true [dependencies.thiserror] version = "^2.0.14" default-features = false [dev-dependencies.doc-comment] version = "0.3.3" [dev-dependencies.regex] version = "^1.0" [dev-dependencies.serde_derive] version = "^1.0" [dev-dependencies.smallvec] version = "^1.15.1" [dev-dependencies.timeit] version = "0.1.2" bendy-0.6.1/Cargo.toml.orig000064400000000000000000000037241046102023000136260ustar 00000000000000[package] name = "bendy" version = "0.6.1" edition = "2024" authors = [ "P3KI ", "TQ Hirsch ", "Bruno Kirschner ", "Oliver Uvman ", ] description = """ A rust library for encoding and decoding bencode with enforced canonicalization rules. """ repository = "https://github.com/P3KI/bendy" license = "BSD-3-Clause" readme = "../README.md" keywords = ["bencode", "serialization", "deserialization", "bittorent"] categories = ["encoding", "no-std"] [badges] maintenance = {status = "actively-developed"} travis-ci = { repository = "P3KI/bendy" } ### DEPENDENCIES ############################################################### [dependencies] rustversion = "1.0.4" serde_ = { version = "^1.0" , optional = true, package = "serde" } serde_bytes = { version = "^0.11.3", optional = true } thiserror = { version = "^2.0.14", default-features = false } smallvec = { version = "^1.15.1", optional = true } [dev-dependencies] doc-comment = "0.3.3" regex = "^1.0" serde_derive = "^1.0" timeit = "0.1.2" smallvec = "^1.15.1" ### FEATURES ################################################################### [features] default = ["std"] # Provide implementations for common standard library types like `Vec` and # `HashMap`. Requires a dependency on the Rust standard library. std = ["thiserror/std"] # Support serde serialization to and deserialization from bencode serde = ["serde_", "serde_bytes"] # Adds extra UTF-8 decode checks debug = [] # Provide utilities for inspecting, testing, debugging bencode inspect = ["smallvec"] ### Targets #################################################################### [[test]] name = "core_test" required-features = ["std"] [[example]] name = "encode_torrent" required-features = ["std"] ### DOCS.RS #################################################################### [package.metadata.docs.rs] # Enable all features so docs are comprehensive all-features = true bendy-0.6.1/README.md000064400000000000000000000575361046102023000122300ustar 00000000000000# Bendy [![Build Status](https://travis-ci.org/P3KI/bendy.svg?branch=master)](https://travis-ci.org/P3KI/bendy) [![Current Version](https://meritbadge.herokuapp.com/bendy)](https://crates.io/crates/bendy) [![License: BSD-3-Clause](https://img.shields.io/github/license/P3KI/bendy.svg)](https://github.com/P3KI/bendy/blob/master/LICENSE-BSD3) A Rust library for encoding and decoding bencode with enforced canonicalization rules. It also provides some facilities for reflecting over arbitrary decoded bencode structures. [Bencode](https://en.wikipedia.org/wiki/Bencode) is a simple but very effective encoding scheme, originating with the BitTorrent peer-to-peer system. --- You may be looking for: - [Known Alternatives](#known-alternatives) - [Why should I use it](#why-should-i-use-it) - [Usage](#usage) - [Feature Flags](#feature-flags) - [Encoding](#encoding-with-tobencode) - [Decoding](#decoding-with-frombencode) - [Serde Support](#serde-support) - [Reflection](#reflection) - [Unsafe Code](#usage-of-unsafe-code) - [Contributing](#contributing) --- ## Known alternatives: This is not the first library to implement Bencode. In fact there are several implementations already: - Toby Padilla [serde-bencode](https://github.com/toby/serde-bencode) - Arjan Topolovec's [rust-bencode](https://github.com/arjantop/rust-bencode), - Murarth's [bencode](https://github.com/murarth/bencode), - and Jonas Hermsmeier's [rust-bencode](https://github.com/jhermsmeier/rust-bencode) ## Why should I use it? So why the extra work adding yet-another-version of a thing that already exists, you might ask? ### Enforced correctness Implementing a canonical encoding form is straight forward. It comes down to defining *a proper way of handling unordered data*. The next step is that bendy's sorting data before encoding it using the regular Bencode rules. If your data is already sorted bendy will of course skip the extra sorting step to gain efficiency. But bendy goes a step further to *ensure correctness*: If you hand the library data that you say is already sorted, bendy still does an in-place verification to *ensure that your data actually is sorted* and complains if it isn't. In the end, once bendy serialized your data, it's Bencode through and through. So it's perfectly compatible with every other Bencode library. Just remember: At this point *only bendy* enforces the correctness of the canonical format if you read it back in. ### Canonical representation Bendy ensures that any de-serialize / serialize round trip produces the exact *same* and *correct* binary representation. This is relevant if you're dealing with unordered sets or map-structured data where theoretically the order is not relevant, but in practice it is, especially if you want to ensure that cryptographic signatures related to the data structure do not get invalidated accidentally. | Data Structure | Default Impl | Comment | |----------------|--------------|--------------------------------------------------------------------------------------------| | Vec | ✔ | Defines own ordering | | VecDeque | ✔ | Defines own ordering | | LinkedList | ✔ | Defines own ordering | | HashMap | ✔ | Ordering missing but content is ordered by key byte representation. | | BTreeMap | ✔ | Defines own ordering | | HashSet | ✘ | (Unordered) Set handling not yet defined | | BTreeSet | ✘ | (Unordered) Set handling not yet defined | | BinaryHeap | ✘ | Ordering missing | | Iterator | ~ | `emit_unchecked_list()` allows to emit any iterable but user needs to ensure the ordering. | **Attention:** - Since most list types already define their inner ordering, data structures like `Vec`, `VecDeque`, and `LinkedList` will not get sorted during encoding! - There is no default implementation for handling generic iterators. This is by design. `Bendy` cannot tell from an iterator whether the underlying structure requires sorting or not and would have to take data as-is. ## Usage First you need to add bendy as a project dependency: ```toml [dependencies] bendy = "^0.4" ``` ### Feature flags Bendy has the following feature flags: * `std` - Enabled by default. * `serde` - Support serde. Requires `std` to be enabled. * `inspect` - Include reflection facilities. ### Encoding with `ToBencode` To encode an object of a type which already implements the `ToBencode` trait it is enough to import the trait and call the `to_bencode()` function on the object. ```rust use bendy::encoding::{ToBencode, Error}; let my_data = vec!["hello", "world"]; let encoded = my_data.to_bencode()?; assert_eq!(b"l5:hello5:worlde", encoded.as_slice()); Ok::<(), Error>(()) ``` ### Implementing `ToBencode` In most cases it should be enough to overwrite the associated `encode` function and keep the default implementation of `to_bencode`. The function will provide you with a `SingleItemEncoder` which must be used to emit any relevant components of the current object. As long as these implement `ToBencode` themselves it is enough to pass them into the `emit` function of the encoder as this will serialize any type implementing the trait. Next to `emit` the encoder also provides a list of functions to encode specific bencode primitives (i.e. `emit_int` and `emit_str`) and nested bencode elements (i.e. `emit_dict` and `emit_list`). These methods should be used if its necessary to output a specific non default data type. **Implementing Integer Encoding** As bencode has native integer support bendy provides default implementations for all of rusts native integer types. This allows to call `to_bencode` on any integer object and to pass these objects into the encoder's `emit_int` function. ```rust use bendy::encoding::{ToBencode, SingleItemEncoder, Error}; struct IntegerWrapper(i64); impl ToBencode for IntegerWrapper { const MAX_DEPTH: usize = 0; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { encoder.emit_int(self.0) } } let example = IntegerWrapper(21); let encoded = example.to_bencode()?; assert_eq!(b"i21e", encoded.as_slice()); let encoded = 21.to_bencode()?; assert_eq!(b"i21e", encoded.as_slice()); Ok::<(), Error>(()) ``` **Encode a byte string** Another data type bencode natively supports are byte strings. Therefore bendy provides default implementations for `String` and `&str`. ```rust use bendy::encoding::{ToBencode, SingleItemEncoder, Error}; struct StringWrapper(String); impl ToBencode for StringWrapper { const MAX_DEPTH: usize = 0; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { encoder.emit_str(&self.0) } } let example = StringWrapper("content".to_string()); let encoded = example.to_bencode()?; assert_eq!(b"7:content", encoded.as_slice()); let encoded = "content".to_bencode()?; assert_eq!(b"7:content", encoded.as_slice()); Ok::<(), Error>(()) ``` As its a very common pattern to represent a byte string as `Vec` bendy exposes the `AsString` wrapper. This can be used to encapsulate any element implementing `AsRef<[u8]>` to output itself as a bencode string instead of a list. ```rust use bendy::encoding::{ToBencode, SingleItemEncoder, Error, AsString}; struct ByteStringWrapper(Vec); impl ToBencode for ByteStringWrapper { const MAX_DEPTH: usize = 0; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { let content = AsString(&self.0); encoder.emit(&content) } } let example = ByteStringWrapper(b"content".to_vec()); let encoded = example.to_bencode()?; assert_eq!(b"7:content", encoded.as_slice()); let encoded = AsString(b"content").to_bencode()?; assert_eq!(b"7:content", encoded.as_slice()); Ok::<(), Error>(()) ``` **Encode a dictionary** If a data structure contains key-value pairs its most likely a good idea to encode it as a bencode dictionary. This is also true for most structs with more then one member as it might be helpful to represent their names to ensure the existence of specific (optional) member. __Attention:__ To ensure a canonical representation bendy requires that the keys of a dictionary emitted via `emit_dict` are sorted in ascending order or the encoding will fail with an error of kind `UnsortedKeys`. In case of an unsorted dictionary it might be useful to use `emit_and_sort_dict` instead. ```rust use bendy::encoding::{ToBencode, SingleItemEncoder, Error}; struct Example { label: String, counter: u64, } impl ToBencode for Example { const MAX_DEPTH: usize = 1; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { encoder.emit_dict(|mut e| { e.emit_pair(b"counter", &self.counter)?; e.emit_pair(b"label", &self.label)?; Ok(()) }) } } let example = Example { label: "Example".to_string(), counter: 0 }; let encoded = example.to_bencode()?; assert_eq!(b"d7:counteri0e5:label7:Examplee", encoded.as_slice()); Ok::<(), Error>(()) ``` **Encode a list** While encoding a list bendy assumes the elements inside this list are inherently sorted through their position inside the list. The implementation is therefore free to choose its own sorting. ```rust use bendy::encoding::{ToBencode, SingleItemEncoder, Error}; struct Location(i64, i64); impl ToBencode for Location { const MAX_DEPTH: usize = 1; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { encoder.emit_list(|e| { e.emit_int(self.0)?; e.emit_int(self.1) }) } } let example = Location(2, 3); let encoded = example.to_bencode()?; assert_eq!(b"li2ei3ee", encoded.as_slice()); Ok::<(), Error>(()) ``` ### Decoding with `FromBencode` To decode an object of a type which already implements the `FromBencode` trait it is enough to import the trait and call the `from_bencode()` function on the object. ```rust use bendy::decoding::{FromBencode, Error}; let encoded = b"l5:hello5:worlde".to_vec(); let decoded = Vec::::from_bencode(&encoded)?; assert_eq!(vec!["hello", "world"], decoded); Ok::<(), Error>(()) ``` ### Implementing `FromBencode` In most cases it should be enough to overwrite the associated `decode_bencode_object` function and keep the default implementation of `from_bencode`. The function will provide you with an representation of a bencode `Object` which must be processed to receive any relevant components of the expected data type. As long as these implement `FromBencode` themselves it is enough to call `decode_bencode_object` on the expected data type of the element as this will deserialize any type implementing the trait. Next to `from_bencode` the bencode `Object` representation also provides a list of helper functions to itself into specific bencode primitives and container (i.e. `bytes_or`, `integer_or_else` or `try_into_list`). Which than can be used to restore the actual element. **Decode an integer** As bencode has native integer support bendy provides default implementations for all of rusts native integer types. This allows to call `from_bencode` on any type of integer. *Attention:* If it's necessary to handle a big integer which has no representation through one of the default data types it's always possible to access the string version of the number during decoding. ```rust use bendy::decoding::{FromBencode, Object, Error}; #[derive(Debug, Eq, PartialEq)] struct IntegerWrapper(i64); impl FromBencode for IntegerWrapper { const EXPECTED_RECURSION_DEPTH: usize = 0; fn decode_bencode_object(object: Object) -> Result { // This is an example for content handling. It would also be possible // to call `i64::decode_bencode_object(object)` directly. let content = object.try_into_integer()?; let number = content.parse::()?; Ok(IntegerWrapper(number)) } } let encoded = b"i21e".to_vec(); let example = IntegerWrapper::from_bencode(&encoded)?; assert_eq!(IntegerWrapper(21), example); let example = i64::from_bencode(&encoded)?; assert_eq!(21, example); Ok::<(), Error>(()) ``` **Decode a byte string** In most cases it is possible to restore a string from its bencode representation as a byte sequence via the `String::from_utf8` and `str::from_utf8`. ```rust use bendy::decoding::{FromBencode, Object, Error}; #[derive(Debug, Eq, PartialEq)] struct StringWrapper(String); impl FromBencode for StringWrapper { const EXPECTED_RECURSION_DEPTH: usize = 0; fn decode_bencode_object(object: Object) -> Result { // This is an example for content handling. It would also be possible // to call `String::decode_bencode_object(object)` directly. let content = object.try_into_bytes()?; let content = String::from_utf8(content.to_vec())?; Ok(StringWrapper(content)) } } let encoded = b"7:content".to_vec(); let example = StringWrapper::from_bencode(&encoded)?; assert_eq!(StringWrapper("content".to_string()), example); let example = String::from_bencode(&encoded)?; assert_eq!("content".to_string(), example); Ok::<(), Error>(()) ``` If the content is a non utf8 encoded string or an actual byte sequence the `AsString` wrapper might be useful to restore the bencode string object as a sequence of bytes through an object of type `Vec`. ```rust use bendy::{ decoding::{FromBencode, Object, Error}, encoding::AsString, }; #[derive(Debug, Eq, PartialEq)] struct ByteStringWrapper(Vec); impl FromBencode for ByteStringWrapper { const EXPECTED_RECURSION_DEPTH: usize = 0; fn decode_bencode_object(object: Object) -> Result { let content = AsString::decode_bencode_object(object)?; Ok(ByteStringWrapper(content.0)) } } let encoded = b"7:content".to_vec(); let example = ByteStringWrapper::from_bencode(&encoded)?; assert_eq!(ByteStringWrapper(b"content".to_vec()), example); let example = AsString::from_bencode(&encoded)?; assert_eq!(b"content".to_vec(), example.0); Ok::<(), Error>(()) ``` **Decode a dictionary** Unwrapping the bencode object into a dictionary will provide a dictionary decoder which can be used to access the included key-value pairs. To improve the error handling in case of huge or multiple nested dictionaries the decoding module provides a `ResultExt` trait which allows to add a context description in case of an error. If multiple context calls are nested they will concatenated in a dot notation like style. ```rust use bendy::decoding::{FromBencode, Object, Error, ResultExt}; #[derive(Debug, Eq, PartialEq)] struct Example { label: String, counter: u64, } impl FromBencode for Example { const EXPECTED_RECURSION_DEPTH: usize = 1; fn decode_bencode_object(object: Object) -> Result { let mut counter = None; let mut label = None; let mut dict = object.try_into_dictionary()?; while let Some(pair) = dict.next_pair()? { match pair { (b"counter", value) => { counter = u64::decode_bencode_object(value) .context("counter") .map(Some)?; }, (b"label", value) => { label = String::decode_bencode_object(value) .context("label") .map(Some)?; }, (unknown_field, _) => { return Err(Error::unexpected_field(String::from_utf8_lossy( unknown_field, ))); }, } } let counter = counter.ok_or_else(|| Error::missing_field("counter"))?; let label= label.ok_or_else(|| Error::missing_field("label"))?; Ok(Example { counter, label }) } } let encoded = b"d7:counteri0e5:label7:Examplee".to_vec(); let expected = Example { label: "Example".to_string(), counter: 0 }; let example = Example::from_bencode(&encoded)?; assert_eq!(expected, example); Ok::<(), Error>(()) ``` **Decode a list** Unwrapping the bencode object into a list will provide a list decoder which can be used to access the contained elements. ```rust use bendy::decoding::{FromBencode, Object, Error}; #[derive(Debug, PartialEq, Eq)] struct Location(i64, i64); impl FromBencode for Location { const EXPECTED_RECURSION_DEPTH: usize = 1; fn decode_bencode_object(object: Object) -> Result { let mut list = object.try_into_list()?; let x = list.next_object()?.ok_or(Error::missing_field("x"))?; let x = i64::decode_bencode_object(x)?; let y = list.next_object()?.ok_or(Error::missing_field("y"))?; let y = i64::decode_bencode_object(y)?; Ok(Location(x, y)) } } let encoded = b"li2ei3ee".to_vec(); let expected = Location(2, 3); let example = Location::from_bencode(&encoded)?; assert_eq!(expected, example); Ok::<(), Error>(()) ``` ### Optional: Limitation of recursive parsing **What?** The library allows to set an expected recursion depth limit for de- and encoding. If set, the parser will use this value as an upper limit for the validation of any nested data structure and abort with an error if an additional level of nesting is detected. While the encoding limit itself is primarily there to increase the confidence of bendy users in their own validation code, the decoding limit should be used to avoid parsing of malformed or malicious external data. - The encoding limit can be set through the `MAX_DEPTH` constant in any implementation of the `ToBencode` trait. - The decoding limit can be set through the `EXPECTED_RECURSION_DEPTH` constant in any implementation of the `FromBencode` trait. **How?** The nesting level calculation always starts on level zero, is incremented by one when the parser enters a nested bencode element (i.e. list, dictionary) and decrement as soon as the related element ends. Therefore any values decoded as bencode strings or integers do not affect the nesting limit. ### Serde Support Bendy supports serde when the `serde` feature is enabled: ```toml [dependencies] bendy = { version = "^0.3", features = ["std", "serde"] } serde = { version = "1.0", features = ["derive"] } ``` With the feature enabled, values can be serialized to and deserialized from bencode with `bendy::serde::from_bytes` and `bendy::serde::to_bytes` respectively: ```rust # #[cfg(not(feature = "serde"))] # fn main() {} # #[cfg(feature = "serde")] # fn main() -> Result<(), bendy::serde::Error> { use serde_derive::{Deserialize, Serialize}; #[serde(crate = "serde_")] #[derive(Serialize, Deserialize, PartialEq, Debug)] struct Foo { bar: String, } let value = Foo { bar: "hello".into(), }; let bencode = bendy::serde::to_bytes(&value)?; assert_eq!(bencode, b"d3:bar5:helloe"); let deserialized = bendy::serde::from_bytes::(&bencode)?; assert_eq!(deserialized, value); Ok(()) # } ``` Information on how Rust types are represented in bencode is available in the [serde module documentation](https://docs.rs/bendy/*/bendy/serde/index.html). ### Reflection Bendy supports rudimentary reflection for use in testing, fuzzing, and debugging. > [!WARNING] > This feature SHOULD NOT be used in production code. > It is not as thoroughly tested as other parts of bendy. To enable the feature only in your tests, you can depend on bendy like so: ```toml [dependencies.bendy] version = "^0.4" [dev-dependencies.bendy] version = "^0.4" features = ["inspect"] ``` The API has been designed for convenience. It is intended to be used to construct evil data to ensure your program responds appropriately to it. One way of doing so is: 1) Build a valid serializable object that your program should be able to handle. 1) Encode the object to bencode. 1) Ingest the resulting bytes with the `inspect::inspect` method. 1) Use the returned `Inspectable` to make modifications that break your program's invariants. 1) Call `.emit()` on the `Inspectable` to receive a `Vec` of bytes which represents your modified object. 1) Attempt to deserialize the object and ensure deserialization fails if it should. 1) Attempt to use the deserialized object and ensure your program fails in the way it ought to. One can also use the constructor methods of `Inspectable` to manually build objects, for example when fuzzing. The core of the API is the `Inspectable` enum, which represents the core primitives of bencode: numbers, bytestrings, dicts and lists. The latter two may contain other `Inspectables`, so this forms a rough AST-like data structure. There is also an enum variant `Inspectable::Raw` which can be used to inject arbitrary bytes into a structure. Inspectables have methods for traversal of the AST, rudimentary search, and mutation of nodes. Most methods will panic instead of returning `Results` or `Options`, since any broken assumption should result in a test failure. Here is an example of making some modifications to an AST. ```rust # #[cfg(not(feature = "inspect"))] # fn main() {} # #[cfg(feature = "inspect")] # fn main() { use bendy::inspect::*; // { // aaa: 10, // bbb: "hello", // other: [ // {secret: "password"}, // ], // } let bytes = b"\ d\ 3:aaa\ i10e\ 3:bbb\ 5:hello\ 5:other\ l\ d\ 6:secret\ 8:password\ e\ e\ e\ "; let mut i = inspect(bytes); println!("Pretty printed repr:\n{}", i.as_rust_string_literal()); // Access list items or dict entries with `nth` i.dict().nth(0).value .replace(Inspectable::new_raw(b"invalid")); // Dict entries can also be accessed with the `entry` method i.dict().entry(b"bbb").value .set_content_byterange(0..2, b'X'); // A Path object can be used to represent traversals. // This is often more convenient, and enables re-use. // Above, the `.dict()` call is used to coerce from // `&mut Inspectable` to `&mut InDict`. When traversing // deep structures, this can get quite verbose. This is // not needed when using Paths for traversal. // Paths can also be used to search the AST. Searches // can only find one occurrence of an item. The first // occurrence in the serialized representation will be // found (depth first search in the AST). i.find(&inspect_path().search_entry(b"secret").value()) .clear_content(); // Or without search: i.find(&inspect_path().entry(b"other").value().nth(0).nth(0).value()) .clear_content(); assert_eq!( i.emit().as_slice(), b"d3:aaainvalid3:bbb5:XXllo5:otherld6:secret0:eee" ); # } ``` See the inspect module in the [docs](https://docs.rs/bendy/latest/bendy/) for the full API. ## Usage of unsafe code The parser would not require any unsafe code to work but it still contains a single unsafe call to `str::from_utf8_unchecked`. This call is used to avoid a duplicated UTF-8 check when the parser converts the bytes representing an incoming integer into a `&str` after its successful validation. Known unsafe usage from dependencies as of 2025-08-14: * serde and serde_bytes: Eight usages. Only a risk if the `serde` feature is enabled. * smallvec: Three usages. Only a risk if the `inspect` feature is enabled. *Disclaimer: Further unsafe code may be introduced through the dependency on the `thiserror` and `rustversion` crates. As of 2025-08-14 these contain no unsafe code.* ## Contributing We welcome everyone to ask questions, open issues or provide merge requests. Each merge request will be reviewed and either landed in the main tree or given feedback for changes that would be required. All code in this repository is under the [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) license. bendy-0.6.1/examples/decode_torrent.rs000064400000000000000000000166461046102023000161320ustar 00000000000000//! A decoder for torrent files. //! //! This example will ... //! //! - read a torrent file, //! - deserialize the bencode formatted information //! - and print the result into stdout. //! //! *Attention*: Please consider to pipe the output into a file of your choice. //! //! # Run the Example //! //! ``` //! cargo run --example decode_torrent > parsing_output.txt //! ``` use bendy::{ decoding::{Error, FromBencode, Object, ResultExt}, encoding::AsString, }; static EXAMPLE_TORRENT: &[u8] = include_bytes!("torrent_files/debian-9.4.0-amd64-netinst.iso.torrent"); /// Main struct containing all required information. /// /// Based on: [http://fileformats.wikia.com/wiki/Torrent_file]. /// /// # Design Decision /// /// To keep the example simple we won't parse the integers fields /// into a concrete number type as the bencode integer definition /// is actually a `BigNum` and the content may not fit. #[allow(dead_code)] #[derive(Debug)] struct MetaInfo { pub announce: String, pub info: Info, pub comment: Option, // not official element pub creation_date: Option, // not official element pub http_seeds: Option>, // not official element } /// File related information (Single-file format) #[allow(dead_code)] #[derive(Debug)] struct Info { pub piece_length: String, pub pieces: Vec, pub name: String, pub file_length: String, } impl FromBencode for MetaInfo { // Try to parse with a `max_depth` of two. // // The required max depth of a data structure is calculated as follows: // // - Every potential nesting level encoded as bencode dictionary or list count as +1, // - everything else is ignored. // // This typically means that we only need to count the amount of nested structs and container // types. (Potentially ignoring lists of bytes as they are normally encoded as strings.) // // struct MetaInfo { // encoded as dictionary (+1) // announce: String, // info: Info { // encoded as dictionary (+1) // piece_length: String, // pieces: Vec, // encoded as string and therefore ignored // name: String, // file_length: String, // }, // comment: Option, // creation_date: Option, // http_seeds: Option> // if available encoded as list but even then doesn't // increase the limit over the deepest chain including // info // } const EXPECTED_RECURSION_DEPTH: usize = Info::EXPECTED_RECURSION_DEPTH + 1; /// Entry point for decoding a torrent. The dictionary is parsed for all /// non-optional and optional fields. Missing optional fields are ignored /// but any other missing fields result in stopping the decoding and in /// spawning [`DecodingError::MissingField`]. fn decode_bencode_object(object: Object) -> Result where Self: Sized, { let mut announce = None; let mut comment = None; let mut creation_date = None; let mut http_seeds = None; let mut info = None; let mut dict_dec = object.try_into_dictionary()?; while let Some(pair) = dict_dec.next_pair()? { match pair { (b"announce", value) => { announce = String::decode_bencode_object(value) .context("announce") .map(Some)?; }, (b"comment", value) => { comment = String::decode_bencode_object(value) .context("comment") .map(Some)?; }, (b"creation date", value) => { creation_date = u64::decode_bencode_object(value) .context("creation_date") .map(Some)?; }, (b"httpseeds", value) => { http_seeds = Vec::decode_bencode_object(value) .context("http_seeds") .map(Some)?; }, (b"info", value) => { info = Info::decode_bencode_object(value) .context("info") .map(Some)?; }, (unknown_field, _) => { return Err(Error::unexpected_field(String::from_utf8_lossy( unknown_field, ))); }, } } let announce = announce.ok_or_else(|| Error::missing_field("announce"))?; let info = info.ok_or_else(|| Error::missing_field("info"))?; Ok(MetaInfo { announce, info, comment, creation_date, http_seeds, }) } } impl FromBencode for Info { const EXPECTED_RECURSION_DEPTH: usize = 1; /// Treats object as dictionary containing all fields for the info struct. /// On success the dictionary is parsed for the fields of info which are /// necessary for torrent. Any missing field will result in a missing field /// error which will stop the decoding. fn decode_bencode_object(object: Object) -> Result where Self: Sized, { let mut file_length = None; let mut name = None; let mut piece_length = None; let mut pieces = None; let mut dict_dec = object.try_into_dictionary()?; while let Some(pair) = dict_dec.next_pair()? { match pair { (b"length", value) => { file_length = value .try_into_integer() .context("file.length") .map(ToString::to_string) .map(Some)?; }, (b"name", value) => { name = String::decode_bencode_object(value) .context("name") .map(Some)?; }, (b"piece length", value) => { piece_length = value .try_into_integer() .context("length") .map(ToString::to_string) .map(Some)?; }, (b"pieces", value) => { pieces = AsString::decode_bencode_object(value) .context("pieces") .map(|bytes| Some(bytes.0))?; }, (unknown_field, _) => { return Err(Error::unexpected_field(String::from_utf8_lossy( unknown_field, ))); }, } } let file_length = file_length.ok_or_else(|| Error::missing_field("file_length"))?; let name = name.ok_or_else(|| Error::missing_field("name"))?; let piece_length = piece_length.ok_or_else(|| Error::missing_field("piece_length"))?; let pieces = pieces.ok_or_else(|| Error::missing_field("pieces"))?; // Check that we discovered all necessary fields Ok(Info { file_length, name, piece_length, pieces, }) } } fn main() -> Result<(), Error> { let torrent = MetaInfo::from_bencode(EXAMPLE_TORRENT)?; println!("{:#?}", torrent); Ok(()) } bendy-0.6.1/examples/encode_torrent.rs000064400000000000000000000073461046102023000161410ustar 00000000000000//! An encoder for torrent files //! //! This example will ... //! //! - serialize a torrent file representing object in bencode format //! - and print the result into stdout. //! //! *Attention*: Please consider to pipe the output into a file of your choice. //! //! # Run the Example //! //! ``` //! cargo run --example encode_torrent > example.torrent //! ``` use std::io::Write; use bendy::encoding::{AsString, Error, SingleItemEncoder, ToBencode}; /// Main struct containing all required information. /// /// Based on: [http://fileformats.wikia.com/wiki/Torrent_file]. /// /// # Design Decision /// /// To keep the example simple we won't parse the integers fields /// into a concrete number type as the bencode integer definition /// is actually a `BigNum` and the content may not fit. #[derive(Debug)] struct MetaInfo { pub announce: String, pub info: Info, pub comment: Option, // not official element pub creation_date: Option, // not official element pub http_seeds: Option>, // not official element } /// File related information (Single-file format) #[derive(Debug)] struct Info { pub piece_length: String, pub pieces: Vec, pub name: String, pub file_length: String, } impl ToBencode for MetaInfo { // Adds an additional recursion level -- itself formatted as dictionary -- // around the info struct. const MAX_DEPTH: usize = Info::MAX_DEPTH + 1; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { encoder.emit_dict(|mut e| { e.emit_pair(b"announce", &self.announce)?; if let Some(comment) = &self.comment { e.emit_pair(b"comment", comment)?; } if let Some(creation_date) = &self.creation_date { e.emit_pair(b"creation date", creation_date)?; } if let Some(seeds) = &self.http_seeds { // List is a simple iterable wrapper that allows to encode // any list like container as bencode list object. e.emit_pair(b"httpseeds", seeds)?; } e.emit_pair(b"info", &self.info) })?; Ok(()) } } impl ToBencode for Info { // The struct is encoded as dictionary and all of it internals are encoded // as flat values, i.e. strings or integers. const MAX_DEPTH: usize = 1; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { encoder.emit_dict(|mut e| { e.emit_pair(b"length", &self.file_length)?; e.emit_pair(b"name", &self.name)?; e.emit_pair(b"piece length", &self.piece_length)?; e.emit_pair(b"pieces", AsString(&self.pieces)) })?; Ok(()) } } fn main() -> Result<(), Box> { let torrent = MetaInfo { announce: "http://bttracker.debian.org:6969/announce".to_owned(), comment: Some("\"Debian CD from cdimage.debian.org\"".to_owned()), creation_date: Some(1_520_682_848.to_string()), http_seeds: Some(vec![ "https://cdimage.debian.org/cdimage/release/9.4.0//srv/cdbuilder.debian.org/dst/deb-cd/weekly-builds/amd64/iso-cd/debian-9.4.0-amd64-netinst.iso".to_owned(), "https://cdimage.debian.org/cdimage/archive/9.4.0//srv/cdbuilder.debian.org/dst/deb-cd/weekly-builds/amd64/iso-cd/debian-9.4.0-amd64-netinst.iso".to_owned(), ]), info: Info { piece_length: 262_144.to_string(), pieces: include_bytes!("torrent_files/pieces.iso").to_vec(), name: "debian-9.4.0-amd64-netinst.iso".to_owned(), file_length: 305_135_616.to_string(), }, }; let data = torrent.to_bencode()?; std::io::stdout().write_all(&data)?; Ok(()) } bendy-0.6.1/examples/torrent_files/debian-9.4.0-amd64-netinst.iso.torrent000064400000000000000000000564151046102023000241340ustar 00000000000000d8:announce41:http://bttracker.debian.org:6969/announce7:comment35:"Debian CD from cdimage.debian.org"13:creation datei1520682848e9:httpseedsl143:https://cdimage.debian.org/cdimage/release/9.4.0//srv/cdbuilder.debian.org/dst/deb-cd/weekly-builds/amd64/iso-cd/debian-9.4.0-amd64-netinst.iso143:https://cdimage.debian.org/cdimage/archive/9.4.0//srv/cdbuilder.debian.org/dst/deb-cd/weekly-builds/amd64/iso-cd/debian-9.4.0-amd64-netinst.isoe4:infod6:lengthi305135616e4:name30:debian-9.4.0-amd64-netinst.iso12:piece lengthi262144e6:pieces23280:0Żp?r 'frRW{Sh( kr]I7jU#Ȫxst.$@M [_>;˘gb9y {?1k%T^@c΄f0vJBE B0XP̸@eXtpݤGq'қTq~O`fxwrN[|Ɍ,, ^p ԳHjSK#[4B#]U%@+ }P 6(4~hJN0|˥F p" ҏ!# c|*[o$c =6 f3m,) =F _AQ| )Do-Bq=Cd4.+Z"$cKjm\!/]gˍ ֽMjGUsR7_>׭dgplH۩?_N= ?-!cҪg<)Tɘm1"H 3q倅GmK[ fϏCt۬T&-m-o.DπLg/w,c)2:,OA0\bR^CA_(jH(a!fh֜V |ѩ2C X.'k' z`v4}ELF0l | chP~{J[{ҞEJ$ɽr頮OZgL+h{WY&Ǿf&x"/yr  4%lI8mj%gpt9j,gLZk>E|])Mq\5F./*qiЊf;-_ZE5ᡟp>=8 4,:#>5Vjk ¤zPXb!mTS&WMk0 6 CcEV-,WV׋`/Jv|w3N>kti G;BZרK6Ż,s9M5 FFCc9]}f[8Ɠqϸb~|~D>9^ݲnU&VŒ\ W_S?^Yc05sXyU[IKs,?+^,sþY =ڂ5jq#}P%,4hj>Piu78 9[&*AyWB|܄V2"OVj4+)s3 Dgsߊ'|S"ٕhͳu$irTDg xHW%zvW:^w{ QUKnT˃Q_isN ~*34iAjgU,= *˚} L`> RT4aWE}VG &oЈn/mךrN<,NV-x#1qbW<͕c4rc ' 9 )`` F49MӅ8c܄ `LEo P3sGc1W1_.켐;u&#Q$I-W"Jr9VAFIlvbt/ƐH7xǩCI^P&!.Of(MJgPCF0$5^G] hN\[TD 8 a;Lc`W } = t+g0ʎ  7O^mv 0Mg4ICYYC2fOW_Wg'ԅtqc?gGb!4]tK?Gʣ<tby]`u_l oY7F}ϼRu5; 7,-ک 7fJ' NyT+ C(g*@W ZLj5m'%̗A>5lAOH8/l#>+O>Iga' BC48ΰ(J..*+49,08AoH `a<=uHI3SCN8vɞyW>Qski&YU}3_ݡ-Ls/4gj߭a~u@STTjC#⛹`I|[TZH Hú:ab^pxMY2m~"rADGyYSq 6jQqNo87"s/h~Ӝ Gmk) s?]ˆzZ$Ȣz)Jqd2"/ЈT-kCGT a?x){ Xs's wSlB743@ K>118'vkY˧A18H+s,>BMoCiK8e8Eİ8i*Gj,աw;X2ueo-ifJlBj-zaeBiW[D~CW#NY"r#?)ρG p`.F9%˿V Ń!/7YLV;_T}aW«R-K'(2|Bħ+w&AdUNmؖ- ֮lUIo5V:Mmf[ؕ1SC32ֈ+s4e23 }3.tIdd"QADkc:3sOW5<e^3>pa9(E8j R=؁u$hSq.vM,A, NS Y͵DfptWjN$9 o 7$)QB~҃ \Hl AP=pi6'}k Zu42Q:"-L7?6jm߆^/(C~Z.R:B9 Pشr;Kׯ>?qMr%Juijl[NY/h~ql[~g1SV {JͥĨӎgD dm"aL+4 cLYkU LCA%g6;^J[$5/%D4ϢA@ƚD!x+ojaS0@ks2WB*#; OpbkY. `?${D[8)B*rDΌY{1R&p%\Itd D@YRR{nҔh{0 Xpm^]ҧnWۑ=sK9skXssq_o Կ92@#'Ɉw SWp^!a3zښ&em%)TUXvS}&.[z%ʓ2Tn؜N]?|j[K% j[ݑ_-O\tגxja&AXP@vBBrq|jJFGZ0 vwgc]#=@|׺Z~>|#ˢԘ)W]EjيˡjE;@S>G7`L`|'D1X~x=7G"dvg "lZg$Im(o7;gH4Ad]D#0|ވ`8h{_-;ҋ!uaC.'AO{e9bJ}5 Q1Ol$>_~MRy|Ҋ䶌vc~n4EI$ F̞(6k ZRcT#jZ QU@zUut/m.$7ӕȢ*e=-׫8''^U:®lď` D' >hdylr*.Ou6ԅDRK+Hߙ:S W X`&'{| ]S|WDF<S'oqَшޠK&X0&\KP1 #Qo\VV;.j*jĕ%ubs#=,ՊN6 d= Snآn0o9Kџzۨ.p%=}?f]in0,UTAݙvSV瀍fA1#ų-hKcrZnܭUz,ѯzC:>.XüCxT(}ny~ GHD.JCI+" E\cx[`X~)үʩzΗlT`pȅ"ZڣnbzMHMTK|OVF_ee@612:mZ !c<ʃ'՝ b*ěo|$29ɇhI zVē;oyRڱUV,< sm ZV>B0ۃHHw}53F?:8h`;aHeYʬej'#g&%_1r@NRÓ'isYbenB<3ѧt5$$aYhh@'*@>)5ZGlO$~ <_!upvLʴ.O 7\4 ~F ;7Lns0`(M*켹2TUínol B( j_WsBzŖGڜF;1/{?ρέfF !)i)fgl/ 3ԿՃ:z#jOjPu 'Y:vIj; ,A˺98UGߣ+fѳ Vұ=yʦkYK}+YdAx;jX%Ĥ bş+Fw־kZCqA&y!ΰw<"M[Mz =ٿ}nY\i=_l5M%QU ؉?0Io05LQaf+ぎ*j-XG@jSE'0/2db њGb=F*S,zzlFAZDZm/Lm{v-Q⌐Iإ`+{?v]tǚ%qJHKVMCNL1q "Q fR nJ 63|*} .虦[=+p3˓ X->pM-s& _5&: 7=sVJ/6 َXGʁmϟNA5ZhB4HDYKQ3{:ˋq=L6(?Q9Q#$đ;HkRi{LRB,Z;e쌯vFlgUrzDVxQ8߻|2T(^#ȼઠ;a)"WϪywT p%4$N]QOjf@X+vX/TPvvrz3`HC\S鵰,#tKdsjY% tO݉ui"^-`݅qg5 (UH#t~d|p.m (;2_4b^]b x~LB7C[ fI x!wܲ_1?o 1$ՀJsMʕJ D@ d=|H-x]0d2zZ]*wϡL^ ȀOO _%i+IYP!(j8Zp*GGфY^uN,{9Q hxd3L$S.xI,b&r*o,WE\=)YFMMKK_+ACq)HaAO] |c}t/e`&ʌ`O9VmNZ/$3-O,}BZkgMD@RHO%Fr/v^Wmz{j D? 9id_u΋ ]*-JF{?8'o~Oay/l6DgzYkc#;_]dNpFpobQAC)',l5x)9ƌbN7D"4j6`c+7Ӂj-HAk:+1qW-/XXtB<ɺ8%5p [Ll] z ͱ ~'=Qf.g$fK-E5q=,hKxT.G[7~=Pdz] b\QXz-5'.ܷp3rO}E.HS:<1ugf6Yq Ri!iN#\GZ==G^BZ5[ЉWѳz\~yQN7DBA|"Be3ey= E]y[k=3T[7)NY|UT:BB*BL=|̢s+e~%GVS_%(&!$x'3MTC|I|L5MUt1Z]kWֺZqA?_طGϯqpdx Ӭ.jzn:Mkj㸲8>cXL[YۅhEd)f& HW]W/#Q7iדvDA#^-lio7kMo|fҏ@'M6 pCv7`nKs}n< d3KfJ6csU", Om6>B#&>]G+0>!n!f{O"sOU #e:* m@X`Tx0us]'-z˓@s9[ Np,$d+.]qAJ7;%gN*.u@)d$&Q2LFgHNnșaO;֟ x2Bv.cbcD@_E Wڬ.N{N[ jsv6]wenXOyan2IFR>擭5~*Flo^! \;9@MX@\Π^bg`@c* +ɊMZ.Fc:"N@Y _yuk1jNNwT4eReitVX8/-7kdi4β]b3V֤r Qk}Lk'v9r{og;o )_oC bN+AHQo:#IKttq^# 6ǷS Nu@E ͠ae๸k͊k] %qhW K9@~1x@l(4yw%|r?éew>+v~k-Kz5MS6+FeZȞ|/bQ&[Q)c8y3DQKl[ p1'd"T=|"@ބE5) g2H{ZkMtۨaN*!^ ըY}][Ϙ}Idj@p+"Ҫ ~iվO޸5xhSUW]GXmKgnl"^}`貵.Z-ؘ% ūqm*Gh,?1 7RzMDhݨ")keA)Ӈel"ȧd<RbQ- 1)pe'*z;8UsT #8L@t1^:-hE<5VX<4"Ldų1zK .(I~bƪe T4IݤUcE3ЈxV$Vd,`Y,N9!YŠ䆸p52!0S ~oOϱAi%A?isCFXWt/$3j^/gml)ރ¥y꙰C'`m/m$]ܿ`O `)U,GEmp-ɨ"(yZE fF̱J~ )O.=5&n*rE]ȣBԊ5BmpHJ]6D3@XꝘAv9=IREP{T:ʭ":w?&~͟|h(!iȢ1t{$K !l,Zc:bw`LVd(1o 3o h!9i&7b{8v>:'pn3U/a"W@MSQzE*՚ҠQw)hI8#QNնf$iFn$ mѸ83NyIP&|UokV"3ְu&hHijTx=JG/Ι0?Ƈ/rhȑ~]`*WII4u~XJ_9 }n|Br.0pRTfԥ3"'Ll0nT2*P$:ڃKpj*)_ <0q NuO+(սNGcBoŐE~v!zH9Q+Sp!skN%yJk'2 מeEV0+v7HD JQ"fґlbcDyPK^" $ܽY\@5R˕4NR-9 I/v ΂jeq]Xf !؄xPPl,rRR?՟&!rqqwrljӰ:'jH@Ho|g>{C\#'T]*$FK0IG=UP~̘7s@=lHYȿW"AO1;=2@^?#[kY-jlFT?Lb׫ ؽ>Z=<;!bZRI-1[SA'Ykwךײ҆å:Bn?:U)#%V)Y‚ A$'OO"81$׊{ǖ.}@6qІͨᨦorӸW@+3Z-{sZdӢ\`]HSl\cu|x\}~W1𕈱P$Q9J׎Y2%+=aᅠ 2M4;$y훍" f?לLTqa)Y0Ur~%fY/i5C|5l/?1k.W!( ]rۈGiu!RTd1Y9UQ AIΗ$r`P[qmlP;8ELW$Ӓ`p_'>=(#-!LM\c#x[ /CgO ]")_m٢@'vY[h{ /YkAG-a5b(?>!bDY,8܊Wx0fmDDVsli)U:|>x]/5|at,M*ra(cA(SD7WٕD0; MUq+uyR𑑚o/'òUIVj,aʍ[W />"`m֨ڻ)Hy&qH^uQ8橉(8:i31:_od%8"0=H1SyxK=<74;AGF}z5r##<"ǡ2c'M 2@P6r(:YKAf)/$:ȁ 5^#koހlni#)Rkv#qBP׀eF*8aa/ 8)V>nAL`z]*ɥ?Y3"7#l )]s:iRl"}0ãӅ4COwv$.i|U/0m]'#ɻW4#%auOy-Vw:!G%*)XOL؃e REs\&:4:x)80RşG. z3جm+n6٨`4lMWWM{sL 7;=BO\,ka7_vO X_e:7,rvrƼN#),u76~d3~ָݫbed>z"_b)/V d&+,L|$)ZWu["\g(B#)F;#̫/ЮW7iQfz,67OI&-E5[Ÿ>KyUF؁E nICO#a\vMQoh~uzr)̋aSxogN= f臓Ӣ鑵]R~h !\ 4\<_UJ" >p,%RJ{EF{_He-Y 18ng"Rs qKaIj,X#sT=:^[݉+ {*꺻kY( 1'Ɓ_<9hm>3dny-DRWEtٺE%@R#ub5bCMhwmu4ap}>oy%o%$VEFt`JOVް*rEuD䚥B􏍵]bT T/A@ac7u2VNG>X h%W7a~\^Hc0l6_V{: GrPzcMqV~81EzyE@:2pbڙI~OrLQ8DjMIp[]Iq-)o{Nlh69Ym!HN^URµ"3C8$l% ^xckvYҲW{MLOOg`n 1if8J]NB)?ԟ8̍La4YRK)<wi.m @yT9$[Ά0 *`usr %F=)#z)x56=/07<6yQ^PH_u&#KȷN5lt(ןvG _&Cy(9]KR|%K]Qz* '7gvuB'iZp9[:}y$pI7K HH.#3:tq*d7Tv R/4Bh"y LMHZ*)eQ;ǚ1Gd0rGWޠRȍ 0@a˓Lث(Vf|r2R5\#y€N}#Ȗh|h~_U;P#$Edgm]~J\(27߹xMVfk=Warnp)E˞ع27dd)TCr;(K[R@"ctLc}bOAb2JSf`{xP|ׂH: ( 2#oýA\+IY|;P J^DIf"p`>EiYW6:?\)o@ K I[d3sjd1/e(BqeCk])|| 3j?y(7‰$:{q{q[T٣29 #ڎiW]턆"-g_^ـbdm1OvTl=,ն~Z9YE8~8b`qk~VP5G #>.0gR2Z]osպ 7U;]rQ9& [8ψhmau ޷^DC'g 26V0ffi3wwsvv?8c}`v6 fh][t oϩ1-O*KS1i1(qdv]cQ-`u}MԙI ؉oYFqH c8+l^-ourdåp#6a S= $S}+^kR#ӑQW mB8H4TM zOdbױPpU6.pTZ'oe%SD)4/?B\Y| exbiݟ@%aJh0C2+Ά ܔ;#đǝ萒~3+%/iw@.;ɂf&F5Ǭt1SC ^e6ХAS}xwh' $|?3_dԓGCJ.{Fg+|ɿtyYBv7 R%"}fqW~4ʁ=P{%]:7nꦉuyL6F1RdQL5苣rXrW~̀"sRrUI/A@/BXcwCX/[sGZV QG)*_dȝjzā Fu@cSYK:^Xw'9(TW|>T w cdy$8Zm*^`ӤƓ˦6m-n!CV?ePHi`#I E 8d/W +KӛI 21JϧyY5|b+l""w؂2p8:nҩDMFw-lk{Ǟ,.B(d_sw0z* [J ӐQƕ}1$]lڠ<'$}zuZANL|@f-!P$ؠ_ Ӡ'z#jnݖ j^,X$AT1@36Waœl8ޓqM8E˭U|KLdkb0aNyja=WcNHqD ܑsT/Hqlme=HLp(Qx'= /&/U-0DQ;2 IWRx]+ čv7\2q.#S:ug|^iWl ~PvLC_vTfٯI4JB m:3lnd}X' T0'Gp2u4Tz/TP'g<–YfFy"}IPjtj/&H}; kmvh} ~on~ [ }:gsfuNFUԬ/vFpܢM92OiZZ?ɒKY EƐ3 HnڎSu_R\Lu%M@ҵ.Qᄁrll_jHc'RHteꨙY;ܬuC_XSWla =/Z_6`$kiYj&!v .l*0<q^2^S /Ɉݜ&#4ZV9Z5SڡNc :t=#ǭaqAD׮fJؑ, 2lhwwŎ?'*??d[#Y3jLlj~j}%U@um]̮LE͖:i1>-MGsEW0'*U `N:nCv~t+H{';M1qrv%fC_⁳َBNh*7F[`.9LrM_&Gqqŏ)Y*""dS҅ΐ!;e1k!1pq44gl°r T^YW*2j J|a(^^;^VC$H!TC}pZ3Ǣrj9LcӺ3'iyIoB[[ F]Pqh\1YpL6+ `%*TTRM Q[41۠C遤v]k$.12[1X=CA"g])6Mk;>DT7Cv`! ,|1  'JW_?msu'vw~nBeUr">ƕu~lq,eZm&^by0+:t3r(6zǂ02ҳS *x釻Yd'}}hfIRUƻ)\Ŷ&ռ-i@ vcÖV à2|GC3gv~kڙs"#2wW8 iӤmՙ?Srr;ޒf({Y7|H?q{"u ؊Ywbs[a-Y$A^ TXg5N#Å DKтYTy,0Ps`s}Kw*19ycZ›-^ prmBd_]tnDY{C3`tyiű:!r; }/1 )HVńkץ-2T@EzRl0d)iXHp5cyfKLx/g4TPU7v--tjd ]o#ܪu?dlv(Un&dnM//ܾ|nf!&'  ۵y XB=pdkhwRa# *cA/]`F`(#o`[M8w|vֻLIri_&W8T5rA뉑xe':+x07EPocSaOo:/*+1x Yv ̰K«ߛH=?Q/y(Hѧo{0n-A~1B)Iz=Vpx A-j*zuk!5p:r@^[8m򯳋_n9IC}XO2UoLbo⚓>n)nv )N<Ej*u&owO']w{;IՖzYO$uZ#MdQo# qb,S|s|Ct:$ te yJ?2my+SD' ZtKA1\^)G3m!} ]h,*!&W޳˷-ģЛ?+uo8qV?, p%O[E{#͞h}Wc=9wuN} s1 >w([xD'n)9ubOSVCgKi\o>cy\t@'htTd,@v>WbW4GMVHi.3*`QP4Q_ɾݍ<5-JCYw!vLCf`a88؛-kc8")7}߆`) k -Rhc5f=[WLu7-fBtdD)XRL+T|F'FuŶSkC%]~ß_m^2M=;a'ru5kA U9Z։5A)`7v=ҵ&8V t7u{u5Ԙ}=X&, wN801&F yWs)+Nk$] (bFT78kgw64 yhXq}cRlPf,S:B'ײ%/(pXGqLEPg(&/^P ˘ۼAcn,\/}5IX` [G* xy}c(F N& ?CB $sg2)#o k%Y)p-͵Qd4ɛhV7MXGM]nMn;jXe2\sѲJ&p^NJLQT1 ;tO⌢ (xJ3c^QZ}H|+= .$O-vA\)*IiIƸ26uw~>2"EBvd. *uXXz]bb6Yy3W/4_:&?6\1xT_g ܲolZ 泘%ِxׁhAF,$}qCckw._+"kG/΅8 Zz QG07'vaT$~VZ!J]8h-٫A;d]:kI3rqQBQIv; J}h5_4uv.SG/u{Hug"4t2W%\^\< Tlr((6o\S:Jc%[GvFLqDzyD~C1F'#lYNCtx5=5zJ47ti za@`6q%坍giލž~ZyM  i>0syg#Ʃs5M^7>vw,UH" ~vef'Ĭp\v(6oMH .# I1X?TCkIZiF:)Li쀸 Rr 'cuo9||{S*XUXE$J -DJӤȹqCYYo!X-#Sk;:/OH ϩB"oĤO<ɶZh*M:f.nW9qU3C:(PHUl&@z<_ Ay. K!pll[؀;8_42N՟S*n &x)cɼ`!J|Ba|'<5[MTcge֎t2~jBѓ_n?k$Dy~pQVJC w%Dc[nur-`urgE~RѠh:T^^ԐXGxV4UM7*ܮ)t}7PE"߈ WW.FA?)[N}m9+- eBˇ3HaݪZ¹5~nPTJr4Ǎ$85%Ya_E,[6 0zJ[L2ý#=FпDU*IFCV O9ޭe4yr/ ">QSPw|+l4܁5r$uyώ</oA f@;F8#Ȫv9PRV wH1.3Uay)Kty>mV.Na]Z gznYO˞vR D3>;eЄfځ“*dUF`Q1 u0\il=|!.WYT>Y.WYT>Y1E/{2eebendy-0.6.1/examples/torrent_files/pieces.iso000064400000000000000000000553601046102023000174230ustar 000000000000000Żp?r 'frRW{Sh( kr]I7jU#Ȫxst.$@M [_>;˘gb9y {?1k%T^@c΄f0vJBE B0XP̸@eXtpݤGq'қTq~O`fxwrN[|Ɍ,, ^p ԳHjSK#[4B#]U%@+ }P 6(4~hJN0|˥F p" ҏ!# c|*[o$c =6 f3m,) =F _AQ| )Do-Bq=Cd4.+Z"$cKjm\!/]gˍ ֽMjGUsR7_>׭dgplH۩?_N= ?-!cҪg<)Tɘm1"H 3q倅GmK[ fϏCt۬T&-m-o.DπLg/w,c)2:,OA0\bR^CA_(jH(a!fh֜V |ѩ2C X.'k' z`v4}ELF0l | chP~{J[{ҞEJ$ɽr頮OZgL+h{WY&Ǿf&x"/yr  4%lI8mj%gpt9j,gLZk>E|])Mq\5F./*qiЊf;-_ZE5ᡟp>=8 4,:#>5Vjk ¤zPXb!mTS&WMk0 6 CcEV-,WV׋`/Jv|w3N>kti G;BZרK6Ż,s9M5 FFCc9]}f[8Ɠqϸb~|~D>9^ݲnU&VŒ\ W_S?^Yc05sXyU[IKs,?+^,sþY =ڂ5jq#}P%,4hj>Piu78 9[&*AyWB|܄V2"OVj4+)s3 Dgsߊ'|S"ٕhͳu$irTDg xHW%zvW:^w{ QUKnT˃Q_isN ~*34iAjgU,= *˚} L`> RT4aWE}VG &oЈn/mךrN<,NV-x#1qbW<͕c4rc ' 9 )`` F49MӅ8c܄ `LEo P3sGc1W1_.켐;u&#Q$I-W"Jr9VAFIlvbt/ƐH7xǩCI^P&!.Of(MJgPCF0$5^G] hN\[TD 8 a;Lc`W } = t+g0ʎ  7O^mv 0Mg4ICYYC2fOW_Wg'ԅtqc?gGb!4]tK?Gʣ<tby]`u_l oY7F}ϼRu5; 7,-ک 7fJ' NyT+ C(g*@W ZLj5m'%̗A>5lAOH8/l#>+O>Iga' BC48ΰ(J..*+49,08AoH `a<=uHI3SCN8vɞyW>Qski&YU}3_ݡ-Ls/4gj߭a~u@STTjC#⛹`I|[TZH Hú:ab^pxMY2m~"rADGyYSq 6jQqNo87"s/h~Ӝ Gmk) s?]ˆzZ$Ȣz)Jqd2"/ЈT-kCGT a?x){ Xs's wSlB743@ K>118'vkY˧A18H+s,>BMoCiK8e8Eİ8i*Gj,աw;X2ueo-ifJlBj-zaeBiW[D~CW#NY"r#?)ρG p`.F9%˿V Ń!/7YLV;_T}aW«R-K'(2|Bħ+w&AdUNmؖ- ֮lUIo5V:Mmf[ؕ1SC32ֈ+s4e23 }3.tIdd"QADkc:3sOW5<e^3>pa9(E8j R=؁u$hSq.vM,A, NS Y͵DfptWjN$9 o 7$)QB~҃ \Hl AP=pi6'}k Zu42Q:"-L7?6jm߆^/(C~Z.R:B9 Pشr;Kׯ>?qMr%Juijl[NY/h~ql[~g1SV {JͥĨӎgD dm"aL+4 cLYkU LCA%g6;^J[$5/%D4ϢA@ƚD!x+ojaS0@ks2WB*#; OpbkY. `?${D[8)B*rDΌY{1R&p%\Itd D@YRR{nҔh{0 Xpm^]ҧnWۑ=sK9skXssq_o Կ92@#'Ɉw SWp^!a3zښ&em%)TUXvS}&.[z%ʓ2Tn؜N]?|j[K% j[ݑ_-O\tגxja&AXP@vBBrq|jJFGZ0 vwgc]#=@|׺Z~>|#ˢԘ)W]EjيˡjE;@S>G7`L`|'D1X~x=7G"dvg "lZg$Im(o7;gH4Ad]D#0|ވ`8h{_-;ҋ!uaC.'AO{e9bJ}5 Q1Ol$>_~MRy|Ҋ䶌vc~n4EI$ F̞(6k ZRcT#jZ QU@zUut/m.$7ӕȢ*e=-׫8''^U:®lď` D' >hdylr*.Ou6ԅDRK+Hߙ:S W X`&'{| ]S|WDF<S'oqَшޠK&X0&\KP1 #Qo\VV;.j*jĕ%ubs#=,ՊN6 d= Snآn0o9Kџzۨ.p%=}?f]in0,UTAݙvSV瀍fA1#ų-hKcrZnܭUz,ѯzC:>.XüCxT(}ny~ GHD.JCI+" E\cx[`X~)үʩzΗlT`pȅ"ZڣnbzMHMTK|OVF_ee@612:mZ !c<ʃ'՝ b*ěo|$29ɇhI zVē;oyRڱUV,< sm ZV>B0ۃHHw}53F?:8h`;aHeYʬej'#g&%_1r@NRÓ'isYbenB<3ѧt5$$aYhh@'*@>)5ZGlO$~ <_!upvLʴ.O 7\4 ~F ;7Lns0`(M*켹2TUínol B( j_WsBzŖGڜF;1/{?ρέfF !)i)fgl/ 3ԿՃ:z#jOjPu 'Y:vIj; ,A˺98UGߣ+fѳ Vұ=yʦkYK}+YdAx;jX%Ĥ bş+Fw־kZCqA&y!ΰw<"M[Mz =ٿ}nY\i=_l5M%QU ؉?0Io05LQaf+ぎ*j-XG@jSE'0/2db њGb=F*S,zzlFAZDZm/Lm{v-Q⌐Iإ`+{?v]tǚ%qJHKVMCNL1q "Q fR nJ 63|*} .虦[=+p3˓ X->pM-s& _5&: 7=sVJ/6 َXGʁmϟNA5ZhB4HDYKQ3{:ˋq=L6(?Q9Q#$đ;HkRi{LRB,Z;e쌯vFlgUrzDVxQ8߻|2T(^#ȼઠ;a)"WϪywT p%4$N]QOjf@X+vX/TPvvrz3`HC\S鵰,#tKdsjY% tO݉ui"^-`݅qg5 (UH#t~d|p.m (;2_4b^]b x~LB7C[ fI x!wܲ_1?o 1$ՀJsMʕJ D@ d=|H-x]0d2zZ]*wϡL^ ȀOO _%i+IYP!(j8Zp*GGфY^uN,{9Q hxd3L$S.xI,b&r*o,WE\=)YFMMKK_+ACq)HaAO] |c}t/e`&ʌ`O9VmNZ/$3-O,}BZkgMD@RHO%Fr/v^Wmz{j D? 9id_u΋ ]*-JF{?8'o~Oay/l6DgzYkc#;_]dNpFpobQAC)',l5x)9ƌbN7D"4j6`c+7Ӂj-HAk:+1qW-/XXtB<ɺ8%5p [Ll] z ͱ ~'=Qf.g$fK-E5q=,hKxT.G[7~=Pdz] b\QXz-5'.ܷp3rO}E.HS:<1ugf6Yq Ri!iN#\GZ==G^BZ5[ЉWѳz\~yQN7DBA|"Be3ey= E]y[k=3T[7)NY|UT:BB*BL=|̢s+e~%GVS_%(&!$x'3MTC|I|L5MUt1Z]kWֺZqA?_طGϯqpdx Ӭ.jzn:Mkj㸲8>cXL[YۅhEd)f& HW]W/#Q7iדvDA#^-lio7kMo|fҏ@'M6 pCv7`nKs}n< d3KfJ6csU", Om6>B#&>]G+0>!n!f{O"sOU #e:* m@X`Tx0us]'-z˓@s9[ Np,$d+.]qAJ7;%gN*.u@)d$&Q2LFgHNnșaO;֟ x2Bv.cbcD@_E Wڬ.N{N[ jsv6]wenXOyan2IFR>擭5~*Flo^! \;9@MX@\Π^bg`@c* +ɊMZ.Fc:"N@Y _yuk1jNNwT4eReitVX8/-7kdi4β]b3V֤r Qk}Lk'v9r{og;o )_oC bN+AHQo:#IKttq^# 6ǷS Nu@E ͠ae๸k͊k] %qhW K9@~1x@l(4yw%|r?éew>+v~k-Kz5MS6+FeZȞ|/bQ&[Q)c8y3DQKl[ p1'd"T=|"@ބE5) g2H{ZkMtۨaN*!^ ըY}][Ϙ}Idj@p+"Ҫ ~iվO޸5xhSUW]GXmKgnl"^}`貵.Z-ؘ% ūqm*Gh,?1 7RzMDhݨ")keA)Ӈel"ȧd<RbQ- 1)pe'*z;8UsT #8L@t1^:-hE<5VX<4"Ldų1zK .(I~bƪe T4IݤUcE3ЈxV$Vd,`Y,N9!YŠ䆸p52!0S ~oOϱAi%A?isCFXWt/$3j^/gml)ރ¥y꙰C'`m/m$]ܿ`O `)U,GEmp-ɨ"(yZE fF̱J~ )O.=5&n*rE]ȣBԊ5BmpHJ]6D3@XꝘAv9=IREP{T:ʭ":w?&~͟|h(!iȢ1t{$K !l,Zc:bw`LVd(1o 3o h!9i&7b{8v>:'pn3U/a"W@MSQzE*՚ҠQw)hI8#QNնf$iFn$ mѸ83NyIP&|UokV"3ְu&hHijTx=JG/Ι0?Ƈ/rhȑ~]`*WII4u~XJ_9 }n|Br.0pRTfԥ3"'Ll0nT2*P$:ڃKpj*)_ <0q NuO+(սNGcBoŐE~v!zH9Q+Sp!skN%yJk'2 מeEV0+v7HD JQ"fґlbcDyPK^" $ܽY\@5R˕4NR-9 I/v ΂jeq]Xf !؄xPPl,rRR?՟&!rqqwrljӰ:'jH@Ho|g>{C\#'T]*$FK0IG=UP~̘7s@=lHYȿW"AO1;=2@^?#[kY-jlFT?Lb׫ ؽ>Z=<;!bZRI-1[SA'Ykwךײ҆å:Bn?:U)#%V)Y‚ A$'OO"81$׊{ǖ.}@6qІͨᨦorӸW@+3Z-{sZdӢ\`]HSl\cu|x\}~W1𕈱P$Q9J׎Y2%+=aᅠ 2M4;$y훍" f?לLTqa)Y0Ur~%fY/i5C|5l/?1k.W!( ]rۈGiu!RTd1Y9UQ AIΗ$r`P[qmlP;8ELW$Ӓ`p_'>=(#-!LM\c#x[ /CgO ]")_m٢@'vY[h{ /YkAG-a5b(?>!bDY,8܊Wx0fmDDVsli)U:|>x]/5|at,M*ra(cA(SD7WٕD0; MUq+uyR𑑚o/'òUIVj,aʍ[W />"`m֨ڻ)Hy&qH^uQ8橉(8:i31:_od%8"0=H1SyxK=<74;AGF}z5r##<"ǡ2c'M 2@P6r(:YKAf)/$:ȁ 5^#koހlni#)Rkv#qBP׀eF*8aa/ 8)V>nAL`z]*ɥ?Y3"7#l )]s:iRl"}0ãӅ4COwv$.i|U/0m]'#ɻW4#%auOy-Vw:!G%*)XOL؃e REs\&:4:x)80RşG. z3جm+n6٨`4lMWWM{sL 7;=BO\,ka7_vO X_e:7,rvrƼN#),u76~d3~ָݫbed>z"_b)/V d&+,L|$)ZWu["\g(B#)F;#̫/ЮW7iQfz,67OI&-E5[Ÿ>KyUF؁E nICO#a\vMQoh~uzr)̋aSxogN= f臓Ӣ鑵]R~h !\ 4\<_UJ" >p,%RJ{EF{_He-Y 18ng"Rs qKaIj,X#sT=:^[݉+ {*꺻kY( 1'Ɓ_<9hm>3dny-DRWEtٺE%@R#ub5bCMhwmu4ap}>oy%o%$VEFt`JOVް*rEuD䚥B􏍵]bT T/A@ac7u2VNG>X h%W7a~\^Hc0l6_V{: GrPzcMqV~81EzyE@:2pbڙI~OrLQ8DjMIp[]Iq-)o{Nlh69Ym!HN^URµ"3C8$l% ^xckvYҲW{MLOOg`n 1if8J]NB)?ԟ8̍La4YRK)<wi.m @yT9$[Ά0 *`usr %F=)#z)x56=/07<6yQ^PH_u&#KȷN5lt(ןvG _&Cy(9]KR|%K]Qz* '7gvuB'iZp9[:}y$pI7K HH.#3:tq*d7Tv R/4Bh"y LMHZ*)eQ;ǚ1Gd0rGWޠRȍ 0@a˓Lث(Vf|r2R5\#y€N}#Ȗh|h~_U;P#$Edgm]~J\(27߹xMVfk=Warnp)E˞ع27dd)TCr;(K[R@"ctLc}bOAb2JSf`{xP|ׂH: ( 2#oýA\+IY|;P J^DIf"p`>EiYW6:?\)o@ K I[d3sjd1/e(BqeCk])|| 3j?y(7‰$:{q{q[T٣29 #ڎiW]턆"-g_^ـbdm1OvTl=,ն~Z9YE8~8b`qk~VP5G #>.0gR2Z]osպ 7U;]rQ9& [8ψhmau ޷^DC'g 26V0ffi3wwsvv?8c}`v6 fh][t oϩ1-O*KS1i1(qdv]cQ-`u}MԙI ؉oYFqH c8+l^-ourdåp#6a S= $S}+^kR#ӑQW mB8H4TM zOdbױPpU6.pTZ'oe%SD)4/?B\Y| exbiݟ@%aJh0C2+Ά ܔ;#đǝ萒~3+%/iw@.;ɂf&F5Ǭt1SC ^e6ХAS}xwh' $|?3_dԓGCJ.{Fg+|ɿtyYBv7 R%"}fqW~4ʁ=P{%]:7nꦉuyL6F1RdQL5苣rXrW~̀"sRrUI/A@/BXcwCX/[sGZV QG)*_dȝjzā Fu@cSYK:^Xw'9(TW|>T w cdy$8Zm*^`ӤƓ˦6m-n!CV?ePHi`#I E 8d/W +KӛI 21JϧyY5|b+l""w؂2p8:nҩDMFw-lk{Ǟ,.B(d_sw0z* [J ӐQƕ}1$]lڠ<'$}zuZANL|@f-!P$ؠ_ Ӡ'z#jnݖ j^,X$AT1@36Waœl8ޓqM8E˭U|KLdkb0aNyja=WcNHqD ܑsT/Hqlme=HLp(Qx'= /&/U-0DQ;2 IWRx]+ čv7\2q.#S:ug|^iWl ~PvLC_vTfٯI4JB m:3lnd}X' T0'Gp2u4Tz/TP'g<–YfFy"}IPjtj/&H}; kmvh} ~on~ [ }:gsfuNFUԬ/vFpܢM92OiZZ?ɒKY EƐ3 HnڎSu_R\Lu%M@ҵ.Qᄁrll_jHc'RHteꨙY;ܬuC_XSWla =/Z_6`$kiYj&!v .l*0<q^2^S /Ɉݜ&#4ZV9Z5SڡNc :t=#ǭaqAD׮fJؑ, 2lhwwŎ?'*??d[#Y3jLlj~j}%U@um]̮LE͖:i1>-MGsEW0'*U `N:nCv~t+H{';M1qrv%fC_⁳َBNh*7F[`.9LrM_&Gqqŏ)Y*""dS҅ΐ!;e1k!1pq44gl°r T^YW*2j J|a(^^;^VC$H!TC}pZ3Ǣrj9LcӺ3'iyIoB[[ F]Pqh\1YpL6+ `%*TTRM Q[41۠C遤v]k$.12[1X=CA"g])6Mk;>DT7Cv`! ,|1  'JW_?msu'vw~nBeUr">ƕu~lq,eZm&^by0+:t3r(6zǂ02ҳS *x釻Yd'}}hfIRUƻ)\Ŷ&ռ-i@ vcÖV à2|GC3gv~kڙs"#2wW8 iӤmՙ?Srr;ޒf({Y7|H?q{"u ؊Ywbs[a-Y$A^ TXg5N#Å DKтYTy,0Ps`s}Kw*19ycZ›-^ prmBd_]tnDY{C3`tyiű:!r; }/1 )HVńkץ-2T@EzRl0d)iXHp5cyfKLx/g4TPU7v--tjd ]o#ܪu?dlv(Un&dnM//ܾ|nf!&'  ۵y XB=pdkhwRa# *cA/]`F`(#o`[M8w|vֻLIri_&W8T5rA뉑xe':+x07EPocSaOo:/*+1x Yv ̰K«ߛH=?Q/y(Hѧo{0n-A~1B)Iz=Vpx A-j*zuk!5p:r@^[8m򯳋_n9IC}XO2UoLbo⚓>n)nv )N<Ej*u&owO']w{;IՖzYO$uZ#MdQo# qb,S|s|Ct:$ te yJ?2my+SD' ZtKA1\^)G3m!} ]h,*!&W޳˷-ģЛ?+uo8qV?, p%O[E{#͞h}Wc=9wuN} s1 >w([xD'n)9ubOSVCgKi\o>cy\t@'htTd,@v>WbW4GMVHi.3*`QP4Q_ɾݍ<5-JCYw!vLCf`a88؛-kc8")7}߆`) k -Rhc5f=[WLu7-fBtdD)XRL+T|F'FuŶSkC%]~ß_m^2M=;a'ru5kA U9Z։5A)`7v=ҵ&8V t7u{u5Ԙ}=X&, wN801&F yWs)+Nk$] (bFT78kgw64 yhXq}cRlPf,S:B'ײ%/(pXGqLEPg(&/^P ˘ۼAcn,\/}5IX` [G* xy}c(F N& ?CB $sg2)#o k%Y)p-͵Qd4ɛhV7MXGM]nMn;jXe2\sѲJ&p^NJLQT1 ;tO⌢ (xJ3c^QZ}H|+= .$O-vA\)*IiIƸ26uw~>2"EBvd. *uXXz]bb6Yy3W/4_:&?6\1xT_g ܲolZ 泘%ِxׁhAF,$}qCckw._+"kG/΅8 Zz QG07'vaT$~VZ!J]8h-٫A;d]:kI3rqQBQIv; J}h5_4uv.SG/u{Hug"4t2W%\^\< Tlr((6o\S:Jc%[GvFLqDzyD~C1F'#lYNCtx5=5zJ47ti za@`6q%坍giލž~ZyM  i>0syg#Ʃs5M^7>vw,UH" ~vef'Ĭp\v(6oMH .# I1X?TCkIZiF:)Li쀸 Rr 'cuo9||{S*XUXE$J -DJӤȹqCYYo!X-#Sk;:/OH ϩB"oĤO<ɶZh*M:f.nW9qU3C:(PHUl&@z<_ Ay. K!pll[؀;8_42N՟S*n &x)cɼ`!J|Ba|'<5[MTcge֎t2~jBѓ_n?k$Dy~pQVJC w%Dc[nur-`urgE~RѠh:T^^ԐXGxV4UM7*ܮ)t}7PE"߈ WW.FA?)[N}m9+- eBˇ3HaݪZ¹5~nPTJr4Ǎ$85%Ya_E,[6 0zJ[L2ý#=FпDU*IFCV O9ޭe4yr/ ">QSPw|+l4܁5r$uyώ</oA f@;F8#Ȫv9PRV wH1.3Uay)Kty>mV.Na]Z gznYO˞vR D3>;eЄfځ“*dUF`Q1 u0\il=|!.WYT>Y.WYT>Y1E/{2bendy-0.6.1/src/assert_matches.rs000064400000000000000000000005071046102023000150750ustar 00000000000000macro_rules! assert_matches { ($expression:expr, $pattern:pat $( if $guard:expr )?) => { match $expression { $pattern $( if $guard )? => {} left => panic!( "assertion failed: (left ~= right)\n left: `{:?}`\n right: `{}`", left, stringify!($pattern $(if $guard)?) ), } } } bendy-0.6.1/src/decoding/decoder.rs000064400000000000000000000563201046102023000152550ustar 00000000000000use alloc::format; use core::str; use crate::{ decoding::{Error, Object}, state_tracker::{StateTracker, StructureError, Token}, }; /// A bencode decoder /// /// This can be used to either get a stream of tokens (using the [`Decoder::tokens()`] method) or to /// read a complete object at a time (using the [`Decoder::next_object()`]) method. #[derive(Debug)] pub struct Decoder<'a> { source: &'a [u8], offset: usize, state: StateTracker<&'a [u8], Error>, } impl<'ser> Decoder<'ser> { /// Create a new decoder from the given byte array pub fn new(buffer: &'ser [u8]) -> Self { Decoder { source: buffer, offset: 0, state: StateTracker::new(), } } /// Set the maximum nesting depth of the decoder. An unlimited-depth decoder may be /// created using `with_max_depth(::max_value())`, but be warned that this will likely /// exhaust memory if the nesting depth is too deep (even when reading raw tokens) pub fn with_max_depth(mut self, new_max_depth: usize) -> Self { self.state.set_max_depth(new_max_depth); self } fn take_byte(&mut self) -> Option { if self.offset < self.source.len() { let ret = Some(self.source[self.offset]); self.offset += 1; ret } else { None } } fn take_chunk(&mut self, count: usize) -> Option<&'ser [u8]> { match self.offset.checked_add(count) { Some(end_pos) if end_pos <= self.source.len() => { let ret = &self.source[self.offset..end_pos]; self.offset = end_pos; Some(ret) }, _ => None, } } fn take_int(&mut self, expected_terminator: char) -> Result<&'ser str, StructureError> { enum State { Start, Sign, Zero, Digits, } let mut curpos = self.offset; let mut state = State::Start; let mut success = false; while curpos < self.source.len() { let c = self.source[curpos] as char; state = match state { State::Start => match c { '-' => State::Sign, '0' => State::Zero, '0'..='9' => State::Digits, _ => return Err(StructureError::unexpected("'-' or '0'..'9'", c, curpos)), }, State::Zero => { if c == expected_terminator { success = true; break; } else { return Err(StructureError::unexpected( format!("{expected_terminator:?}"), c, curpos, )); } }, State::Sign => match c { '1'..='9' => State::Digits, _ => return Err(StructureError::unexpected("'1'..'9'", c, curpos)), }, State::Digits => match c { '0'..='9' => state, x if x == expected_terminator => { success = true; break; }, _ => { return Err(StructureError::unexpected( format!("{expected_terminator:?} or '0'..'9'"), c, curpos, )); }, }, }; curpos += 1; } if !success { return Err(StructureError::UnexpectedEof); } #[cfg(feature = "debug")] let ival = str::from_utf8(&self.source[self.offset..curpos]) .expect("We've already examined every byte in the string"); #[cfg(not(feature="debug"))] let ival = // Avoid a second UTF-8 check here unsafe { str::from_utf8_unchecked(&self.source[self.offset..curpos]) }; self.offset = curpos + 1; Ok(ival) } fn raw_next_token(&mut self) -> Result, Error> { let token = match self.take_byte().ok_or(StructureError::UnexpectedEof)? as char { 'e' => Token::End, 'l' => Token::List, 'd' => Token::Dict, 'i' => Token::Num(self.take_int('e')?), '0'..='9' => { self.offset -= 1; let curpos = self.offset; let ival = self.take_int(':')?; let len: usize = str::parse(ival).map_err(|_| StructureError::SyntaxError { unexpected: format!("Invalid integer at offset {curpos}"), })?; Token::String(self.take_chunk(len).ok_or(StructureError::UnexpectedEof)?) }, tok => { return Err(Error::from(StructureError::SyntaxError { unexpected: format!( "Invalid token starting with {:?} at offset {}", tok, self.offset - 1 ), })); }, }; Ok(token) } /// Read the next token. Returns Ok(Some(token)) if a token was successfully read, fn next_token(&mut self) -> Result>, Error> { self.state.check_error()?; if self.offset == self.source.len() { self.state.observe_eof()?; return Ok(None); } let tok_result = self.raw_next_token(); let tok = self.state.latch_err(tok_result)?; self.state.observe_token(&tok)?; Ok(Some(tok)) } /// Iterate over the tokens in the input stream. This guarantees that the resulting stream /// of tokens constitutes a valid bencoded structure. pub fn tokens(self) -> Tokens<'ser> { Tokens(self) } } /// Iterator over the tokens in the input stream. This guarantees that the resulting stream /// of tokens constitutes a valid bencoded structure. pub struct Tokens<'a>(Decoder<'a>); impl<'a> Iterator for Tokens<'a> { type Item = Result, Error>; fn next(&mut self) -> Option { // Only report an error once if self.0.state.check_error().is_err() { return None; } match self.0.next_token() { Ok(Some(token)) => Some(Ok(token)), Ok(None) => None, Err(err) => Some(Err(err)), } } } // High level interface impl<'ser> Decoder<'ser> { /// Read the next object from the encoded stream /// /// If the beginning of an object was successfully read, returns `Ok(Some(object))`. /// At the end of the input stream, this will return `Ok(None)`; otherwise, returns /// `Err(some_error)`. /// /// Note that complex objects (lists and dicts) are not fully validated before being /// returned from this method, so you may still get an error while decoding the contents /// of the object pub fn next_object<'obj>(&'obj mut self) -> Result>, Error> { use self::Token::*; Ok(match self.next_token()? { None | Some(End) => None, Some(List) => Some(Object::List(ListDecoder::new(self))), Some(Dict) => Some(Object::Dict(DictDecoder::new(self))), Some(String(s)) => Some(Object::Bytes(s)), Some(Num(s)) => Some(Object::Integer(s)), }) } } /// A dictionary read from the input stream #[derive(Debug)] pub struct DictDecoder<'obj, 'ser: 'obj> { decoder: &'obj mut Decoder<'ser>, finished: bool, start_point: usize, } /// A list read from the input stream #[derive(Debug)] pub struct ListDecoder<'obj, 'ser: 'obj> { decoder: &'obj mut Decoder<'ser>, finished: bool, start_point: usize, } impl<'obj, 'ser: 'obj> DictDecoder<'obj, 'ser> { fn new(decoder: &'obj mut Decoder<'ser>) -> Self { let offset = decoder.offset - 1; DictDecoder { decoder, finished: false, start_point: offset, } } /// Parse the next key/value pair from the dictionary. Returns `Ok(None)` /// at the end of the dictionary pub fn next_pair<'item>( &'item mut self, ) -> Result)>, Error> { if self.finished { return Ok(None); } // We convert to a token to release the mut ref to decoder let key = self.decoder.next_object()?.map(Object::into_token); if let Some(Token::String(k)) = key { // This unwrap should be safe because None would produce an error here let v = self.decoder.next_object()?.unwrap(); Ok(Some((k, v))) } else { // We can't have gotten anything but a string, as anything else would be // a state error self.finished = true; Ok(None) } } /// Consume (and validate the structure of) the rest of the items from the /// dictionary. This method should be used to check for encoding errors if /// [`DictDecoder::next_pair`] is not called until it returns `Ok(None)`. pub fn consume_all(&mut self) -> Result<(), Error> { while self.next_pair()?.is_some() { // just drop the items } Ok(()) } /// Get the raw bytes that made up this dictionary pub fn into_raw(mut self) -> Result<&'ser [u8], Error> { self.consume_all()?; Ok(&self.decoder.source[self.start_point..self.decoder.offset]) } } impl<'obj, 'ser: 'obj> Drop for DictDecoder<'obj, 'ser> { fn drop(&mut self) { // we don't care about errors in drop; they'll be reported again in the parent self.consume_all().ok(); } } impl<'obj, 'ser: 'obj> ListDecoder<'obj, 'ser> { fn new(decoder: &'obj mut Decoder<'ser>) -> Self { let offset = decoder.offset - 1; ListDecoder { decoder, finished: false, start_point: offset, } } /// Get the next item from the list. Returns `Ok(None)` at the end of the list pub fn next_object<'item>(&'item mut self) -> Result>, Error> { if self.finished { return Ok(None); } let item = self.decoder.next_object()?; if item.is_none() { self.finished = true; } Ok(item) } /// Consume (and validate the structure of) the rest of the items from the /// list. This method should be used to check for encoding errors if /// [`ListDecoder::next_object`] is not called until it returns [`Ok(())`]. /// /// [`Ok(())`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Ok pub fn consume_all(&mut self) -> Result<(), Error> { while self.next_object()?.is_some() { // just drop the items } Ok(()) } /// Get the raw bytes that made up this list pub fn into_raw(mut self) -> Result<&'ser [u8], Error> { self.consume_all()?; Ok(&self.decoder.source[self.start_point..self.decoder.offset]) } } impl<'obj, 'ser: 'obj> Drop for ListDecoder<'obj, 'ser> { fn drop(&mut self) { // we don't care about errors in drop; they'll be reported again in the parent self.consume_all().ok(); } } #[cfg(test)] mod test { #[cfg(not(feature = "std"))] use alloc::{vec, vec::Vec}; use core::iter; use regex; use super::*; static SIMPLE_MSG: &'static [u8] = b"d3:bari1e3:fooli2ei3eee"; fn decode_tokens(msg: &[u8]) -> Vec> { let tokens: Vec> = Decoder::new(msg).tokens().collect(); if tokens.iter().all(Result::is_ok) { tokens.into_iter().map(Result::unwrap).collect() } else { panic!( "Unexpected tokenization error. Received tokens: {:?}", tokens ); } } fn decode_err(msg: &[u8], err_regex: &str) { let mut tokens: Vec> = Decoder::new(msg).tokens().collect(); if tokens.iter().all(Result::is_ok) { panic!("Unexpected parse success: {:?}", tokens); } else { let err = format!("{}", tokens.pop().unwrap().err().unwrap()); let err_regex = regex::Regex::new(err_regex).expect("Test regexes should be valid"); if !err_regex.is_match(&err) { panic!("Unexpected error: {}", err); } } } #[test] fn simple_bdecode_tokenization() { use self::Token::*; let tokens: Vec<_> = decode_tokens(SIMPLE_MSG); assert_eq!( tokens, vec![ Dict, String(&b"bar"[..]), Num(&"1"[..]), String(&b"foo"[..]), List, Num(&"2"[..]), Num(&"3"[..]), End, End, ] ); } #[test] fn short_dict_should_fail() { decode_err(b"d", r"EOF"); } #[test] fn short_list_should_fail() { decode_err(b"l", r"EOF"); } #[test] fn short_int_should_fail() { decode_err(b"i12", r"EOF"); } #[test] fn negative_numbers_and_zero_should_parse() { use self::Token::*; let tokens: Vec<_> = decode_tokens(b"i0ei-1e"); assert_eq!(tokens, vec![Num(&"0"), Num(&"-1")],); } #[test] fn negative_zero_is_illegal() { decode_err(b"i-0e", "got '0'"); } #[test] fn leading_zeros_are_illegal() { decode_err(b"i00e", "got '0'"); decode_err(b"i-00e", "got '0'"); decode_err(b"i01e", "got '1'"); decode_err(b"i-01e", "got '0'"); decode_err(b"i001e", "got '0'"); decode_err(b"i-001e", "got '0'"); } #[test] fn map_keys_must_be_strings() { decode_err(b"d3:fooi1ei2ei3ee", r"Map keys must be strings"); } #[test] fn map_keys_must_ascend() { decode_err(b"d3:fooi1e3:bari1ee", r"Keys were not sorted"); } #[test] fn map_keys_must_be_unique() { decode_err(b"d3:fooi1e3:fooi1ee", r"Keys were not sorted"); } #[test] fn map_keys_must_have_values() { decode_err(b"d3:fooe", r"Missing map value"); } #[test] fn strings_must_have_bodies() { decode_err(b"3:", r"EOF"); } #[test] fn ints_must_have_bodies() { decode_err(b"ie", r"Expected.*got 'e'"); } #[test] fn recursion_should_be_limited() { let mut msg = Vec::new(); msg.extend(iter::repeat(b'l').take(4096)); msg.extend(iter::repeat(b'e').take(4096)); decode_err(&msg, r"nesting depth"); } #[test] fn recursion_bounds_should_be_tight() { let test_msg = b"lllleeee"; assert!( Decoder::new(test_msg) .with_max_depth(4) .tokens() .last() .unwrap() .is_ok() ); assert!( Decoder::new(test_msg) .with_max_depth(3) .tokens() .last() .unwrap() .is_err() ); } #[test] fn dict_drop_should_consume_struct() { let mut decoder = Decoder::new(b"d3:fooi1e3:quxi2eei1000e"); drop(decoder.next_object()); let token = decoder.tokens().next().unwrap().unwrap(); assert_eq!(token, Token::Num("1000")); } #[test] fn list_drop_should_consume_struct() { let mut decoder = Decoder::new(b"li1ei2ei3eei1000e"); drop(decoder.next_object()); let token = decoder.tokens().next().unwrap().unwrap(); assert_eq!(token, Token::Num("1000")); } #[test] fn bytes_or_should_work_on_bytes() { assert_eq!( Ok(&b"foo"[..]), Object::Bytes(b"foo").bytes_or(Err("failure")) ); } #[test] fn bytes_or_should_not_work_on_other_types() { assert_eq!( Err("failure"), Object::Integer("123").bytes_or(Err("failure")) ); let mut list_decoder = Decoder::new(b"le"); assert_eq!( Err("failure"), list_decoder .next_object() .unwrap() .unwrap() .bytes_or(Err("failure")) ); let mut dict_decoder = Decoder::new(b"de"); assert_eq!( Err("failure"), dict_decoder .next_object() .unwrap() .unwrap() .bytes_or(Err("failure")) ); } #[test] fn bytes_or_else_should_work_on_bytes() { assert_eq!( Ok(&b"foo"[..]), Object::Bytes(b"foo").bytes_or_else(|_| Err("failure")) ); } #[test] fn bytes_or_else_should_not_work_on_other_types() { assert_eq!( Err("failure"), Object::Integer("123").bytes_or_else(|_| Err("failure")) ); let mut list_decoder = Decoder::new(b"le"); assert_eq!( Err("failure"), list_decoder .next_object() .unwrap() .unwrap() .bytes_or_else(|_| Err("failure")) ); let mut dict_decoder = Decoder::new(b"de"); assert_eq!( Err("failure"), dict_decoder .next_object() .unwrap() .unwrap() .bytes_or_else(|_| Err("failure")) ); } #[test] fn integer_str_or_should_work_on_int() { assert_eq!( Ok(&"123"[..]), Object::Integer("123").integer_or(Err("failure")) ); } #[test] fn integer_str_or_should_not_work_on_other_types() { assert_eq!( Err("failure"), Object::Bytes(b"foo").integer_or(Err("failure")) ); let mut list_decoder = Decoder::new(b"le"); assert_eq!( Err("failure"), list_decoder .next_object() .unwrap() .unwrap() .integer_or(Err("failure")) ); let mut dict_decoder = Decoder::new(b"de"); assert_eq!( Err("failure"), dict_decoder .next_object() .unwrap() .unwrap() .integer_or(Err("failure")) ); } #[test] fn integer_str_or_else_should_work_on_int() { assert_eq!( Ok(&"123"[..]), Object::Integer("123").integer_or_else(|_| Err("failure")) ); } #[test] fn integer_str_or_else_should_not_work_on_other_types() { assert_eq!( Err("failure"), Object::Bytes(b"foo").integer_or_else(|_| Err("failure")) ); let mut list_decoder = Decoder::new(b"le"); assert_eq!( Err("failure"), list_decoder .next_object() .unwrap() .unwrap() .integer_or_else(|_| Err("failure")) ); let mut dict_decoder = Decoder::new(b"de"); assert_eq!( Err("failure"), dict_decoder .next_object() .unwrap() .unwrap() .integer_or_else(|_| Err("failure")) ); } #[test] fn list_or_should_work_on_list() { let mut list_decoder = Decoder::new(b"le"); assert!( list_decoder .next_object() .unwrap() .unwrap() .list_or(Err("failure")) .is_ok() ); } #[test] fn list_or_should_not_work_on_other_types() { assert_eq!( "failure", Object::Bytes(b"foo").list_or(Err("failure")).unwrap_err() ); assert_eq!( "failure", Object::Integer("foo").list_or(Err("failure")).unwrap_err() ); let mut dict_decoder = Decoder::new(b"de"); assert_eq!( "failure", dict_decoder .next_object() .unwrap() .unwrap() .list_or(Err("failure")) .unwrap_err() ); } #[test] fn list_or_else_should_work_on_list() { let mut list_decoder = Decoder::new(b"le"); assert!( list_decoder .next_object() .unwrap() .unwrap() .list_or_else(|_| Err("failure")) .is_ok() ); } #[test] fn list_or_else_should_not_work_on_other_types() { assert_eq!( "failure", Object::Bytes(b"foo") .list_or_else(|_| Err("failure")) .unwrap_err() ); assert_eq!( "failure", Object::Integer("foo") .list_or_else(|_| Err("failure")) .unwrap_err() ); let mut dict_decoder = Decoder::new(b"de"); assert_eq!( "failure", dict_decoder .next_object() .unwrap() .unwrap() .list_or_else(|_| Err("failure")) .unwrap_err() ); } #[test] fn dictionary_or_should_work_on_dict() { let mut dict_decoder = Decoder::new(b"de"); assert!( dict_decoder .next_object() .unwrap() .unwrap() .dictionary_or(Err("failure")) .is_ok() ); } #[test] fn dictionary_or_should_not_work_on_other_types() { assert_eq!( "failure", Object::Bytes(b"foo") .dictionary_or(Err("failure")) .unwrap_err() ); assert_eq!( "failure", Object::Integer("foo") .dictionary_or(Err("failure")) .unwrap_err() ); let mut list_decoder = Decoder::new(b"le"); assert_eq!( "failure", list_decoder .next_object() .unwrap() .unwrap() .dictionary_or(Err("failure")) .unwrap_err() ); } #[test] fn dictionary_or_else_should_work_on_dict() { let mut dict_decoder = Decoder::new(b"de"); assert!( dict_decoder .next_object() .unwrap() .unwrap() .dictionary_or_else(|_| Err("failure")) .is_ok() ); } #[test] fn dictionary_or_else_should_not_work_on_other_types() { assert_eq!( "failure", Object::Bytes(b"foo") .dictionary_or_else(|_| Err("failure")) .unwrap_err() ); assert_eq!( "failure", Object::Integer("foo") .dictionary_or_else(|_| Err("failure")) .unwrap_err() ); let mut list_decoder = Decoder::new(b"le"); assert_eq!( "failure", list_decoder .next_object() .unwrap() .unwrap() .dictionary_or_else(|_| Err("failure")) .unwrap_err() ); } } bendy-0.6.1/src/decoding/error.rs000064400000000000000000000125501046102023000147760ustar 00000000000000use alloc::{ format, str::Utf8Error, string::{FromUtf8Error, String, ToString}, }; use core::{fmt::Display, num::ParseIntError}; #[cfg(feature = "std")] use std::sync::Arc; use thiserror::Error; use crate::state_tracker; #[derive(Debug, Clone)] pub struct Error { context: Option, kind: ErrorKind, } impl core::fmt::Display for Error { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{}", self.kind)?; if let Some(c) = &self.context { write!(f, "\nin context:\n\t{}", c)?; } Ok(()) } } impl core::error::Error for Error { fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { self.kind.source() } } // An enumeration of potential errors that appear during bencode deserialization. #[derive(Debug, Clone, Error)] #[non_exhaustive] pub enum ErrorKind { /// Error that occurs if the serialized structure contains invalid semantics. #[cfg(feature = "std")] #[error("malformed content discovered: {source}")] MalformedContent { #[source] source: Arc, }, /// Error that occurs if the serialized structure contains invalid semantics. #[cfg(not(feature = "std"))] #[error("malformed content discovered")] MalformedContent, /// Error that occurs if the serialized structure is incomplete. #[error("missing field: {field}")] MissingField { field: String }, /// Error in the bencode structure (e.g. a missing field and seperator). #[error("bencode encoding corrupted ({source})")] StructureError { source: state_tracker::StructureError, }, /// Error that occurs if the serialized structure contains an unexpected field. #[error("unexpected field: {field}")] UnexpectedField { field: String }, /// Error through an unexpected bencode token during deserialization. #[error("discovered {discovered} but expected {expected}")] UnexpectedToken { expected: String, discovered: String, }, } pub trait ResultExt { fn context(self, context: impl Display) -> Self; } impl Error { pub fn context(mut self, context: impl Display) -> Self { if let Some(current) = self.context.as_mut() { *current = format!("{current}\n\t{context}"); } else { self.context = Some(context.to_string()); } self } /// Raised when there is a general error while deserializing a type. /// The message should not be capitalized and should not end with a period. #[cfg(feature = "std")] pub fn malformed_content(source: SourceT) -> Self where SourceT: std::error::Error + Send + Sync + 'static, { let error = Arc::new(source); ErrorKind::MalformedContent { source: error }.into() } /// Raised when there is a general error while deserializing a type. /// The message should not be capitalized and should not end with a period. #[cfg(not(feature = "std"))] pub fn malformed_content(_cause: T) -> Self { Self::from(ErrorKind::MalformedContent) } /// Returns a `Error::MissingField` which contains the name of the field. pub fn missing_field(field_name: impl Display) -> Self { Error::from(ErrorKind::MissingField { field: field_name.to_string(), }) } /// Returns a `Error::UnexpectedField` which contains the name of the field. pub fn unexpected_field(field_name: impl Display) -> Self { Error::from(ErrorKind::UnexpectedField { field: field_name.to_string(), }) } /// Returns a `Error::UnexpectedToken` which contains a custom error message. pub fn unexpected_token(expected: impl Display, discovered: impl Display) -> Self { Error::from(ErrorKind::UnexpectedToken { expected: expected.to_string(), discovered: discovered.to_string(), }) } } impl From for Error { fn from(kind: ErrorKind) -> Self { Self { context: None, kind, } } } impl From for Error { fn from(error: state_tracker::StructureError) -> Self { Self::from(ErrorKind::StructureError { source: error }) } } impl From for Error { fn from(err: FromUtf8Error) -> Self { Self::malformed_content(err) } } impl From for Error { fn from(err: Utf8Error) -> Self { Self::malformed_content(err) } } impl From for Error { fn from(err: ParseIntError) -> Self { Self::malformed_content(err) } } impl ResultExt for Result { fn context(self, context: impl Display) -> Self { self.map_err(|err| err.context(context)) } } #[test] fn decoding_errors_are_sync_send() { use crate::decoding::error::{Error, ErrorKind}; fn is_send() {} fn is_sync() {} is_send::(); is_send::(); is_sync::(); is_sync::(); } #[test] fn error_context_is_displayed() { let e:Result<(), Error> = Err(Error::missing_field("aaa")).context("bbb").context("ccc"); let e = e.unwrap_err().to_string(); println!("{}", &e); assert!(e.contains("aaa\n")); assert!(e.contains("in context:\n")); assert!(e.contains("bbb\n")); assert!(e.contains("ccc")); } bendy-0.6.1/src/decoding/from_bencode.rs000064400000000000000000000135541046102023000162740ustar 00000000000000#[cfg(not(feature = "std"))] use alloc::{collections::BTreeMap, rc::Rc, string::String, vec::Vec}; #[cfg(feature = "std")] use std::{ collections::{BTreeMap, HashMap}, hash::{BuildHasher, Hash}, rc::Rc, }; use crate::{ decoding::{Decoder, Error, Object}, encoding::AsString, state_tracker::StructureError, }; /// Basic trait for bencode based value deserialization. pub trait FromBencode { /// Maximum allowed depth of nested structures before the decoding should be aborted. const EXPECTED_RECURSION_DEPTH: usize = 2048; /// Deserialize an object from its byte representation. fn from_bencode(bytes: &[u8]) -> Result where Self: Sized, { let mut decoder = Decoder::new(bytes).with_max_depth(Self::EXPECTED_RECURSION_DEPTH); let object = decoder.next_object()?; object.map_or( Err(Error::from(StructureError::UnexpectedEof)), Self::decode_bencode_object, ) } /// Deserialize an object from its intermediate bencode representation. fn decode_bencode_object(object: Object) -> Result where Self: Sized; } macro_rules! impl_from_bencode_for_integer { ($($type:ty)*) => {$( impl FromBencode for $type { const EXPECTED_RECURSION_DEPTH: usize = 0; fn decode_bencode_object(object: Object) -> Result where Self: Sized, { let content = object.try_into_integer()?; let number = content.parse::<$type>()?; Ok(number) } } )*} } impl_from_bencode_for_integer!(u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize); impl FromBencode for Vec { const EXPECTED_RECURSION_DEPTH: usize = ContentT::EXPECTED_RECURSION_DEPTH + 1; fn decode_bencode_object(object: Object) -> Result where Self: Sized, { let mut list = object.try_into_list()?; let mut results = Vec::new(); while let Some(object) = list.next_object()? { let item = ContentT::decode_bencode_object(object)?; results.push(item); } Ok(results) } } impl FromBencode for String { const EXPECTED_RECURSION_DEPTH: usize = 0; fn decode_bencode_object(object: Object) -> Result where Self: Sized, { let content = object.try_into_bytes()?; let content = String::from_utf8(content.to_vec())?; Ok(content) } } impl FromBencode for BTreeMap where K: FromBencode + Ord, V: FromBencode, { const EXPECTED_RECURSION_DEPTH: usize = V::EXPECTED_RECURSION_DEPTH + 1; fn decode_bencode_object(object: Object) -> Result where Self: Sized, { let mut dict = object.try_into_dictionary()?; let mut result = BTreeMap::default(); while let Some((key, value)) = dict.next_pair()? { let key = K::decode_bencode_object(Object::Bytes(key))?; let value = V::decode_bencode_object(value)?; result.insert(key, value); } Ok(result) } } #[cfg(feature = "std")] impl FromBencode for HashMap where K: FromBencode + Hash + Eq, V: FromBencode, H: BuildHasher + Default, { const EXPECTED_RECURSION_DEPTH: usize = V::EXPECTED_RECURSION_DEPTH + 1; fn decode_bencode_object(object: Object) -> Result where Self: Sized, { let mut dict = object.try_into_dictionary()?; let mut result = HashMap::default(); while let Some((key, value)) = dict.next_pair()? { let key = K::decode_bencode_object(Object::Bytes(key))?; let value = V::decode_bencode_object(value)?; result.insert(key, value); } Ok(result) } } impl FromBencode for Rc { const EXPECTED_RECURSION_DEPTH: usize = T::EXPECTED_RECURSION_DEPTH; fn decode_bencode_object(object: Object) -> Result where Self: Sized, { T::decode_bencode_object(object).map(Rc::new) } } impl FromBencode for AsString> { const EXPECTED_RECURSION_DEPTH: usize = 0; fn decode_bencode_object(object: Object) -> Result where Self: Sized, { object.try_into_bytes().map(Vec::from).map(AsString) } } #[cfg(test)] mod test { #[cfg(not(feature = "std"))] use alloc::{format, vec::Vec}; use crate::encoding::AsString; use super::*; #[test] fn from_bencode_to_string_should_work_with_valid_input() { let expected_message = "hello"; let serialized_message = format!("{}:{}", expected_message.len(), expected_message).into_bytes(); let decoded_message = String::from_bencode(&serialized_message).unwrap(); assert_eq!(expected_message, decoded_message); } #[test] fn from_bencode_to_as_string_should_work_with_valid_input() { let expected_message = "hello"; let serialized_message = format!("{}:{}", expected_message.len(), expected_message).into_bytes(); let decoded_vector = AsString::from_bencode(&serialized_message).unwrap(); assert_eq!(expected_message.as_bytes(), &decoded_vector.0[..]); } #[test] #[should_panic(expected = "Num")] fn from_bencode_to_as_string_should_fail_for_integer() { AsString::>::from_bencode(&b"i1e"[..]).unwrap(); } #[test] #[should_panic(expected = "NestingTooDeep")] fn from_bencode_to_as_string_should_fail_for_list() { AsString::>::from_bencode(&b"l1:ae"[..]).unwrap(); } #[test] #[should_panic(expected = "NestingTooDeep")] fn from_bencode_to_as_string_should_fail_for_dictionary() { AsString::>::from_bencode(&b"d1:a1:ae"[..]).unwrap(); } } bendy-0.6.1/src/decoding/mod.rs000064400000000000000000000046151046102023000144270ustar 00000000000000//! Decodes a bencoded struct //! //! # Basic decoding //! For any decoding process, first we need to create a decoder: //! //! ``` //! # use bendy::decoding::{Decoder}; //! # //! # let buf: &[u8] = b"d3:fooi1ee"; //! let _decoder = Decoder::new(buf); //! ``` //! //! Decoders have a depth limit to prevent resource exhaustion from hostile inputs. By default, it's //! set high enough for most structures that you'd encounter when prototyping, but for production //! use, not only may it not be enough, but the higher the depth limit, the more stack space an //! attacker can cause your program to use, so we recommend setting the bounds tightly: //! //! ``` //! # use bendy::decoding::{Decoder}; //! # //! # let buf: &[u8] = b"d3:fooi1ee"; //! let _decoder = Decoder::new(buf).with_max_depth(3); //! ``` //! //! Atoms (integers and strings) have depth zero, and lists and dicts have a depth equal to the //! depth of their deepest member plus one. As an special case, an empty list or dict has depth 1. //! //! Now, you can start reading objects: //! //! ``` //! # use bendy::decoding::{Decoder,Object}; //! # //! # fn decode_list(_: bendy::decoding::ListDecoder) {} //! # fn decode_dict(_: bendy::decoding::DictDecoder) {} //! # //! # let buf: &[u8] = b"d3:fooi1ee"; //! # let mut decoder = Decoder::new(buf); //! # //! match decoder.next_object().unwrap() { //! None => (), // EOF //! Some(Object::List(d)) => decode_list(d), //! Some(Object::Dict(d)) => decode_dict(d), //! Some(Object::Integer(_)) => (), // integer, as a string //! Some(Object::Bytes(_)) => (), // A raw bytestring //! }; //! ``` //! //! # Error handling //! //! Once an error is encountered, the decoder won't try to muddle through it; instead, every future //! call to the decoder will return the same error. This behaviour can be used to check the syntax //! of an input object without fully decoding it: //! //! ``` //! # use bendy::decoding::Decoder; //! # //! fn syntax_check(buf: &[u8]) -> bool { //! let mut decoder = Decoder::new(buf); //! decoder.next_object().ok(); // ignore the return value of this //! return decoder.next_object().is_ok(); //! } //! # //! # assert!(syntax_check(b"i18e")); //! ``` mod decoder; mod error; mod from_bencode; mod object; pub use self::{ decoder::{Decoder, DictDecoder, ListDecoder, Tokens}, error::{Error, ErrorKind, ResultExt}, from_bencode::FromBencode, object::Object, }; bendy-0.6.1/src/decoding/object.rs000064400000000000000000000331021046102023000151070ustar 00000000000000use crate::{ decoding::{DictDecoder, Error, ListDecoder}, state_tracker::Token, }; /// An object read from a decoder pub enum Object<'obj, 'ser: 'obj> { /// A list of arbitrary objects List(ListDecoder<'obj, 'ser>), /// A map of string-valued keys to arbitrary objects Dict(DictDecoder<'obj, 'ser>), /// An unparsed integer Integer(&'ser str), /// A byte string Bytes(&'ser [u8]), } impl<'obj, 'ser: 'obj> Object<'obj, 'ser> { pub fn into_token(self) -> Token<'ser> { match self { Object::List(_) => Token::List, Object::Dict(_) => Token::Dict, Object::Bytes(bytes) => Token::String(bytes), Object::Integer(num) => Token::Num(num), } } /// Try to treat the object as a byte string, mapping [`Object::Bytes(v)`] into /// [`Ok(v)`]. Any other variant returns the given default value. /// /// Default arguments passed into `bytes_or` are eagerly evaluated; if you /// are passing the result of a function call, it is recommended to use /// [`bytes_or_else`], which is lazily evaluated. /// /// [`Object::Bytes(v)`]: self::Object::Bytes /// [`Ok(v)`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Ok /// [`bytes_or_else`]: self::Object::bytes_or_else /// /// # Examples /// /// ``` /// use bendy::decoding::Object; /// /// let x = Object::Bytes(b"foo"); /// assert_eq!(Ok(&b"foo"[..]), x.bytes_or(Err("failure"))); /// /// let x = Object::Integer("foo"); /// assert_eq!(Err("failure"), x.bytes_or(Err("failure"))); /// ``` pub fn bytes_or( self, default: Result<&'ser [u8], ErrorT>, ) -> Result<&'ser [u8], ErrorT> { match self { Object::Bytes(content) => Ok(content), _ => default, } } /// Try to treat the object as a byte string, mapping [`Object::Bytes(v)`] into /// [`Ok(v)`]. Any other variant is passed into the given fallback method. /// /// [`Object::Bytes(v)`]: self::Object::Bytes /// [`Ok(v)`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Ok /// /// # Examples /// /// ``` /// use bendy::decoding::Object; /// /// let x = Object::Bytes(b"foo"); /// assert_eq!( /// Ok(&b"foo"[..]), /// x.bytes_or_else(|obj| Err(obj.into_token().name())) /// ); /// /// let x = Object::Integer("foo"); /// assert_eq!( /// Err("Num"), /// x.bytes_or_else(|obj| Err(obj.into_token().name())) /// ); /// ``` pub fn bytes_or_else( self, op: impl FnOnce(Self) -> Result<&'ser [u8], ErrorT>, ) -> Result<&'ser [u8], ErrorT> { match self { Object::Bytes(content) => Ok(content), _ => op(self), } } /// Try to treat the object as a byte string, mapping [`Object::Bytes(v)`] into /// [`Ok(v)`]. Any other variant results in an [`ErrorKind::UnexpectedToken`]. /// /// [`Object::Bytes(v)`]: self::Object::Bytes /// [`Ok(v)`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Ok /// [`ErrorKind::UnexpectedToken`]: crate::decoding::ErrorKind::UnexpectedToken /// /// # Examples /// /// ``` /// use bendy::decoding::Object; /// /// let x = Object::Bytes(b"foo"); /// assert_eq!(b"foo", x.try_into_bytes().unwrap()); /// /// let x = Object::Integer("foo"); /// assert!(x.try_into_bytes().is_err()); /// ``` pub fn try_into_bytes(self) -> Result<&'ser [u8], Error> { self.bytes_or_else(|obj| Err(Error::unexpected_token("String", obj.into_token().name()))) } /// Try to treat the object as an integer and return the internal string representation, /// mapping [`Object::Integer(v)`] into [`Ok(v)`]. Any other variant returns the given /// default value. /// /// Default arguments passed into `integer_or` are eagerly evaluated; if you are passing /// the result of a function call, it is recommended to use [`integer_or_else`], which /// is lazily evaluated. /// /// [`Object::Integer(v)`]: self::Object::Integer /// [`Ok(v)`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Ok /// [`integer_or_else`]: self::Object::integer_or_else /// /// # Examples /// /// ``` /// use bendy::decoding::Object; /// /// let x = Object::Integer("123"); /// assert_eq!(Ok(&"123"[..]), x.integer_or(Err("failure"))); /// /// let x = Object::Bytes(b"foo"); /// assert_eq!(Err("failure"), x.integer_or(Err("failure"))); /// ``` pub fn integer_or( self, default: Result<&'ser str, ErrorT>, ) -> Result<&'ser str, ErrorT> { match self { Object::Integer(content) => Ok(content), _ => default, } } /// Try to treat the object as an integer and return the internal string representation, /// mapping [`Object::Integer(v)`] into [`Ok(v)`]. Any other variant is passed into the /// given fallback method. /// /// [`Object::Integer(v)`]: self::Object::Integer /// [`Ok(v)`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Ok /// /// # Examples /// /// ``` /// use bendy::decoding::Object; /// /// let x = Object::Integer("123"); /// assert_eq!( /// Ok(&"123"[..]), /// x.integer_or_else(|obj| Err(obj.into_token().name())) /// ); /// /// let x = Object::Bytes(b"foo"); /// assert_eq!( /// Err("String"), /// x.integer_or_else(|obj| Err(obj.into_token().name())) /// ); /// ``` pub fn integer_or_else( self, op: impl FnOnce(Self) -> Result<&'ser str, ErrorT>, ) -> Result<&'ser str, ErrorT> { match self { Object::Integer(content) => Ok(content), _ => op(self), } } /// Try to treat the object as an integer and return the internal string representation, /// mapping [`Object::Integer(v)`] into [`Ok(v)`]. Any other variant results in an /// [`ErrorKind::UnexpectedToken`]. /// /// [`Object::Integer(v)`]: self::Object::Integer /// [`Ok(v)`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Ok /// [`ErrorKind::UnexpectedToken`]: crate::decoding::ErrorKind::UnexpectedToken /// /// # Examples /// /// ``` /// use bendy::decoding::Object; /// /// let x = Object::Integer("123"); /// assert_eq!("123", x.try_into_integer().unwrap()); /// /// let x = Object::Bytes(b"foo"); /// assert!(x.try_into_integer().is_err()); /// ``` pub fn try_into_integer(self) -> Result<&'ser str, Error> { self.integer_or_else(|obj| Err(Error::unexpected_token("Num", obj.into_token().name()))) } /// Try to treat the object as a list and return the internal list content decoder, /// mapping [`Object::List(v)`] into [`Ok(v)`]. Any other variant returns the given /// default value. /// /// Default arguments passed into `list_or` are eagerly evaluated; if you are passing /// the result of a function call, it is recommended to use [`list_or_else`], which is /// lazily evaluated. /// /// [`Object::List(v)`]: self::Object::List /// [`Ok(v)`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Ok /// [`list_or_else`]: self::Object::list_or_else /// /// # Examples /// /// ``` /// use bendy::decoding::{Decoder, Object}; /// /// let mut list_decoder = Decoder::new(b"le"); /// let x = list_decoder.next_object().unwrap().unwrap(); /// /// assert!(x.list_or(Err("failure")).is_ok()); /// /// let x = Object::Bytes(b"foo"); /// assert_eq!("failure", x.list_or(Err("failure")).unwrap_err()); /// ``` pub fn list_or( self, default: Result, ErrorT>, ) -> Result, ErrorT> { match self { Object::List(content) => Ok(content), _ => default, } } /// Try to treat the object as a list and return the internal list content decoder, /// mapping [`Object::List(v)`] into [`Ok(v)`]. Any other variant is passed into the /// given fallback method. /// /// [`Object::List(v)`]: self::Object::List /// [`Ok(v)`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Ok /// /// # Examples /// /// ``` /// use bendy::decoding::{Decoder, Object}; /// /// let mut list_decoder = Decoder::new(b"le"); /// let x = list_decoder.next_object().unwrap().unwrap(); /// /// assert!(x.list_or_else(|obj| Err(obj.into_token().name())).is_ok()); /// /// let x = Object::Bytes(b"foo"); /// assert_eq!( /// "String", /// x.list_or_else(|obj| Err(obj.into_token().name())) /// .unwrap_err() /// ); /// ``` pub fn list_or_else( self, op: impl FnOnce(Self) -> Result, ErrorT>, ) -> Result, ErrorT> { match self { Object::List(content) => Ok(content), _ => op(self), } } /// Try to treat the object as a list and return the internal list content decoder, /// mapping [`Object::List(v)`] into [`Ok(v)`]. Any other variant results in an /// [`ErrorKind::UnexpectedToken`]. /// /// [`Object::List(v)`]: self::Object::List /// [`Ok(v)`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Ok /// [`ErrorKind::UnexpectedToken`]: crate::decoding::ErrorKind::UnexpectedToken /// /// # Examples /// /// ``` /// use bendy::decoding::{Decoder, Object}; /// /// let mut list_decoder = Decoder::new(b"le"); /// let x = list_decoder.next_object().unwrap().unwrap(); /// /// assert!(x.try_into_list().is_ok()); /// /// let x = Object::Bytes(b"foo"); /// assert!(x.try_into_list().is_err()); /// ``` pub fn try_into_list(self) -> Result, Error> { self.list_or_else(|obj| Err(Error::unexpected_token("List", obj.into_token().name()))) } /// Try to treat the object as a dictionary and return the internal dictionary content /// decoder, mapping [`Object::Dict(v)`] into [`Ok(v)`]. Any other variant returns the /// given default value. /// /// Default arguments passed to `dictionary_or` are eagerly evaluated; if you are passing /// the result of a function call, it is recommended to use [`dictionary_or_else`], which /// is lazily evaluated. /// /// [`Object::Dict(v)`]: self::Object::Dict /// [`Ok(v)`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Ok /// [`dictionary_or_else`]: self::Object::dictionary_or_else /// /// # Examples /// /// ``` /// use bendy::decoding::{Decoder, Object}; /// /// let mut dict_decoder = Decoder::new(b"de"); /// let x = dict_decoder.next_object().unwrap().unwrap(); /// /// assert!(x.dictionary_or(Err("failure")).is_ok()); /// /// let x = Object::Bytes(b"foo"); /// assert_eq!("failure", x.dictionary_or(Err("failure")).unwrap_err()); /// ``` pub fn dictionary_or( self, default: Result, ErrorT>, ) -> Result, ErrorT> { match self { Object::Dict(content) => Ok(content), _ => default, } } /// Try to treat the object as a dictionary and return the internal dictionary content /// decoder, mapping [`Object::Dict(v)`] into [`Ok(v)`]. Any other variant is passed /// into the given fallback method. /// /// [`Object::Dict(v)`]: self::Object::Dict /// [`Ok(v)`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Ok /// /// # Examples /// /// ``` /// use bendy::decoding::{Decoder, Object}; /// /// let mut dict_decoder = Decoder::new(b"de"); /// let x = dict_decoder.next_object().unwrap().unwrap(); /// /// assert!( /// x.dictionary_or_else(|obj| Err(obj.into_token().name())) /// .is_ok() /// ); /// /// let x = Object::Bytes(b"foo"); /// assert_eq!( /// "String", /// x.dictionary_or_else(|obj| Err(obj.into_token().name())) /// .unwrap_err() /// ); /// ``` pub fn dictionary_or_else( self, op: impl FnOnce(Self) -> Result, ErrorT>, ) -> Result, ErrorT> { match self { Object::Dict(content) => Ok(content), _ => op(self), } } /// Try to treat the object as a dictionary and return the internal dictionary content /// decoder, mapping [`Object::Dict(v)`] into [`Ok(v)`]. Any other variant results in /// an [`ErrorKind::UnexpectedToken`]. /// /// [`Object::Dict(v)`]: self::Object::Dict /// [`Ok(v)`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Ok /// [`ErrorKind::UnexpectedToken`]: crate::decoding::ErrorKind::UnexpectedToken /// /// # Examples /// /// ``` /// use bendy::decoding::{Decoder, Object}; /// /// let mut dict_decoder = Decoder::new(b"de"); /// let x = dict_decoder.next_object().unwrap().unwrap(); /// /// assert!(x.try_into_dictionary().is_ok()); /// /// let x = Object::Bytes(b"foo"); /// assert!(x.try_into_dictionary().is_err()); /// ``` pub fn try_into_dictionary(self) -> Result, Error> { self.dictionary_or_else(|obj| Err(Error::unexpected_token("Dict", obj.into_token().name()))) } } bendy-0.6.1/src/encoding/encoder.rs000064400000000000000000000347731046102023000153110ustar 00000000000000#[cfg(not(feature = "std"))] use alloc::{ borrow::ToOwned, collections::BTreeMap, format, string::{String, ToString}, vec::Vec, }; #[cfg(feature = "std")] use std::{collections::BTreeMap, vec::Vec}; use crate::{ encoding::{Error, PrintableInteger, ToBencode}, state_tracker::{StateTracker, StructureError, Token}, }; /// The actual encoder. Unlike the decoder, this is not zero-copy, as that would /// result in a horrible interface #[derive(Default, Debug)] pub struct Encoder { state: StateTracker, Error>, output: Vec, } impl Encoder { /// Create a new encoder pub fn new() -> Self { ::default() } /// Set the max depth of the encoded object #[must_use] pub fn with_max_depth(mut self, max_depth: usize) -> Self { self.state.set_max_depth(max_depth); self } /// Emit a single token to the encoder pub(crate) fn emit_token(&mut self, token: Token) -> Result<(), Error> { self.state.check_error()?; self.state.observe_token(&token)?; match token { Token::List => self.output.push(b'l'), Token::Dict => self.output.push(b'd'), Token::String(s) => { // Writing to a vec can't fail let length = s.len().to_string(); self.output.extend_from_slice(length.as_bytes()); self.output.push(b':'); self.output.extend_from_slice(s); }, Token::Num(num) => { // Alas, this doesn't verify that the given number is valid self.output.push(b'i'); self.output.extend_from_slice(num.as_bytes()); self.output.push(b'e'); }, Token::End => self.output.push(b'e'), } Ok(()) } /// Emit an arbitrary encodable object pub fn emit(&mut self, value: E) -> Result<(), Error> { self.emit_with(|e| value.encode(e)) } /// Emit a single object using an encoder pub fn emit_with(&mut self, value_cb: F) -> Result<(), Error> where F: FnOnce(SingleItemEncoder) -> Result<(), Error>, { let mut value_written = false; let ret = value_cb(SingleItemEncoder { encoder: self, value_written: &mut value_written, }); self.state.latch_err(ret)?; if !value_written { return self .state .latch_err(Err(Error::from(StructureError::invalid_state( "No value was emitted", )))); } Ok(()) } /// Emit an integer pub fn emit_int(&mut self, value: T) -> Result<(), Error> { // This doesn't use emit_token, as that would require that I write the integer to a // temporary buffer and then copy it to the output; writing it directly saves at // least one memory allocation self.state.check_error()?; // We observe an int here, as we need something that isn't a string (and therefore // possibly valid as a key) but we also want to require as few state transitions as // possible (for performance) self.state.observe_token(&Token::Num(""))?; self.output.push(b'i'); self.output.extend_from_slice(value.to_string().as_bytes()); self.output.push(b'e'); Ok(()) } /// Emit a string pub fn emit_str(&mut self, value: &str) -> Result<(), Error> { self.emit_token(Token::String(value.as_bytes())) } /// Emit a byte array pub fn emit_bytes(&mut self, value: &[u8]) -> Result<(), Error> { self.emit_token(Token::String(value)) } /// Emit a dictionary where you know that the keys are already /// sorted. The callback must emit key/value pairs to the given /// encoder in sorted order. If the key/value pairs may not be /// sorted, [`emit_unsorted_dict`] should be used instead. /// /// [`emit_unsorted_dict`]: SingleItemEncoder::emit_unsorted_dict /// /// Example: /// /// ``` /// # use bendy::encoding::{Encoder, Error}; /// # /// # fn main() -> Result<(), Error>{ /// let mut encoder = Encoder::new(); /// encoder.emit_dict(|mut e| { /// e.emit_pair(b"a", "foo")?; /// e.emit_pair(b"b", 2) /// }) /// # } /// ``` pub fn emit_dict(&mut self, content_cb: F) -> Result<(), Error> where F: FnOnce(SortedDictEncoder) -> Result<(), Error>, { self.emit_token(Token::Dict)?; content_cb(SortedDictEncoder { encoder: self })?; self.emit_token(Token::End) } /// Emit an arbitrary list. The callback should emit the contents /// of the list to the given encoder. /// /// E.g., to emit the list `[1,2,3]`, you would write /// /// ``` /// # use bendy::encoding::{Encoder, Error}; /// # fn main() -> Result<(), Error> { /// let mut encoder = Encoder::new(); /// encoder.emit_list(|e| { /// e.emit_int(1)?; /// e.emit_int(2)?; /// e.emit_int(3) /// }) /// # } /// ``` pub fn emit_list(&mut self, list_cb: F) -> Result<(), Error> where F: FnOnce(&mut Encoder) -> Result<(), Error>, { self.emit_token(Token::List)?; list_cb(self)?; self.emit_token(Token::End) } /// Emit a dictionary that may have keys out of order. This will write the dict /// values to temporary memory, then sort them before adding them to the serialized /// stream /// /// Example. /// /// ``` /// # use bendy::encoding::{Encoder, Error}; /// # /// # fn main() -> Result<(), Error> { /// let mut encoder = Encoder::new(); /// encoder.emit_and_sort_dict(|e| { /// // Unlike in the example for Encoder::emit_dict(), these keys aren't sorted /// e.emit_pair(b"b", 2)?; /// e.emit_pair(b"a", "foo") /// }) /// # } /// ``` pub fn emit_and_sort_dict(&mut self, content_cb: F) -> Result<(), Error> where F: FnOnce(&mut UnsortedDictEncoder) -> Result<(), Error>, { let mut encoder = self.begin_unsorted_dict()?; content_cb(&mut encoder)?; self.end_unsorted_dict(encoder) } /// Return the encoded string, if all objects written are complete pub fn get_output(mut self) -> Result, Error> { self.state.observe_eof()?; Ok(self.output) } pub(crate) fn begin_unsorted_dict(&mut self) -> Result { // emit the dict token so that a pre-existing state error is reported early self.emit_token(Token::Dict)?; Ok(UnsortedDictEncoder::new(self.state.remaining_depth())) } pub(crate) fn end_unsorted_dict(&mut self, encoder: UnsortedDictEncoder) -> Result<(), Error> { let content = encoder.done()?; for (k, v) in content { self.emit_bytes(&k)?; // We know that the output is a single object by construction self.state.observe_token(&Token::Num(""))?; self.output.extend_from_slice(&v); } self.emit_token(Token::End)?; Ok(()) } } /// An encoder that can only encode a single item. See [`Encoder`] /// for usage examples; the only difference between these classes is /// that `SingleItemEncoder` can only be used once. pub struct SingleItemEncoder<'a> { encoder: &'a mut Encoder, /// Whether we attempted to write a value to the encoder. The value /// of the referent of this field is meaningless if the encode method /// failed. value_written: &'a mut bool, } impl<'a> SingleItemEncoder<'a> { /// Emit an arbitrary encodable object pub fn emit(self, value: &E) -> Result<(), Error> { value.encode(self) } /// Emit a single object using an encoder pub fn emit_with(self, value_cb: F) -> Result<(), Error> where F: FnOnce(SingleItemEncoder) -> Result<(), Error>, { value_cb(self) } /// Emit an integer pub fn emit_int(self, value: T) -> Result<(), Error> { *self.value_written = true; self.encoder.emit_int(value) } /// Emit a string pub fn emit_str(self, value: &str) -> Result<(), Error> { *self.value_written = true; self.encoder.emit_str(value) } /// Emit a byte array pub fn emit_bytes(self, value: &[u8]) -> Result<(), Error> { *self.value_written = true; self.encoder.emit_bytes(value) } /// Emit an arbitrary list pub fn emit_list(self, list_cb: F) -> Result<(), Error> where F: FnOnce(&mut Encoder) -> Result<(), Error>, { *self.value_written = true; self.encoder.emit_list(list_cb) } /// Emit a sorted dictionary. If the input dictionary is unsorted, this will return an error. pub fn emit_dict(self, content_cb: F) -> Result<(), Error> where F: FnOnce(SortedDictEncoder) -> Result<(), Error>, { *self.value_written = true; self.encoder.emit_dict(content_cb) } /// Emit a dictionary that may have keys out of order. This will write the dict /// values to temporary memory, then sort them before adding them to the serialized /// stream pub fn emit_unsorted_dict(self, content_cb: F) -> Result<(), Error> where F: FnOnce(&mut UnsortedDictEncoder) -> Result<(), Error>, { *self.value_written = true; self.encoder.emit_and_sort_dict(content_cb) } /// Emit an arbitrary list. /// /// Attention: If this method is used while canonical output is required /// the caller needs to ensure that the iterator has a defined order. pub fn emit_unchecked_list( self, iterable: impl Iterator, ) -> Result<(), Error> { self.emit_list(|e| { for item in iterable { e.emit(item)?; } Ok(()) }) } } /// Encodes a map with pre-sorted keys pub struct SortedDictEncoder<'a> { encoder: &'a mut Encoder, } impl<'a> SortedDictEncoder<'a> { /// Emit a key/value pair pub fn emit_pair(&mut self, key: &[u8], value: E) -> Result<(), Error> where E: ToBencode, { self.encoder.emit_token(Token::String(key))?; self.encoder.emit(value) } /// Equivalent to [`SortedDictEncoder::emit_pair()`], but forces the type of the value /// to be a callback pub fn emit_pair_with(&mut self, key: &[u8], value_cb: F) -> Result<(), Error> where F: FnOnce(SingleItemEncoder) -> Result<(), Error>, { self.encoder.emit_token(Token::String(key))?; self.encoder.emit_with(value_cb) } } /// Helper to write a dictionary that may have keys out of order. This will buffer the /// dict values in temporary memory, then sort them before adding them to the serialized /// stream pub struct UnsortedDictEncoder { content: BTreeMap, Vec>, error: Result<(), Error>, remaining_depth: usize, } impl UnsortedDictEncoder { pub(crate) fn new(remaining_depth: usize) -> Self { Self { content: BTreeMap::new(), error: Ok(()), remaining_depth, } } /// Emit a key/value pair pub fn emit_pair(&mut self, key: &[u8], value: E) -> Result<(), Error> where E: ToBencode, { self.emit_pair_with(key, |e| value.encode(e)) } /// Emit a key/value pair where the value is produced by a callback pub fn emit_pair_with(&mut self, key: &[u8], value_cb: F) -> Result<(), Error> where F: FnOnce(SingleItemEncoder) -> Result<(), Error>, { let mut value_written = false; let mut encoder = Encoder::new().with_max_depth(self.remaining_depth); let ret = value_cb(SingleItemEncoder { encoder: &mut encoder, value_written: &mut value_written, }); if ret.is_err() { self.error = ret.clone(); return ret; } if !value_written { self.error = Err(Error::from(StructureError::InvalidState { state: "No value was emitted".to_owned(), })); } else { self.error = encoder.state.observe_eof(); } if self.error.is_err() { return self.error.clone(); } let encoded_object = encoder .get_output() .expect("Any errors should have been caught by observe_eof"); self.save_pair(key, encoded_object) } #[cfg(feature = "serde")] pub(crate) fn remaining_depth(&self) -> usize { self.remaining_depth } pub(crate) fn save_pair( &mut self, unencoded_key: &[u8], encoded_value: Vec, ) -> Result<(), Error> { #[cfg(not(feature = "std"))] use alloc::collections::btree_map::Entry; #[cfg(feature = "std")] use std::collections::btree_map::Entry; if self.error.is_err() { return self.error.clone(); } let vacancy = match self.content.entry(unencoded_key.to_owned()) { Entry::Vacant(vacancy) => vacancy, Entry::Occupied(occupation) => { self.error = Err(Error::from(StructureError::InvalidState { state: format!( "Duplicate key {}", String::from_utf8_lossy(occupation.key()) ), })); return self.error.clone(); }, }; vacancy.insert(encoded_value); Ok(()) } pub(crate) fn done(self) -> Result, Vec>, Error> { self.error?; Ok(self.content) } } #[cfg(test)] mod test { use super::*; #[test] pub fn simple_encoding_works() { let mut encoder = Encoder::new(); encoder .emit_dict(|mut e| { e.emit_pair(b"bar", 25)?; e.emit_pair_with(b"foo", |e| { e.emit_list(|e| { e.emit_str("baz")?; e.emit_str("qux") }) }) }) .expect("Encoding shouldn't fail"); assert_eq!( &encoder .get_output() .expect("Complete object should have been written"), &b"d3:bari25e3:fool3:baz3:quxee" ); } #[test] fn emit_cb_must_emit() { let mut encoder = Encoder::new(); assert!(encoder.emit_with(|_| Ok(())).is_err()); } } bendy-0.6.1/src/encoding/error.rs000064400000000000000000000035511046102023000150110ustar 00000000000000#[cfg(feature = "std")] use std::sync::Arc; use thiserror::Error; use crate::state_tracker; /// An enumeration of potential errors that appear during bencode encoding. #[derive(Debug, Clone, Error)] #[non_exhaustive] pub enum Error { /// Error that occurs if the serialized structure contains invalid semantics. #[cfg(feature = "std")] #[error("malformed content discovered: {source}")] MalformedContent { source: Arc, }, /// Error that occurs if the serialized structure contains invalid semantics. #[cfg(not(feature = "std"))] #[error("malformed content discovered")] MalformedContent, /// Error in the bencode structure (e.g. a missing field end separator). #[error("bencode encoding corrupted")] StructureError { source: state_tracker::StructureError, }, } impl Error { /// Raised when there is a general error while deserializing a type. /// The message should not be capitalized and should not end with a period. /// /// Note that, when building with no_std, this method accepts any type as /// its argument. #[cfg(feature = "std")] pub fn malformed_content(source: SourceT) -> Self where SourceT: std::error::Error + Send + Sync + 'static, { let error = Arc::new(source); Error::MalformedContent { source: error } } #[cfg(not(feature = "std"))] pub fn malformed_content(_cause: T) -> Self { Error::MalformedContent } } impl From for Error { fn from(error: state_tracker::StructureError) -> Self { Error::StructureError { source: error } } } #[test] fn encoding_errors_are_sync_send() { use crate::encoding::error::Error; fn is_send() {} fn is_sync() {} is_send::(); is_sync::(); } bendy-0.6.1/src/encoding/mod.rs000064400000000000000000000102231046102023000144310ustar 00000000000000//! An encoder for bencode. Guarantees that the output string is valid bencode //! //! # Encoding a structure //! //! The easiest way to encode a structure is to implement [`ToBencode`] for it. For most structures, //! this should be very simple: //! //! ``` //! # use bendy::encoding::{ToBencode, SingleItemEncoder, Error}; //! //! struct Message { //! foo: i32, //! bar: String, //! } //! //! impl ToBencode for Message { //! // Atoms have depth zero. The struct wrapper adds one level to that //! const MAX_DEPTH: usize = 1; //! //! fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { //! encoder.emit_dict(|mut e| { //! // Use e to emit the values //! e.emit_pair(b"bar", &self.bar)?; //! e.emit_pair(b"foo", &self.foo) //! })?; //! Ok(()) //! } //! } //! # //! # fn main() -> Result<(), Error> { //! # let message = Message{ //! # foo: 1, //! # bar: "quux".to_string(), //! # }; //! # //! # message.to_bencode().map(|_| ()) //! # } //! ``` //! //! Then, messages can be serialized using [`ToBencode::to_bencode`]: //! //! ``` //! # use bendy::encoding::{ToBencode, SingleItemEncoder, Error}; //! # //! # struct Message { //! # foo: i32, //! # bar: String, //! # } //! # //! # impl ToBencode for Message { //! # // Atoms have depth zero. The struct wrapper adds one level to that //! # const MAX_DEPTH: usize = 1; //! # //! # fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { //! # encoder.emit_dict(|mut e| { //! # // Use e to emit the values. They must be in sorted order here. //! # // If sorting the dict first is annoying, you can also use //! # // encoder.emit_and_sort_dict //! # e.emit_pair(b"bar", &self.bar)?; //! # e.emit_pair(b"foo", &self.foo) //! # })?; //! # Ok(()) //! # } //! # } //! # //! # fn main() -> Result<(), Error> { //! let message = Message { //! foo: 1, //! bar: "quux".to_string(), //! }; //! //! message.to_bencode() //! # .map(|_| ()) //! # } //! ``` //! //! Most primitive types already implement [`ToBencode`]. //! //! # Nesting depth limits //! //! To allow this to be used on limited platforms, all implementations of [`ToBencode`] include a //! maximum nesting depth. Atoms (integers and byte strings) are considered to have depth 0. An //! object (a list or dict) containing only atoms has depth 1, and in general, an object has a depth //! equal to the depth of its deepest member plus one. //! //! ## Dynamic depth //! //! In some cases, an object doesn't have a statically known depth. For example, ASTs may be //! arbitrarily nested. Such objects should have `MAX_DEPTH` set to 0, and callers should construct //! the Encoder manually, adding an appropriate buffer for the depth: //! //! ``` //! # use bendy::encoding::{ToBencode, Encoder, Error}; //! # //! # type ObjectType = u32; //! # static OBJECT: u32 = 0; //! # //! # fn main() -> Result<(), Error> { //! let mut encoder = Encoder::new().with_max_depth(ObjectType::MAX_DEPTH + 10); //! //! encoder.emit(OBJECT)?; //! encoder.get_output() //! # .map_err(Error::from) //! # .map(|_| ()) // ignore a success return value //! # } //! ``` //! //! # Error handling //! //! Once an error occurs during encoding, all future calls to the same encoding stream will fail //! early with the same error. It is not defined whether any callback or implementation of //! [`ToBencode::encode`] is called before returning an error; such callbacks should //! respond to failure by bailing out as quickly as possible. //! //! Not all values in [`Error`] can be caused by an encoding operation. Specifically, you only need //! to worry about [`UnsortedKeys`] and [`NestingTooDeep`]. //! //! [`ToBencode::encode`]: self::ToBencode::encode //! [`UnsortedKeys`]: self::Error#UnsortedKeys //! [`NestingTooDeep`]: self::Error#NestingTooDeep mod encoder; mod error; mod printable_integer; mod to_bencode; pub use self::{ encoder::{Encoder, SingleItemEncoder, SortedDictEncoder, UnsortedDictEncoder}, error::Error, printable_integer::PrintableInteger, to_bencode::{AsString, ToBencode}, }; bendy-0.6.1/src/encoding/printable_integer.rs000064400000000000000000000005641046102023000173560ustar 00000000000000#[cfg(not(feature = "std"))] use core::fmt::Display; #[cfg(feature = "std")] use std::fmt::Display; /// A value that can be formatted as a decimal integer pub trait PrintableInteger: Display {} macro_rules! impl_integer { ($($type:ty)*) => {$( impl PrintableInteger for $type {} )*} } impl_integer!(u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize); bendy-0.6.1/src/encoding/to_bencode.rs000064400000000000000000000146451046102023000157670ustar 00000000000000#[cfg(not(feature = "std"))] use alloc::{ collections::{BTreeMap, LinkedList, VecDeque}, rc::Rc, string::String, sync::Arc, vec::Vec, }; #[cfg(feature = "std")] use std::{ collections::{BTreeMap, HashMap, LinkedList, VecDeque}, hash::{BuildHasher, Hash}, rc::Rc, sync::Arc, }; use crate::encoding::{Encoder, Error, SingleItemEncoder}; /// An object that can be encoded into a single bencode object pub trait ToBencode { /// The maximum depth that this object could encode to. Leaves do not consume a level, so an /// `i1e` has depth 0 and `li1ee` has depth 1. /// /// A depth of 0 may also indicate a statically unknown depth. See the /// [`encoding`][crate::encoding#dynamic-depth] module docs. const MAX_DEPTH: usize; /// Encode this object into the bencode stream fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error>; /// Encode this object to a byte string fn to_bencode(&self) -> Result, Error> { let mut encoder = Encoder::new().with_max_depth(Self::MAX_DEPTH); encoder.emit_with(|e| self.encode(e))?; let bytes = encoder.get_output()?; Ok(bytes) } } /// Wrapper to allow `Vec` encoding as bencode string element. #[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq, PartialOrd, Ord)] pub struct AsString(pub I); // Forwarding impls impl<'a, E: 'a + ToBencode + Sized> ToBencode for &'a E { const MAX_DEPTH: usize = E::MAX_DEPTH; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { E::encode(self, encoder) } } #[cfg(feature = "std")] impl ToBencode for Box { const MAX_DEPTH: usize = E::MAX_DEPTH; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { E::encode(self, encoder) } } impl ToBencode for Rc { const MAX_DEPTH: usize = E::MAX_DEPTH; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { E::encode(self, encoder) } } impl ToBencode for Arc { const MAX_DEPTH: usize = E::MAX_DEPTH; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { E::encode(self, encoder) } } // Base type impls impl ToBencode for &str { const MAX_DEPTH: usize = 0; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { encoder.emit_str(self) } } impl ToBencode for String { const MAX_DEPTH: usize = 0; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { encoder.emit_str(self) } } macro_rules! impl_encodable_integer { ($($type:ty)*) => {$( impl ToBencode for $type { const MAX_DEPTH: usize = 1; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { encoder.emit_int(*self).map_err(Error::from) } } )*} } impl_encodable_integer!(u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize); macro_rules! impl_encodable_iterable { ($($type:ident)*) => {$( impl ToBencode for $type where ContentT: ToBencode { const MAX_DEPTH: usize = ContentT::MAX_DEPTH + 1; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { encoder.emit_list(|e| { for item in self { e.emit(item)?; } Ok(()) })?; Ok(()) } } )*} } impl_encodable_iterable!(Vec VecDeque LinkedList); impl ToBencode for &[ContentT] where ContentT: ToBencode, { const MAX_DEPTH: usize = ContentT::MAX_DEPTH + 1; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { encoder.emit_list(|e| { for item in *self { e.emit(item)?; } Ok(()) })?; Ok(()) } } impl, V: ToBencode> ToBencode for BTreeMap { const MAX_DEPTH: usize = V::MAX_DEPTH + 1; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { encoder.emit_dict(|mut e| { for (k, v) in self { e.emit_pair(k.as_ref(), v)?; } Ok(()) })?; Ok(()) } } #[cfg(feature = "std")] impl ToBencode for HashMap where K: AsRef<[u8]> + Eq + Hash, V: ToBencode, S: BuildHasher, { const MAX_DEPTH: usize = V::MAX_DEPTH + 1; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { encoder.emit_dict(|mut e| { let mut pairs = self .iter() .map(|(k, v)| (k.as_ref(), v)) .collect::>(); pairs.sort_by_key(|&(k, _)| k); for (k, v) in pairs { e.emit_pair(k, v)?; } Ok(()) })?; Ok(()) } } impl ToBencode for AsString where I: AsRef<[u8]>, { const MAX_DEPTH: usize = 1; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { encoder.emit_bytes(self.0.as_ref())?; Ok(()) } } impl AsRef<[u8]> for AsString where I: AsRef<[u8]>, { fn as_ref(&self) -> &'_ [u8] { self.0.as_ref() } } impl<'a, I> From<&'a [u8]> for AsString where I: From<&'a [u8]>, { fn from(content: &'a [u8]) -> Self { AsString(I::from(content)) } } #[cfg(test)] mod test { #[cfg(not(feature = "std"))] use alloc::{borrow::ToOwned, vec}; use super::*; struct Foo { bar: u32, baz: Vec, qux: Vec, } impl ToBencode for Foo { const MAX_DEPTH: usize = 2; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), Error> { encoder.emit_dict(|mut e| { e.emit_pair(b"bar", &self.bar)?; e.emit_pair(b"baz", &self.baz)?; e.emit_pair(b"qux", AsString(&self.qux))?; Ok(()) })?; Ok(()) } } #[test] fn simple_encodable_works() { let mut encoder = Encoder::new(); encoder .emit(Foo { bar: 5, baz: vec!["foo".to_owned(), "bar".to_owned()], qux: b"qux".to_vec(), }) .unwrap(); assert_eq!( &encoder.get_output().unwrap()[..], &b"d3:bari5e3:bazl3:foo3:bare3:qux3:quxe"[..] ); } } bendy-0.6.1/src/inspect/display.rs000064400000000000000000000175021046102023000152050ustar 00000000000000//! Methods for displaying [`Inspectable`]s. //! //! Refer to the [documentation here][crate::inspect#displaying--outputting]. use core::fmt::{Display, Write}; use crate::inspect::*; impl<'a> Display for Inspectable<'a> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Inspectable::String(x) => Display::fmt(x, f), Inspectable::Dict(x) => Display::fmt(x, f), Inspectable::List(x) => Display::fmt(x, f), Inspectable::Int(x) => Display::fmt(x, f), Inspectable::Raw(x) => { for b in x.iter() { write!(f, "\\x{b:02X}")?; } Ok(()) }, } } } impl<'a> Display for InString<'a> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let bytes = self.content(); let all_printable = bytes.iter().all(|b| is_printable_byte(*b)); write!(f, "{}:", self.len())?; if all_printable { for &b in bytes.iter() { let c = char::from_u32(b as u32).expect("Already ensured all chars are printable"); f.write_char(c)?; } Ok(()) } else { for b in bytes.iter() { write!(f, "\\x{b:02X}")?; } Ok(()) } } } impl<'a> Display for InInt<'a> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.write_char('i')?; f.write_str(&self.bytes)?; f.write_char('e') } } impl<'a> Display for InDict<'a> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.write_char('d')?; for InDictEntry { key, value } in self.items.iter() { Display::fmt(key, f)?; Display::fmt(value, f)?; } f.write_char('e') } } impl<'a> Display for InList<'a> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.write_char('l')?; for inspectable in self.items.iter() { Display::fmt(inspectable, f)?; } f.write_char('e') } } impl<'a> Inspectable<'a> { /// Provides a [`String`] of pretty-printed bencode in a format /// suitable for copy-pasting as a Rust string literal. /// /// # Example /// /// ``` /// # use bendy::inspect::*; /// let i = inspect(b"li0ei1ee"); /// let lit = i.as_rust_string_literal(); /// println!("{lit}"); /// // Prints: /// // let pretty_bencode = b"\ /// // l\ /// // i0e\ /// // i1e\ /// // e\ /// // " /// ``` #[must_use] pub fn as_rust_string_literal(&self) -> String { self.internal_pretty_print(true) } /// Provides a [`String`] of pretty-printed bencode. /// /// # Example /// /// ``` /// # use bendy::inspect::*; /// let i = inspect(b"li0ei1ee"); /// let lit = i.as_pretty_printed(); /// println!("{lit}"); /// // Prints: /// // l /// // i0e /// // i1e /// // e /// ``` #[must_use] pub fn as_pretty_printed(&self) -> String { self.internal_pretty_print(false) } fn internal_pretty_print(&self, as_literal: bool) -> String { fn dispatch( inspectable: &Inspectable, indent: &mut usize, as_literal: bool, out: &mut String, ) { let newline = |indent: &usize, out: &mut String| { if as_literal { out.push('\\'); } out.push('\n'); for _ in 0..*indent { out.push('\t'); } }; match inspectable { x @ Inspectable::Raw(_) | x @ Inspectable::String(_) | x @ Inspectable::Int(_) => { out.push_str(x.to_string().as_str()); }, Inspectable::List(InList { items }) => { out.push('l'); *indent += 1; for i in items.iter() { newline(indent, out); dispatch(i, indent, as_literal, out); } *indent -= 1; newline(indent, out); out.push('e'); }, Inspectable::Dict(InDict { items }) => { out.push('d'); *indent += 1; for i in items.iter() { let InDictEntry { key, value } = i; newline(indent, out); out.push_str(key.to_string().as_str()); newline(indent, out); dispatch(value, indent, as_literal, out); } *indent -= 1; newline(indent, out); out.push('e'); }, } } let mut output = String::new(); let mut indent = 0; if as_literal { output.push_str("let pretty_bencode = b\"\\\n"); } dispatch(self, &mut indent, as_literal, &mut output); if as_literal { output.push_str("\\\n\""); } output } } #[allow(clippy::manual_range_contains)] fn is_printable_byte(b: u8) -> bool { 40 <= b && b < 127 } #[cfg(test)] mod tests { use super::*; #[test] fn valid_printing_works() { fn ensure_prints_as(b: impl AsRef<[u8]>, expected: Option<&str>) { let b = b.as_ref(); let i = inspect(b).to_string(); if let Some(e) = expected { assert_eq!(i.as_str(), e); } else { let e = String::from_utf8(b.to_vec()).unwrap(); assert_eq!(i, e); } } fn ensure_roundtrip(b: impl AsRef<[u8]>) { ensure_prints_as(b, None); } ensure_roundtrip(b"i200e"); ensure_roundtrip(b"4:helo"); ensure_roundtrip(b"de"); ensure_roundtrip(b"le"); ensure_roundtrip(b"li2e6:inliste"); ensure_roundtrip(b"d1:ai1e1:bi2ee"); ensure_prints_as( b"d5:countli1ei2ei3ee4:null1:\x00e", Some("d5:countli1ei2ei3ee4:null1:\\x00e"), ); } #[ignore] #[test] fn as_rust_string_literal_test() { fn print(b: impl AsRef<[u8]>) { let b = b.as_ref(); let i = inspect(b).as_rust_string_literal(); println!("-------\n{}", i); } print(b"i200e"); print(b"4:helo"); print(b"de"); print(b"le"); print(b"li2e6:inliste"); print(b"d1:ai1e1:bi2ee"); print(b"d5:countli1ei2ei3ee4:null1:\x00e"); let ap = all_printables_bencode_list(); print(&ap); panic!() } #[ignore] #[test] fn debug_print_test() { let i = inspect(b"i200e"); dbg!(i); let i = inspect(b"4:helo"); dbg!(i); let i = inspect(b"de"); dbg!(i); let i = inspect(b"le"); dbg!(i); let i = inspect(b"li2e4:liste"); dbg!(i); let i = inspect(b"d1:ai1e1:bi2ee"); dbg!(i); panic!() } #[test] fn as_rust_string_literal_doesnt_break_quoting() { // We must not considuer double quote symbols printable, since // we don't escape them or turn them into \x22 in as_rust_string_literal assert!(!is_printable_byte(b'"')); } fn all_printables_bencode_list() -> Vec { let mut ap = Vec::new(); ap.push(b'l'); for b in 40_u8..127 { // Just checking that the printable byte ranges are synced assert!(is_printable_byte(b)); ap.push(b'1'); ap.push(b':'); ap.push(b); } ap.push(b'e'); ap } } bendy-0.6.1/src/inspect/mod.rs000064400000000000000000000361261046102023000143220ustar 00000000000000//! Reflection for testing //! //! Provides facilities for creating an AST-like object //! tree from any valid bencode: [`Inspectable`]. Decoding bencode into //! an [`Inspectable`] is done using recursion, so stack size limits //! apply. //! //! Use for reflection, modifying, testing, and pretty printing. //! //! Not recommended for use in production code. //! //! # Overview //! //! See the Reflection chapter of the [README.md][r]. //! //! # Creating //! //! The [`Inspectable`] docs explain how to create an `Inspectable` to get started. //! //! # Displaying / Outputting //! //! * Use [`.emit`][Inspectable::emit] to get the proper serialized bencode representation as a //! byte vector. //! * Derives [`Debug`][core::fmt::Debug]. //! * Provides [`.as_rust_string_literal`][Inspectable::as_rust_string_literal] for pretty printing. //! Useful when inspecting structures, hard coding test cases, etc. //! * Provided [`Display`][core::fmt::Display] implementation is similar to `.as_rust_string_literal`, //! but does not insert newlines, tabs, etc. Useful for debug logs. //! //! # Coercion //! //! Mutable coercion of [`Inspectable`] to variant items: [`.string`][Inspectable::string], //! [`.int`][`Inspectable::int`], [`.dict`][`Inspectable::dict`], [`.list`][`Inspectable::list`], //! and [`.raw`][`Inspectable::raw`]. //! //! Immutable coercion: [`.string_ref`][`Inspectable::string_ref`], [`.int_ref`][`Inspectable::int_ref`], //! [`.dict_ref`][`Inspectable::dict_ref`], [`.list_ref`][`Inspectable::list_ref`], //! and [`.raw_ref`][`Inspectable::raw_ref`]. //! //! These coercion method names are reversed compared to the standard `_mut` idiom in Rust, because you //! almost always want to mutate these objects, not just look at them. These methods will panic if //! called on an [`Inspectable`] with the wrong enum variant. //! //! # Traversing //! //! Use [`inspect_path()`][ip] to create a [`PathBuilder`][pb], for use with //! the [`.find`][Inspectable::find], [`.find_ref`][Inspectable::find_ref], //! [`.clone_and_mutate`][Inspectable::clone_and_mutate], and [`.mutate`][Inspectable::mutate] //! methods. These will drill down into the AST according to the //! [`Step`][crate::inspect::traverse::Step]s in the [`Path`][pb]. //! //! Searches in path traversal are implemented using recursion, so stack limits may //! apply. One can also use a combination of coercion and the following methods to //! traverse the AST without using Paths: //! //! * [`InList`]: [`.nth`][InList::nth], [`.nth_ref`][InList::nth_ref] //! * [`InDict`]: [`.nth`][InDict::nth], [`.nth_ref`][InDict::nth_ref], //! and [`.entry`][InDict::entry], [`.entry_ref`][InDict::entry_ref]. //! * [`InDictEntry`]: [`.key`][InDictEntry::key] and [`.value`][InDictEntry::value]. //! //! The traversal methods will all panic on failure. //! //! # Mutating //! //! There are many ways to mutate an [`Inspectable`] AST. Some of them require first coercing the //! [`Inspectable`] to its appropriate subtype. These are all best applied using these functions: //! [`.clone_and_mutate`][Inspectable::clone_and_mutate] and [`.mutate`][Inspectable::mutate]. //! For convenience, there is also [`.apply`][Inspectable::apply]. //! //! Here are all the mutation methods, grouped by the type they belong to. Methods on [`Inspectable`] //! may only be used on the type variants specified in parentheses. They will panic if used on a type //! variant they do not support. All of these except `.replace` are also available on their respective //! type variants. //! //! * [`Inspectable`] //! * [`.clear_content`][Inspectable::clear_content] (all) //! * [`.remove_entry`][`Inspectable::remove_entry`] (dicts only) //! * [`.remove_nth`][`Inspectable::remove_nth`] (dicts and lists) //! * [`.replace`][Inspectable::replace] (all) //! * [`.set_content_byterange`][Inspectable::set_content_byterange] (bytestrings only) //! * [`.truncate`][Inspectable::truncate] (bytestrings only) //! * [`InDict`] //! * [`.sort`][InDict::sort] //! * [`InInt`] //! * [`.set`][InInt::set] //! * [`InString`] (bytestrings) //! * [`.clear_fake_length`][InString::clear_fake_length] //! * [`.set_fake_length`][InString::set_fake_length] //! * [`.set_content_str`][InString::set_content_str] //! * [`.set_content_string`][InString::set_content_string] //! * [`.set_content_u8`][InString::set_content_u8] //! * [`.set_content_vec`][InString::set_content_vec] //! //! All properties of [`Inspectable`] and its type variants are public, so you may also make your own mutators. //! //! [r]: https://github.com/P3KI/bendy/blob/master/README.md#reflection //! [pb]: crate::inspect::traverse::PathBuilder //! [ip]: crate::inspect::inspect_path #[cfg(not(feature = "std"))] use alloc::{ borrow::Cow, string::{String, ToString}, vec::Vec, }; #[cfg(feature = "std")] use std::{borrow::Cow, vec::Vec}; pub mod display; pub mod mutate; #[doc(hidden)] pub mod parse; pub mod traverse; /// Attempt to decode a u8 buffer into an inspectable. /// Panics on error. Use [`Inspectable::try_from`] for a /// fallible alternative. pub fn inspect<'ser>(buf: &'ser [u8]) -> Inspectable<'ser> { Inspectable::try_from(buf).expect("Could not decode buffer into inspectable") } /// Builds a path that can be used to traverse from an Inspectable AST. /// [Read here][crate::inspect#traversing] for details. pub fn inspect_path<'ser>() -> traverse::PathBuilder<'ser> { traverse::PathBuilder::new() } /// An object that represents something that bencode can /// decode to. Usable for reflection, modification, /// testing, and pretty printing. /// /// See the [module][crate::inspect] documentation for usage. /// /// # Construction /// /// You parse bencode into an Inspectable AST like this: /// ``` /// # use bendy; /// # use bendy::inspect::*; /// let bencode = b"i10e"; /// let inspectable = bendy::inspect::inspect(bencode); /// assert_eq!(inspectable.emit(), bencode); /// ``` /// /// Or use the [`Inspectable::try_from`] method for fallible construction. #[derive(Debug, PartialEq, Eq, Clone)] pub enum Inspectable<'ser> { Int(InInt<'ser>), String(InString<'ser>), Raw(Cow<'ser, [u8]>), Dict(InDict<'ser>), List(InList<'ser>), } impl<'ser> Inspectable<'ser> { /// Construct a new [`Inspectable::Raw`] pub fn new_raw(buf: &'ser [u8]) -> Inspectable<'ser> { Inspectable::Raw(Cow::from(buf)) } /// Construct a new [`Inspectable::Int`] pub fn new_int(i: i64) -> Inspectable<'ser> { let inint = InInt { bytes: Cow::Owned(i.to_string()), }; Inspectable::Int(inint) } /// Construct a new [`Inspectable::String`] pub fn new_string(buf: &'ser [u8]) -> Inspectable<'ser> { Inspectable::String(InString::new(buf)) } /// Construct a new [`Inspectable::Dict`] pub fn new_dict() -> Inspectable<'ser> { Inspectable::Dict(InDict::new()) } /// Construct a new [`Inspectable::List`] pub fn new_list() -> Inspectable<'ser> { Inspectable::List(InList::new()) } /// Get the serialized bencode representation of this [`Inspectable`] pub fn emit(&self) -> Vec { let mut res = Vec::new(); fn emit_str(s: &InString, out: &mut Vec) { let len = s.len().to_string(); out.extend_from_slice(len.as_bytes()); out.push(b':'); out.extend_from_slice(s.content()) } fn dispatch(i: &Inspectable, out: &mut Vec) { match i { Inspectable::String(x) => { emit_str(x, out); }, Inspectable::Raw(x) => { out.extend_from_slice(x); }, Inspectable::Int(x) => { out.push(b'i'); out.extend_from_slice(x.bytes.as_bytes()); out.push(b'e'); }, Inspectable::List(x) => { out.push(b'l'); for item in x.items.iter() { dispatch(item, out); } out.push(b'e'); }, Inspectable::Dict(x) => { out.push(b'd'); for InDictEntry { key, value } in x.items.iter() { dispatch(key, out); dispatch(value, out); } out.push(b'e'); }, } } dispatch(self, &mut res); res } } /// An [`Inspectable`] AST node representing a byte string. /// Stores the raw bytes of the bytestring, in a [`Cow`]. #[doc(alias = "bytestring")] #[doc(alias = "byte string")] #[doc(alias = "string")] #[derive(Debug, PartialEq, Eq, Clone)] pub struct InString<'ser> { /// The bytes in this ByteString. Does not include /// the prefix length and prefix separator `:`. pub bytes: Cow<'ser, [u8]>, /// If set, will provide a fake length prefix when emitted. pub fake_length: Option, } /// An [`Inspectable`] AST node representing an integer. /// Stores the raw bytes of the integer, in a [`Cow`]. #[doc(alias = "int")] #[doc(alias = "number")] #[derive(Debug, PartialEq, Eq, Clone)] pub struct InInt<'ser> { /// The raw bytes making up the number. /// Does not include the `i` prefix and `e` suffix. pub bytes: Cow<'ser, str>, } /// An [`Inspectable`] AST node representing a dictionary entry. /// This only appears in the [`InDict`] node, never as a variant /// of [`Inspectables`][Inspectable]. #[doc(alias = "dict entry")] #[doc(alias = "dictionary entry")] #[derive(Debug, PartialEq, Eq, Clone)] pub struct InDictEntry<'ser> { /// This struct field MUST contain an Inspectable::String /// for the resulting bencode to be valid. pub key: Inspectable<'ser>, pub value: Inspectable<'ser>, } /// An [`Inspectable`] AST node representing a dictionary. #[doc(alias = "dict")] #[doc(alias = "dictionary")] #[derive(Debug, PartialEq, Eq, Clone)] pub struct InDict<'ser> { /// The entries in the dictionary. /// Note that bendy will NOT decode a dict /// if the keys are unsorted. pub items: Vec>, } /// An [`Inspectable`] AST node representing a list. #[derive(Debug, PartialEq, Eq, Clone)] #[doc(alias = "list")] #[doc(alias = "vec")] pub struct InList<'ser> { pub items: Vec>, } impl<'ser> InInt<'ser> { pub fn new(buf: &'ser str) -> Self { InInt { bytes: Cow::from(buf), } } /// Returns an i64 of the value represented. /// Panics if this is not possible. #[must_use] pub fn as_i64(&self) -> i64 { self.bytes.parse().expect("Could not parse InInt as i64") } } impl<'ser> InString<'ser> { pub fn new(buf: &'ser [u8]) -> InString<'ser> { InString { bytes: Cow::from(buf), fake_length: None, } } /// Returns the number of bytes in the bytestring, /// or the fake length of the bytestring if set. pub fn len(&self) -> usize { self.fake_length.unwrap_or_else(|| self.bytes.len()) } /// Returns true if there are no bytes in the bytestring, /// or if the fake length of the bytestring is 0 if set. pub fn is_empty(&self) -> bool { self.len() == 0 } /// Convenience method to get an immutable reference /// to the bytestring's contents. pub fn content(&self) -> &[u8] { &self.bytes } } impl<'ser> InList<'ser> { #[allow(clippy::new_without_default)] pub fn new() -> Self { InList { items: Vec::new() } } } impl<'ser> InDict<'ser> { #[allow(clippy::new_without_default)] pub fn new() -> Self { InDict { items: Vec::new() } } } impl<'ser> InDictEntry<'ser> { /// Creates a new dict entry. If the given key is not a byte string /// ([`Inspectable::String`]) then bendy will not decode the resulting bencode. pub fn new(key: Inspectable<'ser>, value: Inspectable<'ser>) -> Self { InDictEntry { key, value } } } #[cfg(test)] mod tests { use super::*; #[test] fn all_traversal_test() { let buf = b"\ l\ i99e\ 5:hello\ d\ 3:one\ i11e\ 3:two\ i22e\ 5:zzzzz\ i33e\ e\ e"; let i = inspect(buf); let l = i.list_ref(); assert_eq!(3, l.items.len()); assert_eq!(99, l.nth_ref(0).int_ref().as_i64()); assert_eq!(b"hello".as_slice(), &*l.nth_ref(1).string_ref().bytes); let d = i.list_ref().nth_ref(2).dict_ref(); let t0 = d.nth_ref(0); let t1 = d.nth_ref(1); let t2 = d.entry_ref(b"zzzzz"); assert_eq!(b"one".as_slice(), &*t0.key.string_ref().bytes); assert_eq!(b"two".as_slice(), &*t1.key.string_ref().bytes); assert_eq!(b"zzzzz".as_slice(), &*t2.key.string_ref().bytes); assert_eq!(11, t0.value.int_ref().as_i64()); assert_eq!(22, t1.value.int_ref().as_i64()); assert_eq!(33, t2.value.int_ref().as_i64()); } #[test] fn int_modify_test() { let buf = b"i64e"; let mut i = inspect(buf); assert_eq!(64, i.int().as_i64()); i.int().set(32); assert_eq!(32, i.int().as_i64()); assert_eq!("i32e", i.to_string().as_str()); assert_eq!(b"i32e", i.emit().as_slice()); let buf = b"\ l\ i43770e\ d\ 3:one\ i11e\ e\ e"; let mut i = inspect(buf); i.list().nth(0).int().set(2); i.list().nth(1).dict().nth(0).value.int().set(1); assert_eq!(b"li2ed3:onei1eee", i.emit().as_slice()); } #[test] fn fake_bytestring_length_test() { let buf = b"\ l\ 5:hello\ d\ 3:one\ i11e\ e\ e"; let mut i = inspect(buf); i.list().nth(0).string().set_fake_length(20); i.list() .nth(1) .dict() .nth(0) .key .string() .set_fake_length(0); assert_eq!(b"l20:hellod0:onei11eee", i.emit().as_slice()); assert_eq!("l20:hellod0:onei11eee", i.to_string().as_str()); } #[test] fn bytestring_set_content_test() { let buf = b"\ l\ 5:hello\ 5:world\ d\ 5:hello\ 5:world\ e\ e"; let mut i = inspect(buf); i.list() .nth(0) .string() .set_content_string("one".to_string()); i.list().nth(1).string().set_content_str("two"); let entry = i.list().nth(2).dict().nth(0); entry.key.string().set_content_u8(b"three"); entry.value.string().set_content_vec(Vec::from(b"four")); assert_eq!(b"l3:one3:twod5:three4:fouree", i.emit().as_slice()); assert_eq!("l3:one3:twod5:three4:fouree", i.to_string().as_str()); let mut i = inspect(b"5:hello"); i.string().set_content_u8(b"\x00\x01"); assert_eq!(b"2:\x00\x01", i.emit().as_slice()); assert_eq!("2:\\x00\\x01", i.to_string().as_str()); } } bendy-0.6.1/src/inspect/mutate.rs000064400000000000000000000317741046102023000150460ustar 00000000000000//! Methods for mutating [`Inspectable`]s. //! //! Refer to the [documentation here][crate::inspect#mutating]. use core::ops::RangeBounds; use crate::inspect::*; impl<'ser, 'obj, 'otherser> Inspectable<'ser> where 'otherser: 'ser, { // Replaces one Inspectable with another. The replacement // must have a lifetime at least as long as the one it is // replacing. pub fn replace(&'obj mut self, other: Inspectable<'otherser>) { *self = other; } } impl<'ser, 'obj> Inspectable<'ser> { /// Clones an Inspectable AST and applies the given function to /// the node indicated by the given Path on the resulting clone. /// /// Panics if the Path fails to find a node. #[must_use] pub fn clone_and_mutate( &'obj self, path: &crate::inspect::traverse::PathBuilder, f: F, ) -> Inspectable<'ser> where F: FnOnce(&mut Inspectable<'ser>), { let mut c = self.clone(); c.mutate(path, f); c } /// Applies the given function to the node in the Inspectable AST indicated by the given Path. /// /// Panics if the Path fails to find a node. pub fn mutate(&'obj mut self, path: &crate::inspect::traverse::PathBuilder, f: F) where F: FnOnce(&mut Inspectable<'ser>), { self.find(path).apply(f) } /// Applies the given function to the Inspectable pub fn apply(&'obj mut self, f: F) where F: FnOnce(&mut Inspectable<'ser>), { f(self); } /// Remove the nth list item or dict entry. /// /// Panics if this is not an [`Inspectable::Dict`] or [`Inspectable::List`]. pub fn remove_nth(&'obj mut self, idx: usize) { match self { Inspectable::Dict(x) => x.remove_nth(idx), Inspectable::List(x) => x.remove_nth(idx), _ => panic!("Remove Nth mutation only available on Dicts and Lists"), } } /// Attempts an [`InString::truncate`] operation on the Inspectable. /// /// Panics if this is not an [`Inspectable::String`]. pub fn truncate(&'obj mut self) { match self { Inspectable::String(instring) => instring.truncate(), _ => panic!("Truncate mutation only available on ByteStrings"), } } /// Attempts an [`InString::set_content_byterange`] operation on the Inspectable. /// /// Panics if this is not an [`Inspectable::String`]. pub fn set_content_byterange(&'obj mut self, range: R, byte: u8) where R: RangeBounds, { match self { Inspectable::String(instring) => instring.set_content_byterange(range, byte), _ => panic!("Set Content ByteRange mutation only available on ByteStrings"), } } /// Applies one of the following operations on the Inspectable, depending on its type: /// * [`InString::clear_content`] /// * [`InInt::clear_content`] /// * [`Vec::clear`] for [`Inspectable::Raw`] /// * [`InDict::clear_content`] /// * [`InList::clear_content`] pub fn clear_content(&'obj mut self) { match self { Inspectable::String(x) => x.clear_content(), Inspectable::Int(x) => x.clear_content(), Inspectable::Raw(x) => x.to_mut().clear(), Inspectable::Dict(x) => x.clear_content(), Inspectable::List(x) => x.clear_content(), } } } impl<'ser, 'obj, 'other> Inspectable<'ser> { /// Removes all entries in the dict with the given key. /// Note that it's normally an error for a bencode dict to have /// multiple identical keys. /// /// Assumes that all entries' keys are Byte Strings, which is /// also required for valid bencode. pub fn remove_entry(&'obj mut self, dict_key: &'other [u8]) { match self { Inspectable::Dict(x) => x.remove_entry(dict_key), _ => panic!("Remove Entry mutation only available on Dicts"), } } } impl<'ser, 'obj> InInt<'ser> { /// Sets the InInt to a specified i64 value. /// Allocates a String. pub fn set(&'obj mut self, value: i64) { *self.bytes.to_mut() = value.to_string(); } /// Set the integer to 0. pub fn clear_content(&'obj mut self) { self.set(0); } } impl<'ser, 'obj> InDict<'ser> { /// Sets the InInt to a specified i64 value. /// Allocates a String. pub fn clear_content(&'obj mut self) { self.items.clear(); } /// Remove the nth entry in the dict. pub fn remove_nth(&'obj mut self, idx: usize) { self.items.remove(idx); } /// Sorts the dictionary's entries by key, so that valid bencode is /// produced (assuming no other problems, like duplicate keys.) /// /// Assumes all entries' keys are byte strings ([`Inspectable::String`] /// objects). Panics otherwise. /// /// # Example /// ``` /// # use bendy::inspect::*; /// let mut d = Inspectable::new_dict(); /// d.dict().items.push(InDictEntry::new( /// Inspectable::new_string(b"zzz"), /// Inspectable::new_int(1), /// )); /// d.dict().items.push(InDictEntry::new( /// Inspectable::new_string(b"aaa"), /// Inspectable::new_int(0), /// )); /// d.dict().sort(); /// assert_eq!(&d.emit(), b"d3:aaai0e3:zzzi1ee"); /// ``` pub fn sort(&'obj mut self) { self.items.sort_unstable_by(|a, b| { let a = &*a.key.string_ref().bytes; let b = &*b.key.string_ref().bytes; a.cmp(b) }); } } impl<'ser, 'obj, 'other> InDict<'ser> { /// Removes all entries in the dict with the given key. /// Note that it's normally an error for a bencode dict to have /// multiple identical keys. /// /// Assumes that all entries' keys are Byte Strings, which is /// also required for valid bencode. pub fn remove_entry(&'obj mut self, dict_key: &'other [u8]) { self.items .retain(|entry| entry.key.string_ref().bytes != dict_key); } } impl<'ser, 'obj> InList<'ser> { /// Empties the list. pub fn clear_content(&'obj mut self) { self.items.clear(); } /// Removes the nth item in the list. pub fn remove_nth(&'obj mut self, idx: usize) { self.items.remove(idx); } } impl<'ser, 'obj> InString<'ser> { /// Set a fake length for the bytestring. This will change /// its length prefix when emitted as bencode. pub fn set_fake_length(&'obj mut self, length: usize) { self.fake_length = Some(length); } /// Unset the fake length. This will cause the length /// prefix to be correctly calculated and used when /// emitted as bencode. pub fn clear_fake_length(&'obj mut self) { self.fake_length = None; } /// Change the contents of the bytestring. Takes a string /// which is coerced into a [`Vec`]. pub fn set_content_string(&'obj mut self, other: String) { self.bytes = Cow::Owned(other.into()); } /// Change the contents of the bytestring. pub fn set_content_vec(&'obj mut self, other: Vec) { self.bytes = Cow::Owned(other); } /// Truncates the bytestring to half its size. Empties it /// if the bytestring is too short to be truncated. pub fn truncate(&'obj mut self) { let bytes = self.bytes.to_mut(); let len = bytes.len(); let new_len = len / 2; if len <= new_len { bytes.clear(); return; } bytes.truncate(new_len); } /// Empties the bytestring. Unless a fake length has been set, /// this will emit as the bencode `0:`. pub fn clear_content(&'obj mut self) { self.bytes.to_mut().clear(); } /// Set the specified [`Range`][std::ops::Range] of bytes in the bytestring to a specified byte. pub fn set_content_byterange(&'obj mut self, range: R, byte: u8) where R: RangeBounds, { let len = self.bytes.len(); let (start, stop) = normalize_range(range, len); let bytes = self.bytes.to_mut(); for i in start..stop { let mref = bytes .get_mut(i) .expect("Tried to replace byte outside of ByteString"); *mref = byte; } } } impl<'me, 'other, 'ser> InString<'ser> where 'other: 'ser, { /// Change the contents of the bytestring. Takes an [`&str`] /// and stores a reference to its raw bytes in a [`Cow`]. pub fn set_content_str(&'me mut self, other: &'other str) { self.bytes = Cow::from(other.as_bytes()); } /// Change the contents of the bytestring. Takes a /// reference to raw bytes and stores them in a [`Cow`]. pub fn set_content_u8(&'me mut self, other: &'other [u8]) { self.bytes = Cow::from(other); } } // Not happy about this. Couldn't find a way to get an iterator from a `RangeBounds`. // And the `Range` type doesn't allow e.g. `..`, `4..`, `..49`, `2..=6` etc to be used. fn normalize_range(range: R, max_end: usize) -> (usize, usize) where R: RangeBounds, { // Unstable for now. See: https://github.com/rust-lang/rust/issues/137300 // if range.is_empty() { // panic!("Can't set byterange contents with an empty range"); // } let beg = range.start_bound(); let end = range.end_bound(); let start = match beg { core::ops::Bound::Included(n) => *n, // Excluded start bound can't be expressed using the range // syntax, so I haven't tested it. Probably doesn't matter? core::ops::Bound::Excluded(n) => n + 1, core::ops::Bound::Unbounded => 0, }; let stop = match end { core::ops::Bound::Included(n) => *n + 1, core::ops::Bound::Excluded(n) => *n, core::ops::Bound::Unbounded => max_end, }; if start >= stop { panic!("Can't set byterange contents with an empty range"); } (start, stop) } #[cfg(test)] mod tests { use super::*; #[test] fn set_byterange_test() { let mut i = inspect(b"5:AAAAA"); assert_eq!(5, i.string().bytes.len()); i.string().set_content_byterange(.., b'0'); assert_eq!(b"5:00000", i.emit().as_slice()); let mut i = inspect(b"5:AAAAA"); i.string().set_content_byterange(0.., b'0'); assert_eq!(b"5:00000", i.emit().as_slice()); let mut i = inspect(b"5:AAAAA"); i.string().set_content_byterange(0..=4, b'0'); assert_eq!(b"5:00000", i.emit().as_slice()); let mut i = inspect(b"5:AAAAA"); i.string().set_content_byterange(0..5, b'0'); assert_eq!(b"5:00000", i.emit().as_slice()); let mut i = inspect(b"5:AAAAA"); i.string().set_content_byterange(0..=0, b'0'); assert_eq!(b"5:0AAAA", i.emit().as_slice()); let mut i = inspect(b"5:AAAAA"); i.string().set_content_byterange(0..1, b'0'); assert_eq!(b"5:0AAAA", i.emit().as_slice()); let mut i = inspect(b"5:AAAAA"); i.string().set_content_byterange(0..=1, b'0'); assert_eq!(b"5:00AAA", i.emit().as_slice()); let mut i = inspect(b"5:AAAAA"); i.string().set_content_byterange(0..2, b'0'); assert_eq!(b"5:00AAA", i.emit().as_slice()); let mut i = inspect(b"5:AAAAA"); i.string().set_content_byterange(0..3, b'0'); assert_eq!(b"5:000AA", i.emit().as_slice()); let mut i = inspect(b"5:AAAAA"); i.string().set_content_byterange(0..4, b'0'); assert_eq!(b"5:0000A", i.emit().as_slice()); let mut i = inspect(b"5:AAAAA"); i.string().set_content_byterange(2..4, b'0'); assert_eq!(b"5:AA00A", i.emit().as_slice()); let mut i = inspect(b"5:AAAAA"); i.string().set_content_byterange(2..5, b'0'); assert_eq!(b"5:AA000", i.emit().as_slice()); } #[test] #[should_panic] fn empty_0() { let mut i = inspect(b"5:AAAAA"); i.string().set_content_byterange(0..0, b'0'); } #[test] #[should_panic] fn empty_1() { let mut i = inspect(b"5:AAAAA"); i.string().set_content_byterange(1..1, b'0'); } #[test] #[should_panic] fn empty_reversed() { let mut i = inspect(b"5:AAAAA"); i.string().set_content_byterange(5..1, b'0'); } #[test] #[should_panic] fn out_of_bounds_range_inclusive() { let mut i = inspect(b"5:AAAAA"); i.string().set_content_byterange(2..=5, b'0'); } #[test] #[should_panic] fn out_of_bounds_range() { let mut i = inspect(b"5:AAAAA"); i.string().set_content_byterange(2..6, b'0'); } #[test] fn replace_test() { let buf = b"\ l\ i99e\ 5:hello\ d\ 3:one\ i11e\ 3:two\ i22e\ e\ e"; let mut i = inspect(buf); let l = i.list(); l.nth(0).replace(Inspectable::new_string(b"aaa")); l.nth(1).replace(inspect(b"li1ei2ei3ee")); l.nth(2).replace(Inspectable::new_int(5)); assert_eq!(b"l3:aaali1ei2ei3eei5ee", i.emit().as_slice()); } } bendy-0.6.1/src/inspect/parse.rs000064400000000000000000000042531046102023000146510ustar 00000000000000//! Internal: Parsing bencode into a tree of [`Inspectable`] objects. #[cfg(not(feature = "std"))] use alloc::{ string::{String, ToString}, vec::Vec, }; #[cfg(feature = "std")] use std::vec::Vec; use crate::{ decoding::{Decoder, DictDecoder, Error as DecodeError, ListDecoder, Object}, inspect::*, state_tracker::StructureError, }; impl<'ser> TryFrom<&'ser [u8]> for Inspectable<'ser> { type Error = DecodeError; /// Parses bencode data into an [`Inspectable`]. fn try_from(buf: &'ser [u8]) -> Result, Self::Error> { let mut decoder = Decoder::new(buf); let obj = decoder .next_object()? .ok_or(StructureError::UnexpectedEof)?; Self::try_from(obj) } } impl<'obj, 'ser> TryFrom> for Inspectable<'ser> { type Error = DecodeError; /// Parses a [`Decoder`] provided [`Object`] into an [`Inspectable`]. fn try_from(object: Object<'obj, 'ser>) -> Result, DecodeError> { Ok(match object { Object::List(ld) => Inspectable::List(InList::try_from(ld)?), Object::Dict(dd) => Inspectable::Dict(InDict::try_from(dd)?), Object::Integer(i) => Inspectable::Int(InInt::new(i)), Object::Bytes(b) => Inspectable::String(InString::new(b)), }) } } impl<'obj, 'ser> TryFrom> for InList<'ser> { type Error = DecodeError; fn try_from(mut ld: ListDecoder<'obj, 'ser>) -> Result { let mut items: Vec> = Vec::new(); while let Some(item) = ld.next_object()? { items.push(item.try_into()?); } Ok(InList { items }) } } impl<'obj, 'ser> TryFrom> for InDict<'ser> { type Error = DecodeError; fn try_from(mut dd: DictDecoder<'obj, 'ser>) -> Result { let mut items: Vec = Vec::new(); while let Some((k, v)) = dd.next_pair()? { items.push(InDictEntry { key: Inspectable::String(InString::new(k)), value: v.try_into()?, }) } Ok(InDict { items }) } } bendy-0.6.1/src/inspect/traverse.rs000064400000000000000000000361741046102023000154010ustar 00000000000000//! Ways of traversing the AST-like structure of [`Inspectable`]s. //! //! Refer to the [documentation here][crate::inspect#traversing]. use smallvec::SmallVec; use crate::inspect::*; /// A `Step` in a search path, which selects what next traversal /// will be applied to an [`Inspectable`] tree. /// /// A search path is a list of Steps. #[derive(Debug, Clone)] pub enum Step<'a> { /// Searches for a child, which is either an [`Inspectable`] or a [Dict /// Entry][crate::inspect::InDictEntry]. /// Only finds the first child matching the specific condition. Search(Search<'a>), /// Access the nth child of a List or Dict. Nth(usize), /// Accesses a Dict Entry with the given key. Entry(&'a [u8]), /// Assume current item is an Dict Entry, access the key. Key, /// Assume current item is an Dict Entry, access the value. Value, } /// An object that represents what to search for in a [`Step::Search`]. #[derive(Debug, Clone)] pub enum Search<'a> { /// Search for a String with this exact value. ByteString(&'a [u8]), /// Search for a Dict Entry that has this exact key. DictKey(&'a [u8]), /// Search for this exact integer. Int(Cow<'a, str>), } type Path<'a> = SmallVec<[Step<'a>; 20]>; /// A builder for a path. A path is a list of [`Step`]s undertaken to traverse /// an [`Inspectable`] AST. /// /// Use the methods provided to add new steps to the path, then provide a reference /// to the [`PathBuilder`] to one of these methods: /// * [`Inspectable::find`] /// * [`Inspectable::find_ref`] /// * [`Inspectable::clone_and_mutate`] /// * [`Inspectable::mutate`] /// /// Unlike the standard build pattern, no `.build()` function is needed to reify /// the builder. /// /// The methods named `into_*` all take ownership of the PathBuilder, add the step, /// then return the builder. This reduces memory allocation, but may be inconvenient. /// /// The methods without the `into_` prefix will create a clone of the PathBuilder, /// add the step to that clone, then return the clone. This may be more convenient /// if you are building multiple paths that all start with the same steps. /// /// The builder can contain up to 20 steps before it needs to allocate. For fuzzing, /// manually building structures using the [`Inspectable`] constructor methods is /// recommended instead of mutating existing [`Inspectable`]s. #[derive(Debug, Clone)] pub struct PathBuilder<'a> { pub steps: Path<'a>, } impl<'obj, 'pb, 'step> PathBuilder<'pb> where 'step: 'pb, { #[allow(clippy::new_without_default)] pub fn new() -> Self { PathBuilder { steps: Default::default(), } } /// Descend into a specific item in a list or entry in a dict, given its index. /// /// For dicts, MUST be followed by a call to one of: [`.key`][PathBuilder::key], /// [`.into_key`][PathBuilder::into_key], [`.value`][PathBuilder::value], /// or [`.into_value`][PathBuilder::into_value]. pub fn into_nth(mut self, idx: usize) -> Self { self.steps.push(Step::Nth(idx)); self } /// Descend into a specific item in a list or entry in a dict, given its index. /// /// For dicts, MUST be followed by a call to one of: [`.key`][PathBuilder::key], /// [`.into_key`][PathBuilder::into_key], [`.value`][PathBuilder::value], /// or [`.into_value`][PathBuilder::into_value]. pub fn nth(&'obj self, idx: usize) -> Self { let next = self.clone(); next.into_nth(idx) } /// Access a Dict Entry that has the given key byte string. /// /// MUST be followed by a call to one of: [`.key`][PathBuilder::key], /// [`.into_key`][PathBuilder::into_key], [`.value`][PathBuilder::value], /// or [`.into_value`][PathBuilder::into_value]. pub fn into_entry(mut self, entry_key: &'step [u8]) -> PathBuilder<'pb> { self.steps.push(Step::Entry(entry_key)); self } /// Access a Dict Entry that has the given key byte string. /// /// MUST be followed by a call to one of: [`.key`][PathBuilder::key], /// [`.into_key`][PathBuilder::into_key], [`.value`][PathBuilder::value], /// or [`.into_value`][PathBuilder::into_value]. pub fn entry(&'obj self, entry_key: &'step [u8]) -> PathBuilder<'pb> { let next = self.clone(); next.into_entry(entry_key) } /// Access the key part of a Dict Entry. pub fn into_key(mut self) -> Self { self.steps.push(Step::Key); self } /// Access the key part of a Dict Entry. pub fn key(&'obj self) -> Self { let next = self.clone(); next.into_key() } /// Access the value part of a Dict Entry. pub fn into_value(mut self) -> Self { self.steps.push(Step::Value); self } /// Access the value part of a Dict Entry. pub fn value(&'obj self) -> Self { let next = self.clone(); next.into_value() } /// Search for byte strings that are not dict keys. pub fn into_search_bytestring(mut self, bytestring: &'step [u8]) -> PathBuilder<'pb> { self.steps .push(Step::Search(Search::ByteString(bytestring))); self } /// Search for byte strings that are not dict keys. pub fn search_bytestring(&'obj self, bytestring: &'step [u8]) -> PathBuilder<'pb> { let next = self.clone(); next.into_search_bytestring(bytestring) } /// Search for Dict Entries that have the given key. /// /// MUST be followed by a call to one of: [`.key`][PathBuilder::key], /// [`.into_key`][PathBuilder::into_key], [`.value`][PathBuilder::value], /// or [`.into_value`][PathBuilder::into_value]. pub fn into_search_entry(mut self, dict_key: &'step [u8]) -> PathBuilder<'pb> { self.steps.push(Step::Search(Search::DictKey(dict_key))); self } /// Search for Dict Entries that have the given key. /// /// MUST be followed by a call to one of: [`.key`][PathBuilder::key], /// [`.into_key`][PathBuilder::into_key], [`.value`][PathBuilder::value], /// or [`.into_value`][PathBuilder::into_value]. pub fn search_entry(&'obj self, dict_key: &'step [u8]) -> PathBuilder<'pb> { let next = self.clone(); next.into_search_entry(dict_key) } /// Search for an integer, provided as a string slice. pub fn into_search_int(mut self, number: &'step str) -> PathBuilder<'pb> { self.steps.push(Step::Search(Search::Int(number.into()))); self } /// Search for an integer, provided as a string slice. pub fn search_int(&'obj self, number: &'step str) -> PathBuilder<'pb> { let next = self.clone(); next.into_search_int(number) } /// Search for an integer, this function converts an i64 to a string slice for you. pub fn into_search_int_i64(mut self, number: i64) -> PathBuilder<'pb> { self.steps .push(Step::Search(Search::Int(number.to_string().into()))); self } /// Search for an integer, this function converts an i64 to a string slice for you. pub fn search_int_i64(&'obj self, number: i64) -> PathBuilder<'pb> { let next = self.clone(); next.into_search_int_i64(number) } } impl<'obj, 'ser, 'pb, 'pbobj> Inspectable<'ser> { /// Gets a mutable reference to the Inspectable pointed to by the given path. /// Panics if no Inspectable matches the given path. pub fn find(&'obj mut self, path: &'pbobj PathBuilder<'pb>) -> &'obj mut Inspectable<'ser> { let res = self.find_impl(&path.steps, 0); match res { None => panic!("Path did not resolve to an Inspectable: {:?}", path), Some(x) => x, } } /// Gets a reference to the Inspectable pointed to by the given path. /// Panics if no Inspectable matches the given path. pub fn find_ref(&'obj self, path: &'pbobj PathBuilder<'pb>) -> &'obj Inspectable<'ser> { let res = self.find_ref_impl(&path.steps, 0); match res { None => panic!("Path did not resolve to an Inspectable: {:?}", path), Some(x) => x, } } } macro_rules! finders { ($(($name:ident, $get:ident, $iter:ident, $( $mutable:ident )? ))*) => {$( impl<'obj, 'ser, 'pb, 'pbobj> Inspectable<'ser> { fn $name( &'obj $($mutable)? self, steps: &'pbobj Path<'pb>, pc: usize, ) -> Option<&'obj $($mutable)? Inspectable<'ser>> { let current_step = if let Some(x) = steps.get(pc) { x } else { return Some(self); }; let descend_into_entry = |entry: &'obj $($mutable)? InDictEntry<'ser>| -> Option<&'obj $($mutable)? Inspectable<'ser>> { match steps.get(pc + 1) { Some(Step::Key) => entry.key.$name(steps, pc + 2), Some(Step::Value) => entry.value.$name(steps, pc + 2), _ => panic!( "A path that selects a dict entry must then select either its key or its value" ), } }; match (self, current_step) { (Inspectable::Raw(_), _) => (), (s @ Inspectable::Int(_), Step::Search(Search::Int(x))) => { if *x == &*s.int_ref().bytes { return Some(s); } }, (Inspectable::Int(_), _) => (), (s @ Inspectable::String(_), Step::Search(Search::ByteString(x))) => { if *x == &*s.string_ref().bytes { return Some(s); } }, (Inspectable::String(_), Step::Search(_)) => (), (Inspectable::String(_), _) => (), (Inspectable::List(_), Step::Key) => (), (Inspectable::List(_), Step::Value) => (), (Inspectable::List(_), Step::Entry(_)) => (), (Inspectable::List(in_list), Step::Nth(idx)) => { let item = in_list.items.$get(*idx)?; return item.$name(steps, pc + 1); }, (Inspectable::List(in_list), Step::Search(_)) => { return in_list .items .$iter() .find_map(|item| item.$name(steps, pc)); }, (Inspectable::Dict(_), Step::Key) => (), (Inspectable::Dict(_), Step::Value) => (), (Inspectable::Dict(in_dict), Step::Nth(idx)) => { let entry = in_dict.items.$get(*idx)?; return descend_into_entry(entry); }, (Inspectable::Dict(in_dict), Step::Entry(key)) => { let entry = in_dict.items.$iter().find(|entry| { let entry_key = if let Inspectable::String(x) = &entry.key { x } else { return false; }; entry_key.bytes == *key })?; return descend_into_entry(entry); }, (Inspectable::Dict(in_dict), Step::Search(Search::DictKey(key))) => { return in_dict.items.$iter().find_map(|entry| { if let Inspectable::String(x) = &entry.key { if x.bytes == *key { return descend_into_entry(entry); } } entry.value.$name(steps, pc) }); }, (Inspectable::Dict(in_dict), Step::Search(_)) => { return in_dict .items .$iter() .find_map(|InDictEntry { value, .. }| value.$name(steps, pc)); }, }; None } } )*}; } finders!( (find_impl, get_mut, iter_mut, mut) (find_ref_impl, get, iter,) ); macro_rules! variant_accessors { ($(($name:ident, $mutname:ident, $source:ident, $target:ty, $targettype:ty))*) => {$( impl<'obj, 'ser> Inspectable<'ser> { #[doc=concat!("Coercion to immutable reference to [`", stringify!($targettype), "`]. Panics on failure.")] pub fn $name(&'obj self) -> &'obj $target { match self { Inspectable::$source(x) => x, _ => panic!("Attempted to take non-{} Inspectable as {}", stringify!($source), stringify!($target)) } } #[doc=concat!("Coercion to mutable reference to [`", stringify!($targettype), "`]. Panics on failure.")] pub fn $mutname(&'obj mut self) -> &'obj mut $target { match self { Inspectable::$source(x) => x, _ => panic!("Attempted to take non-{} Inspectable as mut_{}", stringify!($source), stringify!($target)) } } } )*}; } variant_accessors! { (string_ref, string, String, InString<'ser>, InString) (int_ref, int, Int, InInt<'ser>, InInt) (raw_ref, raw, Raw, Cow<'ser, [u8]>, [u8]) (dict_ref, dict, Dict, InDict<'ser>, InDict) (list_ref, list, List, InList<'ser>, InList) } impl<'obj, 'ser> InList<'ser> { pub fn nth_ref(&'obj self, idx: usize) -> &'obj Inspectable<'ser> { self.items .get(idx) .expect("Could not access nth Inspectable in list") } pub fn nth(&'obj mut self, idx: usize) -> &'obj mut Inspectable<'ser> { self.items .get_mut(idx) .expect("Could not mutably access nth Inspectable in list") } } impl<'obj, 'ser> InDict<'ser> { pub fn nth_ref(&'obj self, idx: usize) -> &'obj InDictEntry<'ser> { self.items .get(idx) .expect("Could not access nth Inspectable in list") } pub fn nth(&'obj mut self, idx: usize) -> &'obj mut InDictEntry<'ser> { self.items .get_mut(idx) .expect("Could not mutably access nth Inspectable in list") } } impl<'obj, 'ser, 'other> InDict<'ser> { pub fn entry_ref(&'obj self, name: &'other [u8]) -> &'obj InDictEntry<'ser> { self.items .iter() .find(|InDictEntry { key, .. }| key.string_ref().bytes == name) .expect("Could not find a Dict Entry with requested key") } pub fn entry(&'obj mut self, name: &'other [u8]) -> &'obj mut InDictEntry<'ser> { self.items .iter_mut() .find(|InDictEntry { key, .. }| key.string_ref().bytes == name) .expect("Could not find a Dict Entry with requested key") } } bendy-0.6.1/src/lib.rs000064400000000000000000000013441046102023000126360ustar 00000000000000//! Encodes and decodes bencoded structures. //! //! The decoder is explicitly designed to be zero-copy as much as possible, and to not //! accept any sort of invalid encoding in any mode (including non-canonical encodings) //! //! The encoder is likewise designed to ensure that it only produces valid structures. #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; #[cfg(all(test, feature = "serde"))] #[macro_use] mod assert_matches; pub mod decoding; pub mod encoding; pub mod state_tracker; #[cfg(any(test, feature = "inspect"))] pub mod inspect; #[cfg(feature = "serde")] pub mod serde; pub mod value; #[rustversion::since(1.40)] const _: () = { #[cfg(doctest)] doc_comment::doctest!("../../README.md"); }; bendy-0.6.1/src/serde/common.rs000064400000000000000000000014331046102023000144610ustar 00000000000000/// Standard library pub(crate) use std::{ convert::TryInto, fmt::{self, Display, Formatter}, iter::Peekable, num::ParseIntError, str::{self, Utf8Error}, }; pub(crate) use serde_ as serde; /// Dependencies pub(crate) use serde::{ Deserialize, de::{ DeserializeSeed, EnumAccess, IntoDeserializer, MapAccess, SeqAccess, VariantAccess, Visitor, }, ser::{ Serialize, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple, SerializeTupleStruct, SerializeTupleVariant, }, }; /// Structs and enums pub(crate) use crate::{ decoding::{self, Decoder, Tokens}, encoding::{self, Encoder, UnsortedDictEncoder}, serde::{Error, Result, ser::Serializer}, state_tracker::{StructureError, Token}, }; bendy-0.6.1/src/serde/de.rs000064400000000000000000000303141046102023000135610ustar 00000000000000//! Serde bencode deserialization. use crate::serde::common::*; /// Deserialize an instance of `T` from bencode pub fn from_bytes<'a, T>(s: &'a [u8]) -> Result where T: Deserialize<'a>, { Deserializer::from_bytes(s).deserialize() } /// Bencode deserializer pub struct Deserializer<'de> { forbid_trailing_bytes: bool, tokens: Peekable>, } impl<'de> Deserializer<'de> { /// Create a new `Deserializer` with the give byte slice pub fn from_bytes(input: &'de [u8]) -> Self { Deserializer { forbid_trailing_bytes: false, tokens: Decoder::new(input).tokens().peekable(), } } /// Return an error if trailing bytes remain after deserialization pub fn with_forbid_trailing_bytes(mut self, forbid_trailing_bytes: bool) -> Self { self.forbid_trailing_bytes = forbid_trailing_bytes; self } /// Consume the deserializer, producing an instance of `T` pub fn deserialize(mut self) -> Result where T: Deserialize<'de>, { let t = T::deserialize(&mut self)?; if self.forbid_trailing_bytes && self.tokens.next().is_some() { return Err(Error::TrailingBytes); } Ok(t) } } impl<'de> Deserializer<'de> { fn next_token(&mut self) -> Result> { match self.tokens.next() { Some(result) => Ok(result?), None => Err(Error::Decode(StructureError::UnexpectedEof.into())), } } fn next_integer(&mut self) -> Result<&'de str> { match self.next_token()? { Token::Num(num) => Ok(num), other => Err(decoding::Error::unexpected_token("Num", other.name()).into()), } } fn next_bytes(&mut self) -> Result<&'de [u8]> { match self.next_token()? { Token::String(bytes) => Ok(bytes), other => Err(decoding::Error::unexpected_token("String", other.name()).into()), } } fn next_string(&mut self) -> Result<&'de str> { let bytes = self.next_bytes()?; let string = str::from_utf8(bytes)?; Ok(string) } fn expect_list_begin(&mut self) -> Result<()> { match self.next_token()? { Token::List => Ok(()), other => Err(decoding::Error::unexpected_token("List", other.name()).into()), } } fn expect_dict_begin(&mut self) -> Result<()> { match self.next_token()? { Token::Dict => Ok(()), other => Err(decoding::Error::unexpected_token("Dict", other.name()).into()), } } fn expect_end(&mut self) -> Result<()> { match self.next_token()? { Token::End => Ok(()), other => Err(decoding::Error::unexpected_token("End", other.name()).into()), } } fn expect_empty_list(&mut self) -> Result<()> { self.expect_list_begin()?; self.expect_end()?; Ok(()) } fn peek_end(&mut self) -> bool { self.peek() == Some(Token::End) } fn peek(&mut self) -> Option> { if let Some(Ok(token)) = self.tokens.peek() { Some(*token) } else { None } } } impl<'de, 'a> serde::de::Deserializer<'de> for &'a mut Deserializer<'de> { type Error = Error; fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, { match self.peek() { Some(Token::Dict) => self.deserialize_map(visitor), Some(Token::String(_)) => self.deserialize_bytes(visitor), Some(Token::List) => self.deserialize_seq(visitor), Some(Token::Num(_)) => self.deserialize_i64(visitor), Some(Token::End) => Err(Error::Decode(StructureError::invalid_state("End").into())), None => Err(Error::Decode(StructureError::UnexpectedEof.into())), } } fn deserialize_bool(self, visitor: V) -> Result where V: Visitor<'de>, { match self.next_integer()? { "0" => visitor.visit_bool(false), "1" => visitor.visit_bool(true), other => Err(Error::InvalidBool(other.to_owned())), } } fn deserialize_i8(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_i8(self.next_integer()?.parse()?) } fn deserialize_i16(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_i16(self.next_integer()?.parse()?) } fn deserialize_i32(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_i32(self.next_integer()?.parse()?) } fn deserialize_i64(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_i64(self.next_integer()?.parse()?) } fn deserialize_i128(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_i128(self.next_integer()?.parse()?) } fn deserialize_u8(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_u8(self.next_integer()?.parse()?) } fn deserialize_u16(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_u16(self.next_integer()?.parse()?) } fn deserialize_u32(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_u32(self.next_integer()?.parse()?) } fn deserialize_u64(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_u64(self.next_integer()?.parse()?) } fn deserialize_u128(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_u128(self.next_integer()?.parse()?) } fn deserialize_f32(self, visitor: V) -> Result where V: Visitor<'de>, { let bytes = self.next_bytes()?; let bits = u32::from_be_bytes( bytes .try_into() .map_err(|_| Error::InvalidF32(bytes.len()))?, ); let value = f32::from_bits(bits); visitor.visit_f32(value) } fn deserialize_f64(self, visitor: V) -> Result where V: Visitor<'de>, { let bytes = self.next_bytes()?; let bits = u64::from_be_bytes( bytes .try_into() .map_err(|_| Error::InvalidF64(bytes.len()))?, ); let value = f64::from_bits(bits); visitor.visit_f64(value) } fn deserialize_char(self, visitor: V) -> Result where V: Visitor<'de>, { let s: &str = self.next_string()?; let count = s.chars().count(); if count != 1 { return Err(Error::InvalidChar(count)); } visitor.visit_char(s.chars().next().unwrap()) } fn deserialize_str(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_borrowed_str(self.next_string()?) } fn deserialize_string(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_str(visitor) } fn deserialize_bytes(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_borrowed_bytes(self.next_bytes()?) } fn deserialize_byte_buf(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_bytes(visitor) } fn deserialize_option(self, visitor: V) -> Result where V: Visitor<'de>, { self.expect_list_begin()?; let value = if self.peek_end() { visitor.visit_none() } else { visitor.visit_some(&mut *self) }; self.expect_end()?; value } fn deserialize_unit(self, visitor: V) -> Result where V: Visitor<'de>, { self.expect_empty_list()?; visitor.visit_unit() } fn deserialize_unit_struct(self, _name: &'static str, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_unit(visitor) } fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_newtype_struct(self) } fn deserialize_seq(self, visitor: V) -> Result where V: Visitor<'de>, { self.expect_list_begin()?; let value = visitor.visit_seq(&mut *self)?; self.expect_end()?; Ok(value) } fn deserialize_tuple(self, _len: usize, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_seq(visitor) } fn deserialize_tuple_struct( self, _name: &'static str, _len: usize, visitor: V, ) -> Result where V: Visitor<'de>, { self.deserialize_seq(visitor) } fn deserialize_map(self, visitor: V) -> Result where V: Visitor<'de>, { self.expect_dict_begin()?; let value = visitor.visit_map(&mut *self)?; self.expect_end()?; Ok(value) } fn deserialize_struct( self, _name: &'static str, _fields: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { self.expect_dict_begin()?; let value = visitor.visit_map(&mut *self)?; self.expect_end()?; Ok(value) } fn deserialize_enum( self, _name: &'static str, _variants: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { if self.peek() == Some(Token::Dict) { self.expect_dict_begin()?; visitor.visit_enum(self) } else { visitor.visit_enum(self.next_string()?.into_deserializer()) } } fn deserialize_identifier(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_str(visitor) } fn deserialize_ignored_any(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_any(visitor) } } impl<'de> SeqAccess<'de> for Deserializer<'de> { type Error = Error; fn next_element_seed(&mut self, seed: T) -> Result> where T: DeserializeSeed<'de>, { if self.peek_end() { return Ok(None); } seed.deserialize(self).map(Some) } } impl<'de> MapAccess<'de> for Deserializer<'de> { type Error = Error; fn next_key_seed(&mut self, seed: K) -> Result> where K: DeserializeSeed<'de>, { if self.peek_end() { return Ok(None); } seed.deserialize(self).map(Some) } fn next_value_seed(&mut self, seed: V) -> Result where V: DeserializeSeed<'de>, { seed.deserialize(self) } } impl<'de> EnumAccess<'de> for &mut Deserializer<'de> { type Error = Error; type Variant = Self; fn variant_seed(self, seed: V) -> Result<(V::Value, Self)> where V: DeserializeSeed<'de>, { Ok((seed.deserialize(&mut *self)?, self)) } } impl<'de> VariantAccess<'de> for &mut Deserializer<'de> { type Error = Error; fn unit_variant(self) -> Result<()> { Ok(()) } fn newtype_variant_seed(self, seed: T) -> Result where T: DeserializeSeed<'de>, { let value = seed.deserialize(&mut *self)?; self.expect_end()?; Ok(value) } fn tuple_variant(self, _len: usize, visitor: V) -> Result where V: Visitor<'de>, { let value = serde::de::Deserializer::deserialize_seq(&mut *self, visitor)?; self.expect_end()?; Ok(value) } fn struct_variant(self, _fields: &'static [&'static str], visitor: V) -> Result where V: Visitor<'de>, { let value = serde::de::Deserializer::deserialize_map(&mut *self, visitor)?; self.expect_end()?; Ok(value) } } bendy-0.6.1/src/serde/error.rs000064400000000000000000000074141046102023000143270ustar 00000000000000///! Serde error and result types use crate::serde::common::*; pub type Result = std::result::Result; /// An enumeration of potential errors that appear during serde serialiation and /// deserialization #[derive(Debug)] pub enum Error { /// Error that occurs if a map with a key type which does not serialize to /// a byte string is encountered ArbitraryMapKeysUnsupported, /// Error that occurs if methods on MapSerializer are called out of order MapSerializationCallOrder, /// Error that occurs if a bool is deserialized from an integer value other /// than `0` or `1` InvalidBool(String), /// Error that occurs if an f32 is deserialized from an string of length other /// than 4 InvalidF32(usize), /// Error that occurs if an f64 is deserialized from an string of length other /// than 8 InvalidF64(usize), /// Error that occurs if a char is deserialized from a string containing more /// than one character InvalidChar(usize), /// Error that occurs if trailing bytes remain after deserialization, if the /// deserializer is configured to forbid trailing bytes TrailingBytes, /// Error that occurs if a serde-related error occurs during serialization CustomEncode(String), /// Error that occurs if a serde-related error occurs during deserialization CustomDecode(String), /// Error that occurs if a problem is encountered during serialization Encode(encoding::Error), /// Error that occurs if a problem is encountered during deserialization Decode(decoding::Error), } impl From for Error { fn from(encoding_error: encoding::Error) -> Self { Error::Encode(encoding_error) } } impl From for Error { fn from(decoding_error: decoding::Error) -> Self { Error::Decode(decoding_error) } } impl From for Error { fn from(parse_int_error: ParseIntError) -> Self { Error::Decode(parse_int_error.into()) } } impl From for Error { fn from(utf8_error: Utf8Error) -> Self { Error::Decode(utf8_error.into()) } } impl serde::ser::Error for Error { fn custom(msg: T) -> Self where T: Display, { Error::CustomEncode(msg.to_string()) } } impl serde::de::Error for Error { fn custom(msg: T) -> Self { Error::CustomDecode(msg.to_string()) } } impl Display for Error { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { Error::CustomEncode(message) => write!(f, "Serialization failed: {}", message), Error::CustomDecode(message) => write!(f, "Deserialization failed: {}", message), Error::Encode(error) => write!(f, "{}", error), Error::Decode(error) => write!(f, "{}", error), Error::InvalidBool(value) => write!(f, "Invalid integer value for bool: `{}`", value), Error::InvalidF32(length) => { write!(f, "Invalid length byte string value for f32: {}", length) }, Error::InvalidF64(length) => { write!(f, "Invalid length byte string value for f64: {}", length) }, Error::InvalidChar(length) => { write!(f, "Invalid length string value for char: {}", length) }, Error::TrailingBytes => write!(f, "Trailing bytes remain after deserializing value"), Error::ArbitraryMapKeysUnsupported => write!( f, "Maps with key types that do not serialize to byte strings are unsupported", ), Error::MapSerializationCallOrder => { write!(f, "Map serialization methods called out of order") }, } } } impl std::error::Error for Error {} bendy-0.6.1/src/serde/mod.rs000064400000000000000000000353211046102023000137530ustar 00000000000000//! Serde Serialization and Deserialization //! ======================================= //! //! Values can be serialized to bencode with `bendy::serde::to_bytes`, and //! deserialized from bencode with `bendy::serde::from_bytes`: //! //! ``` //! use bendy::serde::{from_bytes, to_bytes}; //! use serde_ as serde; //! use serde_derive::{Deserialize, Serialize}; //! //! assert_eq!(to_bytes(&10).unwrap(), b"i10e"); //! assert_eq!(from_bytes::(b"i10e").unwrap(), 10); //! //! #[serde(crate = "serde_")] //! #[derive(Serialize, Deserialize, Debug, PartialEq)] //! struct Foo { //! bar: bool, //! } //! //! assert_eq!(to_bytes(&Foo { bar: true }).unwrap(), b"d3:bari1ee"); //! assert_eq!(from_bytes::(b"d3:bari1ee").unwrap(), Foo { bar: true }); //! ``` //! //! Bencode Representations //! ----------------------- //! //! Rust types and values are represented in bencode as follows: //! //! - `true`: The integer value `1`. //! - `false`: The integer value `0`. //! - `char`: A string containing the UTF-8 encoding of the value. //! - `f32`: Represented as a length-four bencode byte string containing the big- //! endian order bytes of the IEEE-754 representation of the value. //! - `f64`: Represented as a length-eight bencode byte string containing the big- //! endian order bytes of the IEEE-754 representation of the value. //! - `()`: Represented as the empty bencode list, `le`. //! - `Some(t)`: Represented as a list containing the bencoding of `t`. //! - `None`: Represented as the empty list. //! - maps, including BTreeMap and HashMap: bencoded dictionaries. //! - record structs: Represented as bencoded dictionaries with the fields of the //! struct represented as UTF-8 keys mapped to the bencoded serializations of the //! values. //! - tuple structs: Represented as bencoded lists containing the serialized values //! of the fields. //! - unit structs: Represented as the empty bencode list, `le`. //! - enum unit variants: Represented as a string containing the name of the variant, //! - enum newtype variants: Represented as a dict mapping the name of the variant //! to the value the variant contains. //! - enum tuple variants: Represented as a dict mapping the name of the variant //! to a list containing the fields of the enum. //! - enum struct variants: Represented as a dict mapping the name of the variant //! to the struct representation of the fields of the variant. //! - untagged enums: Repesented as the variant value without any surrounding dictionary. //! //! Bencode dictionary keys may only be byte strings. For this reason, map types with //! keys that do not serialize as byte strings are unsupported. //! //! Note that values of type `f32` and `f64` do not conform to bencode's canonical //! representation rules. For example, both `f32` and `f64` support negative zero //! values which have different bit patterns, but which represent the same logical //! value as positive zero. //! //! If you require bencoded values to have canonical representations, then it is best //! to avoid floating point values. //! //! Example Representations //! ----------------------- //! //! ``` //! use bendy::serde::to_bytes; //! use serde::Serialize; //! use serde_ as serde; //! use serde_derive::Serialize; //! use std::collections::HashMap; //! //! fn repr(value: impl Serialize, bencode: impl AsRef<[u8]>) { //! assert_eq!(to_bytes(&value).unwrap(), bencode.as_ref()); //! } //! //! repr(true, "i1e"); //! repr(false, "i0e"); //! repr((), "le"); //! repr('a', "1:a"); //! repr('Å', b"2:\xC3\x85"); //! repr(0, "i0e"); //! repr(-15, "i-15e"); //! repr(1.0f32, b"4:\x3F\x80\x00\x00"); //! repr(1.0f64, b"8:\x3F\xF0\x00\x00\x00\x00\x00\x00"); //! //! let none: Option = None; //! repr(none, "le"); //! repr(Some(0), "li0ee"); //! //! let mut map = HashMap::new(); //! map.insert("foo", 1); //! map.insert("bar", 2); //! repr(map, "d3:bari2e3:fooi1ee"); //! //! #[serde(crate = "serde_")] //! #[derive(Serialize)] //! struct Unit; //! repr(Unit, "le"); //! //! #[serde(crate = "serde_")] //! #[derive(Serialize)] //! struct Newtype(String); //! repr(Newtype("foo".into()), "3:foo"); //! //! #[serde(crate = "serde_")] //! #[derive(Serialize)] //! struct Tuple(bool, i32); //! repr(Tuple(false, 100), "li0ei100ee"); //! //! #[serde(crate = "serde_")] //! #[derive(Serialize)] //! struct Record { //! a: String, //! b: bool, //! } //! //! repr( //! Record { //! a: "hello".into(), //! b: false, //! }, //! "d1:a5:hello1:bi0ee", //! ); //! //! #[serde(crate = "serde_")] //! #[derive(Serialize)] //! enum Enum { //! Unit, //! Newtype(i32), //! Tuple(bool, i32), //! Struct { a: char, b: bool }, //! } //! //! repr(Enum::Unit, "4:Unit"); //! repr(Enum::Newtype(-1), "d7:Newtypei-1ee"); //! repr(Enum::Tuple(true, 10), "d5:Tupleli1ei10eee"); //! repr(Enum::Struct { a: 'x', b: true }, "d6:Structd1:a1:x1:bi1eee"); //! //! #[serde(untagged)] //! #[serde(crate = "serde_")] //! #[derive(Serialize)] //! enum Untagged { //! Foo { x: i32 }, //! Bar { y: char }, //! } //! //! repr(Untagged::Foo { x: -1 }, "d1:xi-1ee"); //! repr(Untagged::Bar { y: 'z' }, "d1:y1:ze"); //! ``` mod common; pub mod de; pub mod error; pub mod ser; pub use de::{Deserializer, from_bytes}; pub use error::{Error, Result}; pub use ser::{Serializer, to_bytes}; #[cfg(test)] mod tests { use super::common::*; use std::{collections::HashMap, fmt::Debug}; use super::{ de::{Deserializer, from_bytes}, ser::to_bytes, }; use serde::{de::DeserializeOwned, ser::Serialize}; use serde_derive::{Deserialize, Serialize}; fn case(value: V, want: B) where V: Serialize + DeserializeOwned + PartialEq + Debug, B: AsRef<[u8]>, { let want = want.as_ref(); let encoded = match to_bytes(&value) { Ok(have) => { assert_eq!( have, want, "Expected `{}` but got `{}` when serializing `{:?}`", String::from_utf8_lossy(&want), String::from_utf8_lossy(&have), value ); have }, Err(err) => panic!("Failed to serialize `{:?}`: {}", value, err), }; let deserialized = match from_bytes::(&encoded) { Ok(deserialized) => deserialized, Err(error) => panic!( "Failed to deserialize `{:?}` from `{}`: {}", value, String::from_utf8_lossy(&encoded), error ), }; assert_eq!( deserialized, value, "Deserialized value != original: `{:?}` != `{:?}`", deserialized, value ); } fn case_borrowed(value: V, want: B) where V: Serialize + Debug, B: AsRef<[u8]>, { let want = want.as_ref(); match to_bytes(&value) { Ok(have) => { assert_eq!( have, want, "Expected `{}` but got `{}` when serializing `{:?}`", String::from_utf8_lossy(&want), String::from_utf8_lossy(&have), value ); }, Err(err) => panic!("Failed to serialize `{:?}`: {}", value, err), } } #[test] fn scalar() { case(false, "i0e"); case(true, "i1e"); case(0u8, "i0e"); case(1u8, "i1e"); case(0u16, "i0e"); case(1u16, "i1e"); case(0u32, "i0e"); case(1u32, "i1e"); case(0u64, "i0e"); case(1u64, "i1e"); case(0u128, "i0e"); case(1u128, "i1e"); case(0usize, "i0e"); case(1usize, "i1e"); case(0i8, "i0e"); case(1i8, "i1e"); case(-1i8, "i-1e"); case(0i16, "i0e"); case(1i16, "i1e"); case(-1i16, "i-1e"); case(0i32, "i0e"); case(1i32, "i1e"); case(-1i32, "i-1e"); case(0i64, "i0e"); case(1i64, "i1e"); case(-1i64, "i-1e"); case(0i128, "i0e"); case(1i128, "i1e"); case(-1i128, "i-1e"); case(0isize, "i0e"); case(1isize, "i1e"); case(-1isize, "i-1e"); } #[test] fn f32() { let value = 100.100f32; let bytes = value.to_bits().to_be_bytes(); let mut bencode: Vec = Vec::new(); bencode.extend(b"4:"); bencode.extend(&bytes); case(value, bencode); } #[test] fn f64() { let value = 100.100f64; let bytes = value.to_bits().to_be_bytes(); let mut bencode: Vec = Vec::new(); bencode.extend(b"8:"); bencode.extend(&bytes); case(value, bencode); } #[test] fn unit() { case((), "le"); } #[test] fn none() { case::, &str>(None, "le"); } #[test] fn some() { case(Some(0), "li0ee"); } #[test] fn char() { case('a', "1:a"); case('\u{1F9D0}', "4:\u{1F9D0}"); } #[test] fn str() { case_borrowed("foo", "3:foo"); } #[test] fn string() { case("foo".to_string(), "3:foo"); } #[test] fn bytes_default() { let value: Vec = vec![1, 2, 3, 4]; case(value, "li1ei2ei3ei4ee"); } #[test] fn bytes_with_serde_bytes() { #[derive(Debug, Serialize, Deserialize, PartialEq)] #[serde(crate = "serde_")] #[serde(transparent)] struct Owned { #[serde(with = "serde_bytes")] bytes: Vec, } case( Owned { bytes: vec![1, 2, 3], }, "3:\x01\x02\x03", ); #[derive(Debug, Serialize, Deserialize, PartialEq)] #[serde(crate = "serde_")] #[serde(transparent)] struct Borrowed<'bytes> { #[serde(with = "serde_bytes")] bytes: &'bytes [u8], } case_borrowed(Borrowed { bytes: &[1, 2, 3] }, b"3:\x01\x02\x03"); } #[test] fn map() { let mut map = HashMap::new(); map.insert("foo".to_owned(), 1); map.insert("bar".to_owned(), 2); case(map, "d3:bari2e3:fooi1ee"); } #[test] fn map_non_byte_key() { let mut map = HashMap::new(); map.insert(1, 1); map.insert(2, 2); assert_matches!(to_bytes(&map), Err(Error::ArbitraryMapKeysUnsupported)); } #[test] fn unit_struct() { #[derive(Debug, Serialize, Deserialize, PartialEq)] #[serde(crate = "serde_")] struct Foo; case(Foo, "le"); } #[test] fn newtype_struct() { #[derive(Debug, Serialize, Deserialize, PartialEq)] #[serde(crate = "serde_")] struct Foo(u8); case(Foo(1), "i1e"); } #[test] fn seq() { case(vec![1, 0, 1], "li1ei0ei1ee"); } #[test] fn tuple_struct() { #[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(crate = "serde_")] struct Foo(String, u32, i32); case(Foo("hello".to_string(), 1, -100), "l5:helloi1ei-100ee"); } #[test] fn record_struct() { #[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(crate = "serde_")] struct Foo { a: u8, b: String, } case( Foo { a: 1, b: "hello".to_string(), }, "d1:ai1e1:b5:helloe", ); } #[test] fn struct_field_order() { // Serde serializes the fields of this struct in the opposite // order to that mandated by bencode. This would trigger an // error if the struct serializer failed to correctly order // the fields during serialization. #[derive(Serialize, Deserialize, Debug, PartialEq, Default)] #[serde(crate = "serde_")] struct Foo { fac: u8, fb: u8, } case(Foo { fac: 0, fb: 1 }, "d3:faci0e2:fbi1ee"); } #[test] fn enum_tests() { #[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(crate = "serde_")] enum Enum { Unit, Newtype(i32), Tuple(bool, i32), Struct { a: char, b: bool }, } case(Enum::Unit, "4:Unit"); case(Enum::Newtype(-1), "d7:Newtypei-1ee"); case(Enum::Tuple(true, 10), "d5:Tupleli1ei10eee"); case(Enum::Struct { a: 'x', b: true }, "d6:Structd1:a1:x1:bi1eee"); } #[test] fn untagged_enum() { #[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(untagged)] #[serde(crate = "serde_")] enum Untagged { Foo { x: i32 }, Bar { y: String }, } case(Untagged::Foo { x: -1 }, "d1:xi-1ee"); case(Untagged::Bar { y: "z".into() }, "d1:y1:ze"); } #[test] fn flatten() { #[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(crate = "serde_")] struct Foo { #[serde(flatten)] bar: Bar, } #[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(crate = "serde_")] struct Bar { x: i32, } case(Foo { bar: Bar { x: 1 } }, "d1:xi1ee"); } #[test] fn invalid_bool() { assert_matches!( from_bytes::(b"i100e"), Err(Error::InvalidBool(ref value)) if value == "100" ); } #[test] fn invalid_f32() { assert_matches!(from_bytes::(b"8:10000000"), Err(Error::InvalidF32(8))); } #[test] fn invalid_f64() { assert_matches!(from_bytes::(b"4:1000"), Err(Error::InvalidF64(4))); } #[test] fn invalid_char() { assert_matches!(from_bytes::(b"2:00"), Err(Error::InvalidChar(2))); } #[test] fn trailing_bytes_forbid() { assert_matches!( Deserializer::from_bytes(b"i1ei1e") .with_forbid_trailing_bytes(true) .deserialize::(), Err(Error::TrailingBytes) ); } #[test] fn trailing_bytes_allow() { assert_matches!( Deserializer::from_bytes(b"i1ei1e").deserialize::(), Ok(1) ); } #[test] fn borrowed_value() { use crate::value::Value; use std::borrow::Cow; #[derive(Debug, Deserialize, PartialEq, Eq)] #[serde(crate = "serde_")] struct Dict<'a> { #[serde(borrow)] v: Value<'a>, } assert_eq!( Deserializer::from_bytes(b"d1:v3:\x01\x02\x03e") .deserialize::>() .unwrap(), Dict { v: Value::Bytes(Cow::Owned(vec![1, 2, 3])) }, ); } } bendy-0.6.1/src/serde/ser/map_serializer.rs000064400000000000000000000037471046102023000170020ustar 00000000000000use crate::serde::common::*; /// Bencode sub-serializer for maps. pub struct MapSerializer<'outer> { pub(crate) outer: &'outer mut Encoder, encoder: UnsortedDictEncoder, key: Option>, } impl<'outer> MapSerializer<'outer> { pub(crate) fn new( outer: &'outer mut Encoder, encoder: UnsortedDictEncoder, ) -> MapSerializer<'outer> { MapSerializer { encoder, outer, key: None, } } fn serialize(&self, value: &T) -> Result> where T: ?Sized + Serialize, { let mut serializer = Serializer::with_max_depth(self.encoder.remaining_depth()); value.serialize(&mut serializer)?; serializer.into_bytes() } } impl<'outer> SerializeMap for MapSerializer<'outer> { type Error = Error; type Ok = (); fn serialize_key(&mut self, key: &T) -> Result<()> where T: ?Sized + Serialize, { if self.key.is_some() { return Err(Error::MapSerializationCallOrder); } let mut encoded = self.serialize(key)?; match encoded.first() { Some(b'0'..=b'9') => {}, _ => return Err(Error::ArbitraryMapKeysUnsupported), } let colon = encoded.iter().position(|b| *b == b':').unwrap(); encoded.drain(0..colon + 1); self.key = Some(encoded); Ok(()) } fn serialize_value(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { match self.key.take() { Some(bytes) => { let encoded = self.serialize(value)?; self.encoder.save_pair(&bytes, encoded)?; Ok(()) }, None => Err(Error::MapSerializationCallOrder), } } fn end(self) -> Result<()> { if self.key.is_some() { return Err(Error::MapSerializationCallOrder); } self.outer.end_unsorted_dict(self.encoder)?; Ok(()) } } bendy-0.6.1/src/serde/ser/mod.rs000064400000000000000000000211521046102023000145410ustar 00000000000000//! Serde bencode serialization. use crate::serde::common::*; pub use map_serializer::MapSerializer; pub use struct_serializer::StructSerializer; mod map_serializer; mod struct_serializer; /// Serialize an instance of `T` to bencode pub fn to_bytes(value: &T) -> Result> where T: ?Sized + Serialize, { let mut serializer = Serializer::new(); value.serialize(&mut serializer)?; serializer.into_bytes() } /// A serde Bencode serializer #[derive(Default)] pub struct Serializer { encoder: Encoder, } impl Serializer { /// Create a new `Serializer` pub fn new() -> Self { ::default() } /// Create a new `Serializer` with a given maximum serialization depth pub fn with_max_depth(max_depth: usize) -> Serializer { Serializer { encoder: Encoder::new().with_max_depth(max_depth), } } /// Consume this `Serializer`, returning the encoded bencode pub fn into_bytes(self) -> Result> { Ok(self.encoder.get_output()?) } fn emit_empty_list(&mut self) -> Result<()> { self.encoder.emit_list(|_| Ok(()))?; Ok(()) } fn begin_struct(&mut self) -> Result> { let encoder = self.encoder.begin_unsorted_dict()?; Ok(StructSerializer::new(&mut self.encoder, encoder)) } fn begin_map(&mut self) -> Result> { let encoder = self.encoder.begin_unsorted_dict()?; Ok(MapSerializer::new(&mut self.encoder, encoder)) } } impl<'a> serde::ser::Serializer for &'a mut Serializer { type Error = Error; type Ok = (); type SerializeMap = MapSerializer<'a>; type SerializeSeq = Self; type SerializeStruct = StructSerializer<'a>; type SerializeStructVariant = StructSerializer<'a>; type SerializeTuple = Self; type SerializeTupleStruct = Self; type SerializeTupleVariant = Self; fn serialize_bool(self, v: bool) -> Result<()> { self.encoder.emit(if v { 1 } else { 0 })?; Ok(()) } fn serialize_i8(self, v: i8) -> Result<()> { self.encoder.emit(v)?; Ok(()) } fn serialize_i16(self, v: i16) -> Result<()> { self.encoder.emit(v)?; Ok(()) } fn serialize_i32(self, v: i32) -> Result<()> { self.encoder.emit(v)?; Ok(()) } fn serialize_i64(self, v: i64) -> Result<()> { self.encoder.emit(v)?; Ok(()) } fn serialize_i128(self, v: i128) -> Result<()> { self.encoder.emit(v)?; Ok(()) } fn serialize_u8(self, v: u8) -> Result<()> { self.encoder.emit(v)?; Ok(()) } fn serialize_u16(self, v: u16) -> Result<()> { self.encoder.emit(v)?; Ok(()) } fn serialize_u32(self, v: u32) -> Result<()> { self.encoder.emit(v)?; Ok(()) } fn serialize_u64(self, v: u64) -> Result<()> { self.encoder.emit(v)?; Ok(()) } fn serialize_u128(self, v: u128) -> Result<()> { self.encoder.emit(v)?; Ok(()) } fn serialize_f32(self, v: f32) -> Result<()> { let bytes = v.to_bits().to_be_bytes(); self.serialize_bytes(&bytes) } fn serialize_f64(self, v: f64) -> Result<()> { let bytes = v.to_bits().to_be_bytes(); self.serialize_bytes(&bytes) } fn serialize_char(self, v: char) -> Result<()> { let mut buffer: [u8; 4] = [0; 4]; self.serialize_str(v.encode_utf8(&mut buffer)) } fn serialize_str(self, v: &str) -> Result<()> { self.serialize_bytes(v.as_bytes()) } fn serialize_bytes(self, v: &[u8]) -> Result<()> { self.encoder.emit_bytes(v)?; Ok(()) } fn serialize_none(self) -> Result<()> { self.emit_empty_list() } fn serialize_some(self, value: &T) -> Result<()> where T: ?Sized + Serialize, { self.encoder.emit_token(Token::List)?; value.serialize(&mut *self)?; self.encoder.emit_token(Token::End)?; Ok(()) } fn serialize_unit(self) -> Result<()> { self.emit_empty_list() } fn serialize_unit_struct(self, _name: &'static str) -> Result<()> { self.emit_empty_list() } fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(self) } fn serialize_seq(self, _len: Option) -> Result { self.encoder.emit_token(Token::List)?; Ok(self) } fn serialize_tuple(self, _len: usize) -> Result { self.encoder.emit_token(Token::List)?; Ok(self) } fn serialize_tuple_struct( self, _name: &'static str, _len: usize, ) -> Result { self.encoder.emit_token(Token::List)?; Ok(self) } fn serialize_map(self, _len: Option) -> Result { self.begin_map() } fn serialize_unit_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, ) -> Result<()> { self.serialize_str(variant) } fn serialize_newtype_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, value: &T, ) -> Result<()> where T: ?Sized + Serialize, { self.encoder.emit_token(Token::Dict)?; self.serialize_str(variant)?; value.serialize(&mut *self)?; self.encoder.emit_token(Token::End)?; Ok(()) } fn serialize_struct(self, _name: &'static str, _len: usize) -> Result { self.begin_struct() } fn serialize_tuple_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, _len: usize, ) -> Result { self.encoder.emit_token(Token::Dict)?; self.serialize_str(variant)?; self.encoder.emit_token(Token::List)?; Ok(self) } fn serialize_struct_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, _len: usize, ) -> Result { self.encoder.emit_token(Token::Dict)?; self.serialize_str(variant)?; self.begin_struct() } } impl<'a> SerializeSeq for &'a mut Serializer { type Error = Error; type Ok = (); fn serialize_element(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(&mut **self) } fn end(self) -> Result<()> { self.encoder.emit_token(Token::End)?; Ok(()) } } impl<'a> SerializeTuple for &'a mut Serializer { type Error = Error; type Ok = (); fn serialize_element(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(&mut **self) } fn end(self) -> Result<()> { self.encoder.emit_token(Token::End)?; Ok(()) } } impl<'a> SerializeTupleStruct for &'a mut Serializer { type Error = Error; type Ok = (); fn serialize_field(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(&mut **self) } fn end(self) -> Result<()> { self.encoder.emit_token(Token::End)?; Ok(()) } } impl<'a> SerializeMap for &'a mut Serializer { type Error = Error; type Ok = (); fn serialize_key(&mut self, _key: &T) -> Result<()> where T: ?Sized + Serialize, { unreachable!() } fn serialize_value(&mut self, _value: &T) -> Result<()> where T: ?Sized + Serialize, { unreachable!() } fn end(self) -> Result<()> { unreachable!() } } impl<'a> SerializeTupleVariant for &'a mut Serializer { type Error = Error; type Ok = (); fn serialize_field(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(&mut **self) } fn end(self) -> Result<()> { self.encoder.emit_token(Token::End)?; self.encoder.emit_token(Token::End)?; Ok(()) } } impl<'a> SerializeStructVariant for &'a mut Serializer { type Error = Error; type Ok = (); fn serialize_field(&mut self, _key: &'static str, _value: &T) -> Result<()> where T: ?Sized + Serialize, { unreachable!() } fn end(self) -> Result<()> { self.encoder.emit_token(Token::End)?; self.encoder.emit_token(Token::End)?; Ok(()) } } bendy-0.6.1/src/serde/ser/struct_serializer.rs000064400000000000000000000031311046102023000175340ustar 00000000000000use crate::serde::common::*; /// Bencode sub-serializer for structs. pub struct StructSerializer<'outer> { pub(crate) outer: &'outer mut Encoder, encoder: UnsortedDictEncoder, } impl<'outer> StructSerializer<'outer> { pub(crate) fn new( outer: &'outer mut Encoder, encoder: UnsortedDictEncoder, ) -> StructSerializer<'outer> { StructSerializer { outer, encoder } } fn save_field(&mut self, key: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize, { let mut serializer = Serializer::with_max_depth(self.encoder.remaining_depth()); value.serialize(&mut serializer)?; let value_bytes = serializer.into_bytes()?; self.encoder.save_pair(key.as_bytes(), value_bytes)?; Ok(()) } } impl<'outer> SerializeStruct for StructSerializer<'outer> { type Error = Error; type Ok = (); fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize, { self.save_field(key, value) } fn end(self) -> Result<()> { self.outer.end_unsorted_dict(self.encoder)?; Ok(()) } } impl<'outer> SerializeStructVariant for StructSerializer<'outer> { type Error = Error; type Ok = (); fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize, { self.save_field(key, value) } fn end(self) -> Result<()> { self.outer.end_unsorted_dict(self.encoder)?; self.outer.emit_token(Token::End)?; Ok(()) } } bendy-0.6.1/src/state_tracker/mod.rs000064400000000000000000000003031046102023000154740ustar 00000000000000//! State tracking for decoding and encoding mod state; mod structure_error; mod token; pub use self::token::Token; pub(crate) use self::{state::StateTracker, structure_error::StructureError}; bendy-0.6.1/src/state_tracker/state.rs000064400000000000000000000112271046102023000160440ustar 00000000000000#[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::mem; use crate::state_tracker::{StructureError, Token}; /// The state of current level of the decoder #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Debug)] enum State, E> { /// An inner list. Allows any token Seq, /// Inside a map, expecting a key. Contains the last key read, so sorting can be validated MapKey(Option), /// Inside a map, expecting a value. Contains the last key read, so sorting can be validated MapValue(S), /// Received an error while decoding Failed(E), } /// Used to validate that a structure is valid #[derive(Debug)] pub struct StateTracker, E = StructureError> { state: Vec>, max_depth: usize, } impl, E> Default for StateTracker { fn default() -> Self { StateTracker { state: Vec::with_capacity(2048), max_depth: 2048, } } } impl, E> StateTracker where S: AsRef<[u8]>, E: From + Clone, { pub fn new() -> Self { ::default() } pub fn set_max_depth(&mut self, new_max_depth: usize) { self.max_depth = new_max_depth } pub fn remaining_depth(&self) -> usize { self.max_depth - self.state.len() } /// Observe that an EOF was seen. This function is idempotent. pub fn observe_eof(&mut self) -> Result<(), E> { self.check_error()?; if self.state.is_empty() { Ok(()) } else { self.latch_err(Err(E::from(StructureError::UnexpectedEof))) } } #[allow(clippy::match_same_arms)] pub fn observe_token<'a>(&mut self, token: &Token<'a>) -> Result<(), E> where S: From<&'a [u8]>, { use self::{State::*, Token::*}; match (self.state.last_mut(), *token) { (None, End) => { return self.latch_err(Err(E::from(StructureError::invalid_state( "End not allowed at top level", )))); }, (Some(Seq), End) | (Some(MapKey(_)), End) => { self.state.pop(); }, (Some(MapKey(Some(oldlabel))), String(label)) if oldlabel.as_ref() >= label => { self.state.pop(); return self.latch_err(Err(E::from(StructureError::UnsortedKeys))); }, (Some(MapKey(Some(_oldlabel))), String(label)) => { *self.state.last_mut().unwrap() = MapValue(S::from(label)); }, (Some(MapKey(None)), String(label)) => { *self.state.last_mut().unwrap() = MapValue(S::from(label)); }, (Some(_oldstate @ MapKey(_)), _tok) => { return self.latch_err(Err(E::from(StructureError::invalid_state( "Map keys must be strings", )))); }, (Some(MapValue(label)), List) | (Some(MapValue(label)), Dict) => { let dummy: &[u8] = &[]; *self.state.last_mut().unwrap() = MapKey(Some(mem::replace(label, dummy.into()))); if self.state.len() >= self.max_depth { return self.latch_err(Err(E::from(StructureError::NestingTooDeep))); } self.state .push(if token == &List { Seq } else { MapKey(None) }); }, (Some(_oldstate @ MapValue(_)), End) => { self.state.pop(); return self.latch_err(Err(E::from(StructureError::invalid_state( "Missing map value", )))); }, (Some(MapValue(label)), _) => { let dummy: &[u8] = &[]; *self.state.last_mut().unwrap() = MapKey(Some(mem::replace(label, dummy.into()))); }, (_oldstate, List) | (_oldstate, Dict) => { if self.state.len() >= self.max_depth { return self.latch_err(Err(E::from(StructureError::NestingTooDeep))); } self.state .push(if token == &List { Seq } else { MapKey(None) }); }, _ => {}, } Ok(()) } pub fn latch_err(&mut self, result: Result) -> Result { self.check_error()?; if let Err(ref err) = result { self.state.push(State::Failed(err.clone())) } result } pub fn check_error(&self) -> Result<(), E> { if let Some(State::Failed(error)) = self.state.last() { Err(error.clone()) } else { Ok(()) } } } bendy-0.6.1/src/state_tracker/structure_error.rs000064400000000000000000000024471046102023000202010ustar 00000000000000#[cfg(not(feature = "std"))] use alloc::{ format, string::{String, ToString}, }; #[cfg(not(feature = "std"))] use core::fmt::Display; #[cfg(feature = "std")] use std::fmt::Display; use thiserror::Error; /// An encoding or decoding error #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Error)] pub enum StructureError { /// Wrong type of token detected. #[error("Saw the wrong type of token: {state}")] InvalidState { state: String }, /// Keys were not sorted. #[error("Keys were not sorted")] UnsortedKeys, /// EOF reached to early. #[error("Reached EOF in the middle of a message")] UnexpectedEof, /// Unexpected characters detected. #[error("Malformed number of unexpected character: {unexpected}")] SyntaxError { unexpected: String }, /// Exceeded the recursion limit. #[error("Maximum nesting depth exceeded")] NestingTooDeep, } impl StructureError { pub fn unexpected(expected: impl Display, got: char, offset: usize) -> Self { StructureError::SyntaxError { unexpected: format!("Expected {expected}, got {got:?} at offset {offset}"), } } pub fn invalid_state(expected: impl Display) -> Self { StructureError::InvalidState { state: expected.to_string(), } } } bendy-0.6.1/src/state_tracker/token.rs000064400000000000000000000013101046102023000160340ustar 00000000000000/// A raw bencode token #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] pub enum Token<'a> { /// The beginning of a list List, /// The beginning of a dictionary Dict, /// A byte string; may not be UTF-8 String(&'a [u8]), /// A number; we explicitly *don't* parse it here, as it could be signed, unsigned, or a bignum Num(&'a str), /// The end of a list or dictionary End, } impl<'a> Token<'a> { pub fn name(&self) -> &'static str { match *self { Token::Dict => "Dict", Token::End => "End", Token::List => "List", Token::Num(_) => "Num", Token::String(_) => "String", } } } bendy-0.6.1/src/value.rs000064400000000000000000000255271046102023000132150ustar 00000000000000//! `Value`s hold arbitrary borrowed or owneed bencode data. Unlike `Objects`, //! they can be cloned and traversed multiple times. //! //! `Value` implements `FromBencode`, `ToBencode`. If the `serde` feature is //! enabled, it also implements `Serialize` and `Deserialize`. use alloc::{ borrow::{Cow, ToOwned}, collections::BTreeMap, vec::Vec, }; #[cfg(feature = "serde")] use std::{ convert::TryInto, fmt::{self, Formatter}, marker::PhantomData, }; #[cfg(feature = "serde")] use serde_ as serde; #[cfg(feature = "serde")] use serde::{ Serialize, ser::{SerializeMap, SerializeSeq}, }; use crate::{ decoding::{FromBencode, Object}, encoding::{SingleItemEncoder, ToBencode}, }; /// An owned or borrowed bencoded value. #[derive(PartialEq, Eq, Clone, Debug)] pub enum Value<'a> { /// An owned or borrowed byte string Bytes(Cow<'a, [u8]>), /// A dictionary mapping byte strings to values Dict(BTreeMap, Value<'a>>), /// A signed integer Integer(i64), /// A list of values List(Vec>), } impl<'a> Value<'a> { /// Convert this Value into an owned Value with static lifetime pub fn into_owned(self) -> Value<'static> { match self { Value::Bytes(bytes) => Value::Bytes(Cow::Owned(bytes.into_owned())), Value::Dict(dict) => Value::Dict( dict.into_iter() .map(|(key, value)| (Cow::Owned(key.into_owned()), value.into_owned())) .collect(), ), Value::Integer(integer) => Value::Integer(integer), Value::List(list) => Value::List(list.into_iter().map(Value::into_owned).collect()), } } } impl<'a> ToBencode for Value<'a> { /// This is set to zero to indicate a statically unknown depth. /// See the [`encoding`][crate::encoding#dynamic-depth] module docs. const MAX_DEPTH: usize = 0; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), crate::encoding::Error> { match self { Value::Bytes(bytes) => encoder.emit_bytes(bytes), Value::Dict(dict) => dict.encode(encoder), Value::Integer(integer) => integer.encode(encoder), Value::List(list) => list.encode(encoder), } } } impl<'a> FromBencode for Value<'a> { /// This is set to zero to indicate a statically unknown depth. /// See the [`encoding`][crate::encoding#dynamic-depth] module docs. const EXPECTED_RECURSION_DEPTH: usize = ::MAX_DEPTH; fn decode_bencode_object(object: Object) -> Result { match object { Object::Bytes(bytes) => Ok(Value::Bytes(Cow::Owned(bytes.to_owned()))), Object::Dict(mut decoder) => { let mut dict = BTreeMap::new(); while let Some((key, value)) = decoder.next_pair()? { dict.insert( Cow::Owned(key.to_owned()), Value::decode_bencode_object(value)?, ); } Ok(Value::Dict(dict)) }, Object::Integer(text) => Ok(Value::Integer(text.parse()?)), Object::List(mut decoder) => { let mut list = Vec::new(); while let Some(object) = decoder.next_object()? { list.push(Value::decode_bencode_object(object)?); } Ok(Value::List(list)) }, } } } #[cfg(feature = "serde")] mod serde_impls { use super::*; use serde_bytes::Bytes; impl<'a> Serialize for Value<'a> { fn serialize(&self, serializer: S) -> Result where S: serde::ser::Serializer, { match self { Value::Bytes(string) => serializer.serialize_bytes(string), Value::Integer(int) => serializer.serialize_i64(*int), Value::List(list) => { let mut seed = serializer.serialize_seq(Some(list.len()))?; for value in list { seed.serialize_element(value)?; } seed.end() }, Value::Dict(dict) => { let mut seed = serializer.serialize_map(Some(dict.len()))?; for (k, v) in dict { let bytes = Bytes::new(k); seed.serialize_entry(bytes, v)?; } seed.end() }, } } } impl<'de: 'a, 'a> serde::de::Deserialize<'de> for Value<'a> { #[inline] fn deserialize(deserializer: D) -> Result, D::Error> where D: serde::de::Deserializer<'de>, { deserializer.deserialize_any(Visitor(PhantomData)) } } struct Visitor<'a>(PhantomData<&'a ()>); impl<'de: 'a, 'a> serde::de::Visitor<'de> for Visitor<'a> { type Value = Value<'a>; fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { formatter.write_str("any valid BEncode value") } fn visit_i64(self, value: i64) -> Result, E> { Ok(Value::Integer(value)) } fn visit_u64(self, value: u64) -> Result, E> { Ok(Value::Integer(value.try_into().unwrap())) } fn visit_borrowed_bytes(self, value: &'de [u8]) -> Result, E> where E: serde::de::Error, { Ok(Value::Bytes(Cow::Borrowed(value))) } fn visit_borrowed_str(self, value: &'de str) -> Result, E> where E: serde::de::Error, { Ok(Value::Bytes(Cow::Borrowed(value.as_bytes()))) } fn visit_str(self, value: &str) -> Result, E> where E: serde::de::Error, { Ok(Value::Bytes(Cow::Owned(value.as_bytes().to_vec()))) } fn visit_string(self, value: String) -> Result, E> { Ok(Value::Bytes(Cow::Owned(value.into_bytes()))) } fn visit_byte_buf(self, value: Vec) -> Result, E> { Ok(Value::Bytes(Cow::Owned(value))) } fn visit_seq(self, mut access: V) -> Result, V::Error> where V: serde::de::SeqAccess<'de>, { let mut list = Vec::new(); while let Some(e) = access.next_element()? { list.push(e); } Ok(Value::List(list)) } fn visit_map(self, mut access: V) -> Result, V::Error> where V: serde::de::MapAccess<'de>, { let mut map = BTreeMap::new(); while let Some((k, v)) = access.next_entry::<&Bytes, _>()? { map.insert(Cow::Borrowed(k.as_ref()), v); } Ok(Value::Dict(map)) } } } #[cfg(test)] mod tests { use crate::{decoding::Decoder, encoding::Encoder}; use super::*; use alloc::{string::String, vec}; /// Round-trip and verify encoding. Uses depth 0. fn case(value: Value, expected: impl AsRef<[u8]>) { case_impl(value, 0, expected); } fn case_impl(value: Value, depth: usize, expected: impl AsRef<[u8]>) { let expected = expected.as_ref(); let mut encoder = Encoder::new().with_max_depth(depth); match encoder.emit(&value) { Err(err) => panic!("Failed to encode `{:?}`: {}", value, err), Ok(()) => (), }; let encoded = match encoder.get_output() { Ok(bytes) => bytes, Err(err) => panic!("Failed to encode `{:?}`: {}", value, err), }; if encoded != expected { panic!( "Expected `{:?}` to encode as `{}`, but got `{}", value, String::from_utf8_lossy(expected), String::from_utf8_lossy(&encoded) ) } let mut decoder = Decoder::new(&encoded).with_max_depth(depth); let object = match decoder.next_object() { Ok(Some(obj)) => obj, Ok(None) => panic!( "Failed to decode value from `{}`: Unexpected EOF.", String::from_utf8_lossy(&encoded), ), Err(err) => panic!( "Failed to decode value from `{}`: {}", String::from_utf8_lossy(&encoded), err, ), }; let decoded = match Value::decode_bencode_object(object) { Ok(decoded) => decoded, Err(err) => panic!( "Failed to decode value from `{}`: {}", String::from_utf8_lossy(&encoded), err, ), }; assert_eq!(decoded, value); #[cfg(feature = "serde")] { let deserialized = match crate::serde::de::from_bytes::(expected) { Ok(deserialized) => deserialized, Err(err) => panic!( "Failed to deserialize value from `{}`: {}", String::from_utf8_lossy(&expected), err ), }; if deserialized != value { panic!( "Deserialize Serialize produced unexpected value: `{:?}` != `{:?}`", deserialized, value ); } let serialized = match crate::serde::ser::to_bytes(&value) { Ok(serialized) => serialized, Err(err) => panic!("Failed to serialize `{:?}`: {}", value, err), }; if serialized != expected { panic!( "Serialize Serialize produced unexpected bencode: `{:?}` != `{:?}`", String::from_utf8_lossy(&serialized), String::from_utf8_lossy(expected) ); } } } #[test] fn bytes() { case(Value::Bytes(Cow::Borrowed(&[1, 2, 3])), b"3:\x01\x02\x03"); case(Value::Bytes(Cow::Owned(vec![1, 2, 3])), b"3:\x01\x02\x03"); } #[test] fn dict() { case_impl(Value::Dict(BTreeMap::new()), 1, "de"); let mut dict = BTreeMap::new(); dict.insert(Cow::Borrowed("foo".as_bytes()), Value::Integer(1)); dict.insert(Cow::Borrowed("bar".as_bytes()), Value::Integer(2)); case_impl(Value::Dict(dict), 1, "d3:bari2e3:fooi1ee"); } #[test] fn integer() { case(Value::Integer(0), "i0e"); case(Value::Integer(-1), "i-1e"); } #[test] fn list() { case_impl(Value::List(Vec::new()), 1, "le"); case_impl( Value::List(vec![ Value::Integer(0), Value::Bytes(Cow::Borrowed(&[1, 2, 3])), ]), 1, b"li0e3:\x01\x02\x03e", ); } } bendy-0.6.1/tests/core_test.rs000064400000000000000000000306371046102023000144410ustar 00000000000000//! Port of https://github.com/jamesleonis/bencode-cljc/blob/master/test/bencode_cljc/core_test.cljc //! //! Should only use #![no_std] compatible features but still requires the //! `std` feature flag to avoid that we need to define a global allocator. extern crate alloc; use alloc::collections::BTreeMap; use bendy::{ decoding::{Error as DecodingError, FromBencode, Object}, encoding::{Error as EncodingError, SingleItemEncoder, ToBencode}, }; // ----------------------------------------------------------------------------- // Macros // ----------------------------------------------------------------------------- macro_rules! list( {} => { Vec::::new() }; { $($value:expr),+ } => { { let mut list = Vec::new(); $( list.push(Something::from($value)); )+ list } }; ); macro_rules! map( { $($key:expr => $value:expr),+ } => { { let mut map = BTreeMap::new(); $( map.insert($key.to_owned(), Something::from($value)); )+ map } }; ); // ----------------------------------------------------------------------------- // Tests // ----------------------------------------------------------------------------- #[test] fn string_test_pairs() -> Result<(), Error> { let pairs = [ ("", "0:"), ("hello", "5:hello"), ("goodbye", "7:goodbye"), ("hello world", "11:hello world"), ("1-5%3~]+=\\| []>.,`??", "20:1-5%3~]+=\\| []>.,`??"), ]; for (original, expected_encoding) in &pairs { let encoded = original.to_bencode()?; assert_eq!(expected_encoding.as_bytes(), encoded.as_slice()); let decoded = String::from_bencode(&encoded)?; assert_eq!(original, &decoded); } Ok(()) } #[test] fn integer_test_pairs() -> Result<(), Error> { let pairs = [ (0, "i0e"), (5, "i5e"), (-5, "i-5e"), (005, "i5e"), (-005, "i-5e"), (1234567890, "i1234567890e"), (-1234567890, "i-1234567890e"), (i64::max_value(), "i9223372036854775807e"), (i64::min_value(), "i-9223372036854775808e"), ]; // Bendy currently doesn't contain a big number implementation.. // // ( // 123456789012345678901234567890123456789012345678901234567890, // "i123456789012345678901234567890123456789012345678901234567890e" // ), // ( // -123456789012345678901234567890123456789012345678901234567890, // "i-123456789012345678901234567890123456789012345678901234567890e" // ) for (original, expected_encoding) in &pairs { let encoded = original.to_bencode()?; assert_eq!(expected_encoding.as_bytes(), encoded.as_slice()); let decoded = i64::from_bencode(&encoded)?; assert_eq!(original, &decoded); } Ok(()) } #[test] fn list_test_pairs() -> Result<(), Error> { let pairs = [ (list![], "le"), (list!["abra", "cadabra"], "l4:abra7:cadabrae"), (list!["spam", "eggs"], "l4:spam4:eggse"), ( list![vec!["list", "of", "lists"], vec!["like", "omygawd!"]], "ll4:list2:of5:listsel4:like8:omygawd!ee", ), ]; for (original, expected_encoding) in &pairs { let encoded = original.to_bencode()?; assert_eq!(expected_encoding.as_bytes(), encoded.as_slice()); let decoded = Vec::::from_bencode(&encoded)?; assert_eq!(original, &decoded); } Ok(()) } #[test] fn map_test_pairs() -> Result<(), Error> { let pairs = [ (BTreeMap::new(), "de"), ( map! {"cow" => "moo", "spam" => "eggs"}, "d3:cow3:moo4:spam4:eggse", ), ( map! {"cow" => "moo", "dog" => "bark"}, "d3:cow3:moo3:dog4:barke", ), ( map! {"dog" => "bark", "cow" => "moo"}, "d3:cow3:moo3:dog4:barke", ), ( map! {"first" => "first", "2ace" => "second", "3ace" => "third"}, "d4:2ace6:second4:3ace5:third5:first5:firste", ), ( map! {"Goodbye" => map! {"maps" => "that don't work", "number" => 100}}, "d7:Goodbyed4:maps15:that don't work6:numberi100eee", ), ( map! { "publisher" => "bob", "publisher-webpage" => "www.example.com", "publisher.location" => "home" }, "d9:publisher3:bob17:publisher-webpage15:www.example.com18:publisher.location4:homee", ), ]; for (original, expected_encoding) in &pairs { let encoded = original.to_bencode()?; assert_eq!(expected_encoding.as_bytes(), encoded.as_slice()); let decoded = BTreeMap::::from_bencode(&encoded)?; assert_eq!(original, &decoded); } Ok(()) } #[test] fn mixed_use_list_pairs() -> Result<(), Error> { let pairs = [( list![0, "heterogeneous", -5, "lists", 10, map! {"map" => "well"}], "li0e13:heterogeneousi-5e5:listsi10ed3:map4:wellee", )]; for (original, expected_encoding) in &pairs { let encoded = original.to_bencode()?; assert_eq!(expected_encoding.as_bytes(), encoded.as_slice()); let decoded = Vec::::from_bencode(&encoded)?; assert_eq!(original, &decoded); } Ok(()) } #[test] fn mixed_use_dict_pairs() -> Result<(), Error> { let pairs = [ ( map! { "hello" => list!["world!", "gaia!", "mother earth!"], "Goodbye" => map! {"maps" => "that don't work", "number" => 100} }, "d7:Goodbyed4:maps15:that don't work6:numberi100ee5:hellol6:world!5:gaia!13:mother earth!ee", ), ( map! {"hello" => list!["world!", "gaia!", "mother earth!"]}, "d5:hellol6:world!5:gaia!13:mother earth!ee", ), (map! {"spam" => list!["a", "b"]}, "d4:spaml1:a1:bee"), ( map! { "t" => "aa", "y" => "q", "q" => "ping", "a" => map! { "id" => "abcdefghij0123456789" } }, "d1:ad2:id20:abcdefghij0123456789e1:q4:ping1:t2:aa1:y1:qe", ), ( map! { "t" => "aa", "y" => "q", "q" => "find_node", "a" => map! { "id" => "abcdefghij0123456789", "target" => "mnopqrstuvwxyz123456" } }, "d1:ad2:id20:abcdefghij01234567896:target20:mnopqrstuvwxyz123456e1:q9:find_node1:t2:aa1:y1:qe", ), ( map! { "t" => "aa", "y" => "q", "q" => "get_peers", "a" => map! { "id" => "abcdefghij0123456789", "info_hash" => "mnopqrstuvwxyz123456" } }, "d1:ad2:id20:abcdefghij01234567899:info_hash20:mnopqrstuvwxyz123456e1:q9:get_peers1:t2:aa1:y1:qe", ), ( map! { "t" => "aa", "y" => "r", "r" => map! { "id" => "abcdefghij0123456789", "token" => "aoeusnth", "values" => vec!["axje.u", "idhtnm"] } }, "d1:rd2:id20:abcdefghij01234567895:token8:aoeusnth6:valuesl6:axje.u6:idhtnmee1:t2:aa1:y1:re", ), ]; for (original, expected_encoding) in &pairs { let encoded = original.to_bencode()?; assert_eq!(expected_encoding.as_bytes(), encoded.as_slice()); let decoded = BTreeMap::::from_bencode(&encoded)?; assert_eq!(original, &decoded); } Ok(()) } #[test] fn illegal_integer_encodings() { let values = [ "i-0e", "i09e", "i-09e", "i-0123e", "i-00123e", "i0123e", "i00123e", "i12-345", "i-12-345", "i-1", "i1", ]; // Bendy currently doesn't fail if it encounters unused tokens // // "i12345ei10e5:eoeoee", // "i-12345ei10e5:eoeoee" for value in &values { let error = i64::from_bencode(value.as_bytes()).unwrap_err(); assert!(error.to_string().contains("encoding corrupted")); } } #[test] fn illegal_string_encodings() { let values = [":hello", "-5:hello", "-5:", "5:", "10:hello"]; // Bendy currently doesn't fail if it encounters unused tokens // // "5:hello5:hello", // "5:helloi10e", // 10:hello5:hello", // "10:helloi0e", // "10:helloi123456789e" for value in &values { let error = String::from_bencode(value.as_bytes()).unwrap_err(); assert!(error.to_string().contains("encoding corrupted")); } } #[test] fn illegal_list_encodings() { let values = [ "l", "lsde", "li10e5hello", "l10:helloi123456789ee", "l10:helloi123456789e5:helloe", "l5:helloi123456789e10:helloe", ]; // Bendy currently doesn't fail if it encounters unused tokens // // "l5:hello5:worldei10e", for value in &values { let error = Vec::::from_bencode(value.as_bytes()).unwrap_err(); assert!(error.to_string().contains("encoding corrupted")); } } #[test] fn illegal_dictionary_encodings() { let values = [ "d", "duuuuure", "d5:hello5:world", "d10:helloi123456789ee", "d5:helloi123456789e5:helloe", "di10e5:hello5:worldi10ee", "d5:worldi10ei10e5:helloe", "dle5:hello5:worldi10ee", "dli10ei11ee5:hello5:worldi10ee", "dde5:hello5:worldi10ee", "dd8:innermapi11ee5:hello5:worldi10ee", ]; // Bendy currently doesn't fail if it encounters unused tokens // // "d5:hello5:worldei10e", for value in &values { let error = BTreeMap::::from_bencode(value.as_bytes()).unwrap_err(); assert!(error.to_string().contains("encoding corrupted")); } } // ----------------------------------------------------------------------------- // Dynamic Typing Utility // ----------------------------------------------------------------------------- #[derive(Debug, PartialEq)] enum Something { Bytes(String), Dict(BTreeMap), Integer(i64), List(Vec), } impl From<&str> for Something { fn from(content: &str) -> Self { Something::Bytes(content.to_owned()) } } impl From> for Something where Something: From, { fn from(content: BTreeMap) -> Self { let content = content .into_iter() .map(|(key, value)| (key, value.into())) .collect(); Something::Dict(content) } } impl From for Something { fn from(content: i64) -> Self { Something::Integer(content) } } impl From> for Something where Something: From, { fn from(content: Vec) -> Self { let content = content.into_iter().map(Into::into).collect(); Something::List(content) } } impl FromBencode for Something { fn decode_bencode_object(object: Object) -> Result where Self: Sized, { let something = match object { Object::Bytes(content) => { Something::Bytes(String::from_utf8_lossy(content).to_string()) }, Object::Integer(number) => Something::Integer(number.parse().unwrap()), object @ Object::Dict(_) => { Something::Dict(BTreeMap::decode_bencode_object(object).unwrap()) }, object @ Object::List(_) => { Something::List(Vec::decode_bencode_object(object).unwrap()) }, }; Ok(something) } } impl ToBencode for Something { const MAX_DEPTH: usize = 999; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), EncodingError> { match self { Something::Bytes(content) => encoder.emit(content), Something::Dict(content) => encoder.emit(content), Something::Integer(content) => encoder.emit(content), Something::List(content) => encoder.emit(content), } } } // ----------------------------------------------------------------------------- // Error // ----------------------------------------------------------------------------- #[allow(dead_code)] #[derive(Debug)] enum Error { DecodingError(DecodingError), EncodingError(EncodingError), } impl From for Error { fn from(error: DecodingError) -> Self { Error::DecodingError(error) } } impl From for Error { fn from(error: EncodingError) -> Self { Error::EncodingError(error) } } bendy-0.6.1/tests/performance_test.rs000064400000000000000000000056161046102023000160110ustar 00000000000000use bendy::{ decoding::{Error as DecodingError, FromBencode, Object}, encoding::{Error as EncodingError, SingleItemEncoder, ToBencode}, }; struct PerformanceTestSubject where T: FromBencode, { list: Vec>, } impl PerformanceTestSubject where T: ToBencode + FromBencode, { fn serialize(&self) -> Vec { self.to_bencode().unwrap() } fn deserialize(bytes: Vec) -> Self { PerformanceTestSubject::::from_bencode(&bytes).unwrap() } } impl ToBencode for PerformanceTestSubject where T: ToBencode + FromBencode, { const MAX_DEPTH: usize = 2; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), EncodingError> { encoder.emit(&self.list) // encoder.emit_list(|e| { // e.emit_int(self.list.len())?; // for vec_elem in &self.list { // for item in vec_elem { // e.emit(item)?; // } // } // Ok(()) // }) } } impl FromBencode for PerformanceTestSubject where T: FromBencode, { const EXPECTED_RECURSION_DEPTH: usize = 2; fn decode_bencode_object(object: Object) -> Result where Self: Sized, { Ok(PerformanceTestSubject { list: Vec::>::decode_bencode_object(object)?, }) // match object { // Object::List(mut encoded_list) => { // let list_length = match encoded_list.next_object()?.unwrap() { // Object::Integer(i) => Ok(i.parse().unwrap()), // _ => Err(bendy::decoding::Error::unexpected_token( // "Integer, size of all_nodes Vec", // "Something else", // )), // }?; // let mut list = Vec::with_capacity(list_length); // for _ in 0..list_length { // list.push(Vec::new()); // for _ in 0..list_length { // list.last_mut().unwrap().push( // T::decode_bencode_object(encoded_list.next_object()?.unwrap()).unwrap(), // ); // } // } // Ok(PerformanceTestSubject { list }) // }, // _ => Err(bendy::decoding::Error::unexpected_field("List")), // } } } #[macro_use] extern crate timeit; #[test] fn this_should_take_long() { const LIST_SIZE: usize = 1000; let test_subject = PerformanceTestSubject:: { list: vec![vec![69; LIST_SIZE]; LIST_SIZE], }; timeit!({ test_subject.serialize(); }); let serialized = test_subject.serialize(); timeit!({ PerformanceTestSubject::::deserialize(serialized.clone()); }); // let _deserialized = PerformanceTestSubject::::deserialize(serialized); } bendy-0.6.1/tests/struct_codec.rs000064400000000000000000000043521046102023000151260ustar 00000000000000use bendy::{ decoding::{Error as DecodingError, FromBencode, Object}, encoding::{Error as EncodingError, SingleItemEncoder, ToBencode}, }; #[derive(PartialEq, Eq, Debug)] struct Example { foo: Vec, bar: i64, } impl ToBencode for Example { const MAX_DEPTH: usize = 2; fn encode(&self, encoder: SingleItemEncoder) -> Result<(), EncodingError> { encoder.emit_dict(|mut dict| { dict.emit_pair(b"bar", &self.bar)?; dict.emit_pair(b"foo", &self.foo) }) } } impl FromBencode for Example { const EXPECTED_RECURSION_DEPTH: usize = 2; fn decode_bencode_object(object: Object) -> Result where Self: Sized, { let mut foo = None; let mut bar = None; let mut dict = object.try_into_dictionary()?; while let Some((key, value)) = dict.next_pair()? { match key { b"foo" => { foo = Vec::decode_bencode_object(value).map(Some)?; }, b"bar" => { bar = i64::decode_bencode_object(value).map(Some)?; }, _ => (), // ignore unknown keys } } Ok(Example { foo: foo.ok_or_else(|| DecodingError::missing_field("foo"))?, bar: bar.ok_or_else(|| DecodingError::missing_field("bar"))?, }) } } #[test] fn should_encode_struct() { let example = Example { foo: vec![2, 3], bar: 1, }; let encoded = example.to_bencode().expect("example encoding is broken"); assert_eq!(encoded, b"d3:bari1e3:fooli2ei3eee".to_vec(),) } #[test] fn should_decode_struct() { let encoded = b"d3:bari1e3:fooli2ei3eee".to_vec(); let example = Example::from_bencode(&encoded).expect("example decoding is broken"); assert_eq!( example, Example { foo: vec![2, 3], bar: 1, } ) } #[test] fn validate_round_trip() { let example = Example { foo: vec![2, 3], bar: 1, }; let encoded = example.to_bencode().expect("example encoding is broken"); let decoded = Example::from_bencode(&encoded).expect("example decoding is broken"); assert_eq!(example, decoded); }