matrix-pickle-0.2.2/.cargo_vcs_info.json0000644000000001620000000000100136050ustar { "git": { "sha1": "1eb91a7e6fcb4915d2cf2f0ed97f8e3fd074f2c7" }, "path_in_vcs": "crates/matrix-pickle" }matrix-pickle-0.2.2/Cargo.lock0000644000000303000000000000100115550ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "anyhow" version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bit-set" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", "windows-sys", ] [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "getrandom" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", "wasip2", ] [[package]] name = "hashbrown" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" [[package]] name = "indexmap" version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "libc" version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "linux-raw-sys" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "matrix-pickle" version = "0.2.2" dependencies = [ "anyhow", "matrix-pickle-derive", "proptest", "thiserror", ] [[package]] name = "matrix-pickle-derive" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a962fc9981f823f6555416dcb2ae9ae67ca412d767ee21ecab5150113ee6285b" dependencies = [ "proc-macro-crate", "proc-macro-error2", "proc-macro2", "quote", "syn", ] [[package]] name = "memchr" version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "ppv-lite86" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ "zerocopy", ] [[package]] name = "proc-macro-crate" version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro-error-attr2" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" dependencies = [ "proc-macro2", "quote", ] [[package]] name = "proc-macro-error2" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", ] [[package]] name = "proc-macro2" version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" dependencies = [ "bit-set", "bit-vec", "bitflags", "num-traits", "rand", "rand_chacha", "rand_xorshift", "regex-syntax", "rusty-fork", "tempfile", "unarray", ] [[package]] name = "quick-error" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ "getrandom", ] [[package]] name = "rand_xorshift" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" dependencies = [ "rand_core", ] [[package]] name = "regex-syntax" version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "rustix" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys", ] [[package]] name = "rusty-fork" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" dependencies = [ "fnv", "quick-error", "tempfile", "wait-timeout", ] [[package]] name = "serde_core" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "syn" version = "2.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tempfile" version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ "fastrand", "getrandom", "once_cell", "rustix", "windows-sys", ] [[package]] name = "thiserror" version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "toml_datetime" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" dependencies = [ "indexmap", "toml_datetime", "toml_parser", "winnow", ] [[package]] name = "toml_parser" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" dependencies = [ "winnow", ] [[package]] name = "unarray" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "wait-timeout" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" dependencies = [ "libc", ] [[package]] name = "wasip2" version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ "wit-bindgen", ] [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-sys" version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link", ] [[package]] name = "winnow" version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] [[package]] name = "wit-bindgen" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "zerocopy" version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", "syn", ] matrix-pickle-0.2.2/Cargo.toml0000644000000022220000000000100116020ustar # 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 = "2021" rust-version = "1.65" name = "matrix-pickle" version = "0.2.2" build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "A simple binary encoding format used in the Matrix world" readme = "README.md" license = "Apache-2.0" repository = "https://github.com/matrix-org/matrix-pickle" [features] default = ["derive"] derive = ["dep:matrix-pickle-derive"] [lib] name = "matrix_pickle" path = "src/lib.rs" [dependencies.matrix-pickle-derive] version = "0.2.2" optional = true [dependencies.thiserror] version = "2.0.17" [dev-dependencies.anyhow] version = "1.0.100" [dev-dependencies.proptest] version = "1.9.0" matrix-pickle-0.2.2/Cargo.toml.orig000064400000000000000000000010101046102023000152550ustar 00000000000000[package] name = "matrix-pickle" version = "0.2.2" edition = "2021" description = "A simple binary encoding format used in the Matrix world" repository = "https://github.com/matrix-org/matrix-pickle" license = "Apache-2.0" rust-version = { workspace = true } [features] derive = ["dep:matrix-pickle-derive"] default = ["derive"] [dependencies] thiserror = "2.0.17" matrix-pickle-derive = { version = "0.2.2", path = "../matrix-pickle-derive", optional = true} [dev-dependencies] anyhow = "1.0.100" proptest = "1.9.0" matrix-pickle-0.2.2/README.md000064400000000000000000000131121046102023000136530ustar 00000000000000![Build Status](https://img.shields.io/github/actions/workflow/status/matrix-org/matrix-pickle/ci.yml?branch=main&style=flat-square) [![codecov](https://img.shields.io/codecov/c/github/matrix-org/matrix-pickle/main.svg?style=flat-square)](https://codecov.io/gh/matrix-org/matrix-pickle) [![License](https://img.shields.io/badge/License-MIT-yellowgreen.svg?style=flat-square)](https://opensource.org/licenses/MIT) [![Docs](https://img.shields.io/crates/v/matrix-pickle?color=blue&label=docs&style=flat-square)](https://docs.rs/matrix-pickle) A simple binary encoding format used in the Matrix world. The `matrix-pickle` binary encoding format is used in the [libolm] and [vodozemac] cryptographic libraries. # How to use The simplest way to use `matrix-pickle` is using the derive macros: ```rust use anyhow::Result; use matrix_pickle::{Encode, Decode}; fn main() -> Result<()> { #[derive(Clone, Debug, Decode, Encode, PartialEq, Eq)] struct MyStruct { public_key: [u8; 32], data: Vec, } let data = MyStruct { public_key: [5u8; 32], data: vec![1, 2, 3], }; let encoded = data.encode_to_vec()?; let decoded = MyStruct::decode_from_slice(&encoded)?; assert_eq!(data, decoded); Ok(()) } ``` # Format definition `matrix-pickle` encodes most values without any metadata, the bytes that are part of the struct in most cases get encoded verbatim. The table bellow defines how common types are encoded. | Type | Example value | Encoded value | Comment | | :-------: | :-----------: | :------------------------: | ------------------------------------------------ | | `u8` | `255` | `[FF]` | Encoded verbatim | | `bool` | `true` | `[01]` | Converted to an `u8` before encoding | | `[u8; N]` | `[1u8, 2u8]` | `[01, 02]` | Encoded verbatim | | `u32` | `16` | `[00, 00, 00, 10]` | Encoded as a byte array in big endian form | | `usize` | `32` | `[00, 00, 00, 20]` | Converted to an `u32` before encoding | | `&[T]` | `&[3u8, 4u8]` | `[00, 00, 00, 02, 03, 04]` | The length gets encoded first, then each element | # Derive support The crate supports deriving `Encode` and `Decode` implementations for structs and enums as long as the types inside them implement `Encode` and `Decode` as well. ## Structs The derive support for structs simply encodes each field of a struct in the order they are defined, for example: ```rust use std::io::Write; use matrix_pickle::{Encode, EncodeError}; struct Foo { first: [u8; 32], second: Vec, } impl Encode for Foo { fn encode(&self, writer: &mut impl Write) -> Result { let mut ret = 0; // Encode the first struct field. ret += self.first.encode(writer)?; // Now encode the second struct field. ret += self.second.encode(writer)?; Ok(ret) } } ``` ## Enums Enums on the other hand first encode the number of the variant as an `u8`, then the value of the enum. Only enums with variants that contain a single associated data value are supported. ```rust use std::io::Write; use matrix_pickle::{Encode, EncodeError}; enum Bar { First(u32), Second(u32), } impl Encode for Bar { fn encode(&self, writer: &mut impl Write) -> Result { let mut ret = 0; match self { Bar::First(value) => { // This is our first variant, encode a 0u8 first. ret += 0u8.encode(writer)?; // Now encode the associated value. ret += value.encode(writer)?; }, Bar::Second(value) => { // This is our second variant, encode a 1u8 first. ret += 1u8.encode(writer)?; // Now encode the associated value. ret += value.encode(writer)?; }, } Ok(ret) } } ``` ## Encoding and decoding secrets For decoding values which are meant to be secret, make sure to box the array. We have a helper attribute that reminds you that values that are meant to be kept secret should be boxed. Simply annotate any struct field using the `#[secret]` attribute. If a value that is meant to be a secret is not boxed a compiler error will be thrown. For example, this snippet won't compile. ```rust,compile_fail use matrix_pickle::{Encode, Decode}; #[derive(Encode, Decode)] struct Key { #[secret] private: [u8; 32], public: [u8; 32], } ``` This example on the other hand compiles. ```rust use matrix_pickle::{Encode, Decode}; #[derive(Encode, Decode)] struct Key { #[secret] private: Box<[u8; 32]>, public: [u8; 32], } ``` # Comparison to bincode The binary format is similar to what the [bincode] crate provides with the following config: ```rust,compile_fail let config = bincode::config::standard() .with_big_endian() .with_fixed_int_encoding() .skip_fixed_array_length(); ``` The two major differences to the format are: * `bincode` uses `u64` to encode slice lengths * `matrix-pickle` uses `u32` to encode slice lengths Other differences are: * No support to configure the encoding format, if you need to tweak the format, use bincode. * No unsafe code. Optimized for simplicity, not for pure performance [libolm]: https://gitlab.matrix.org/matrix-org/olm/ [vodozemac]: https://github.com/matrix-org/vodozemac/ [bincode]: https://github.com/bincode-org/bincode/ matrix-pickle-0.2.2/src/decode.rs000064400000000000000000000056241046102023000147650ustar 00000000000000// Copyright 2021 Damir Jelić // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use std::io::{Cursor, Read}; use crate::{DecodeError, MAX_ARRAY_LENGTH}; /// A trait for decoding values that were encoded using the `matrix-pickle` binary format. pub trait Decode { /// Try to read and decode a value from the given reader. fn decode(reader: &mut impl Read) -> Result where Self: Sized; /// Try to read and decode a value from the given byte slice. fn decode_from_slice(buffer: &[u8]) -> Result where Self: Sized, { let mut cursor = Cursor::new(buffer); Self::decode(&mut cursor) } } impl Decode for u8 { fn decode(reader: &mut impl Read) -> Result { let mut buffer = [0u8; 1]; reader.read_exact(&mut buffer)?; Ok(buffer[0]) } } impl Decode for bool { fn decode(reader: &mut impl Read) -> Result { let value = u8::decode(reader)?; Ok(value != 0) } } impl Decode for u32 { fn decode(reader: &mut impl Read) -> Result { let mut buffer = [0u8; 4]; reader.read_exact(&mut buffer)?; Ok(u32::from_be_bytes(buffer)) } } impl Decode for usize { fn decode(reader: &mut impl Read) -> Result { let size = u32::decode(reader)?; size.try_into() .map_err(|_| DecodeError::OutsideUsizeRange(size as u64)) } } impl Decode for [u8; N] { fn decode(reader: &mut impl Read) -> Result { let mut buffer = [0u8; N]; reader.read_exact(&mut buffer)?; Ok(buffer) } } impl Decode for Box<[u8; N]> { fn decode(reader: &mut impl Read) -> Result { let mut buffer = Box::new([0u8; N]); reader.read_exact(buffer.as_mut_slice())?; Ok(buffer) } } impl Decode for Vec { fn decode(reader: &mut impl Read) -> Result { let length = usize::decode(reader)?; if length > MAX_ARRAY_LENGTH { Err(DecodeError::ArrayTooBig(length)) } else { let mut buffer = Vec::with_capacity(length); for _ in 0..length { let element = T::decode(reader)?; buffer.push(element); } Ok(buffer) } } } matrix-pickle-0.2.2/src/encode.rs000064400000000000000000000047221046102023000147750ustar 00000000000000// Copyright 2022 Damir Jelić // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use std::io::{Cursor, Write}; use crate::{EncodeError, MAX_ARRAY_LENGTH}; /// A trait for encoding values into the `matrix-pickle` binary format. pub trait Encode { /// Try to encode and write a value to the given writer, returning how many bytes were written. fn encode(&self, writer: &mut impl Write) -> Result; /// Try to encode a value into a new `Vec`. fn encode_to_vec(&self) -> Result, EncodeError> { let buffer = Vec::new(); let mut cursor = Cursor::new(buffer); self.encode(&mut cursor)?; Ok(cursor.into_inner()) } } impl Encode for u8 { fn encode(&self, writer: &mut impl Write) -> Result { Ok(writer.write(&[*self])?) } } impl Encode for bool { fn encode(&self, writer: &mut impl Write) -> Result { (*self as u8).encode(writer) } } impl Encode for [u8; N] { fn encode(&self, writer: &mut impl Write) -> Result { writer.write_all(self)?; Ok(N) } } impl Encode for u32 { fn encode(&self, writer: &mut impl Write) -> Result { let bytes = self.to_be_bytes(); bytes.encode(writer) } } impl Encode for usize { fn encode(&self, writer: &mut impl Write) -> Result { let value = u32::try_from(*self).map_err(|_| EncodeError::OutsideU32Range(*self))?; value.encode(writer) } } impl Encode for [T] { fn encode(&self, writer: &mut impl Write) -> Result { let length = self.len(); if length > MAX_ARRAY_LENGTH { Err(EncodeError::ArrayTooBig(length)) } else { let mut ret = length.encode(writer)?; for value in self { ret += value.encode(writer)?; } Ok(ret) } } } matrix-pickle-0.2.2/src/error.rs000064400000000000000000000035761046102023000146770ustar 00000000000000// Copyright 2021, 2022 Damir Jelić // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use thiserror::Error; /// Error type describing failure modes for libolm pickle decoding. #[derive(Debug, Error)] pub enum DecodeError { /// There was an error while reading from the source of the libolm, usually /// not enough data was provided. #[error(transparent)] IO(#[from] std::io::Error), /// The encoded usize doesn't fit into the usize of the architecture that is /// decoding. #[error( "The decoded value {0} does not fit into the usize type of this \ architecture" )] OutsideUsizeRange(u64), /// An array in the pickle has too many elements. #[error("An array has too many elements: {0}")] ArrayTooBig(usize), /// TODO #[error("TODO {0}")] UnknownEnumVariant(u8), } /// Error type describing failure modes for libolm pickle decoding. #[derive(Debug, Error)] pub enum EncodeError { /// There was an error while writing to the buffer. #[error(transparent)] IO(#[from] std::io::Error), /// The usize value that should be encoded doesn't fit into the u32 range of /// values. #[error("The usize value {0} does not fit into the u32 range of values.")] OutsideU32Range(usize), /// An array in the pickle has too many elements. #[error("An array has too many elements: {0}")] ArrayTooBig(usize), } matrix-pickle-0.2.2/src/lib.rs000064400000000000000000000106621046102023000143060ustar 00000000000000// Copyright 2021, 2022 Damir Jelić // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #![doc = include_str!("../README.md")] #![deny( clippy::mem_forget, clippy::unwrap_used, dead_code, missing_docs, trivial_casts, trivial_numeric_casts, unsafe_code, unsafe_op_in_unsafe_fn, unused_import_braces, unused_qualifications )] #![cfg_attr(docsrs, feature(doc_auto_cfg))] extern crate self as matrix_pickle; mod decode; mod encode; mod error; const MAX_ARRAY_LENGTH: usize = u16::MAX as usize; #[cfg(feature = "derive")] pub use matrix_pickle_derive::*; pub use decode::*; pub use encode::*; pub use error::*; #[cfg(test)] mod test { use proptest::prelude::*; use super::*; macro_rules! encode_cycle { ($value:expr => $type:ty) => { let value = $value; let encoded = value .encode_to_vec() .expect("We can always encode into to a Vec"); let decoded = <$type>::decode_from_slice(&encoded) .expect("Decoding a freshly encoded value always works"); assert_eq!( value, decoded, "The original value and the decoded value are not the same" ); }; } macro_rules! encode_length_check { ($value:expr) => { let mut buffer = Vec::new(); let size = $value .encode(&mut buffer) .expect("We can always encode into to a Vec"); assert_eq!(size, buffer.len()); }; } #[test] fn encode_cycle() { encode_cycle!(10u8 => u8); encode_cycle!(10u32 => u32); encode_cycle!(10usize => usize); encode_cycle!(true => bool); encode_cycle!(false => bool); encode_cycle!(vec![1, 2, 3, 4] => Vec); } #[test] fn encode_length_check() { encode_length_check!(10u8); encode_length_check!(10u32); encode_length_check!(10usize); encode_length_check!(true); encode_length_check!(false); encode_length_check!([1u32, 2u32, 3u32, 4u32]); } proptest! { #[test] fn encode_cycle_u8(a in 0..u8::MAX) { encode_cycle!(a => u8); } #[test] fn encode_cycle_u32(a in 0..u32::MAX) { encode_cycle!(a => u32); } #[test] fn encode_cycle_usize(a in 0..u32::MAX) { let a = a as usize; encode_cycle!(a => usize); } fn encode_cycle_vec(bytes in prop::collection::vec(any::(), 0..1000)) { encode_cycle!(bytes => Vec); } } #[test] fn max_array_length() { assert!(matches!( [false; MAX_ARRAY_LENGTH + 1].encode_to_vec(), Err(EncodeError::ArrayTooBig(_)) )); let mut buffer = Vec::::new(); (MAX_ARRAY_LENGTH + 1) .encode(&mut buffer) .expect("Should encode length"); assert!(matches!( Vec::::decode(&mut &*buffer), Err(DecodeError::ArrayTooBig(_)) )); } #[test] #[cfg(feature = "derive")] fn derive() { #[derive(Clone, Encode, Decode, PartialEq, Debug)] struct Foo { thing: [u8; 32], #[secret] another: Box<[u8; 64]>, } let foo = Foo { thing: [1u8; 32], another: Box::new([2u8; 64]), }; encode_cycle!(foo.clone() => Foo); #[derive(Clone, Encode, Decode, PartialEq, Debug)] struct Bar([u8; 32]); let bar = Bar([1u8; 32]); encode_cycle!(bar.clone() => Bar); #[derive(Encode, Decode, PartialEq, Debug)] enum Something { Foo(Foo), Bar(Bar), } let something = Something::Foo(foo); encode_cycle!(something => Something); let something = Something::Bar(bar); encode_cycle!(something => Something); } }