Support custom errors in 'Encode' and 'Decode' (using associated 'Error' type); Further split 'EncodeError' into 'IsizeEncodeError', 'UsizeEncodeError', 'CollectionEncodeError', 'RefCellEncodeError', 'ItemEncodeError', and 'EnumEncodeError'; Fix the 'Encode' implementation of 'LinkedList'; Further split 'DecodeError' into 'BoolDecodeError', 'CharDecodeError', 'CStringDecodeError', 'NonZeroDecodeError', 'CollectionDecodeError', 'SystemTimeDecodeError', 'EnumDecodeError', and 'ItemDecodeError'; Honour custom discriminant types; Add 'DecodeBorrowed' trait (implement appropriately); Implement 'Decode' for 'Cow'; Refactor derive macros; Update lints; Rename test modules from 'test' to 'tests'; Restructure some trait implementations; Add 'proc-macro' feature flag; Add 'GenericEncodeError' and 'GenericDecodeError' error types for derived traits; Add 'PrimitiveDiscriminant' trait; Lock 'Arc' implementations behind atomic widths; Add '?Sized' clauses to some 'Encode' implementations; Update readme; Fix doc entry for 'SizedStr::new'; Update atomic tests; Make 'SizedEncode' a safe trait; Do not automatically implement 'Encode' when deriving 'SizedEncode'; Add 'copy_from_slice' method to 'SizedSlice'; Add 'each_ref' and 'each_mut' methods to 'SizedSlice'; Add more benchmarks; Remove Ciborium benchmarks; Rename project to *Librum*; Rename 'bzipper' crate to 'librum'; Rename 'bzipper_macros' crate to 'librum-macros'; Rename 'bzipper_benchmarks' crate to 'librum-benchmarks';
This commit is contained in:
parent
74b8d2caa2
commit
0160d295bd
78 changed files with 4630 additions and 2540 deletions
34
CHANGELOG.md
34
CHANGELOG.md
|
@ -1,8 +1,40 @@
|
|||
# Changelog
|
||||
|
||||
This is the changelog of bzipper.
|
||||
This is the changelog of Librum.
|
||||
See `README.md` for more information.
|
||||
|
||||
## 0.12.0
|
||||
|
||||
* Support custom errors in `Encode` and `Decode` (using associated `Error` type)
|
||||
* Further split `EncodeError` into `IsizeEncodeError`, `UsizeEncodeError`, `CollectionEncodeError`, `RefCellEncodeError`, `ItemEncodeError`, and `EnumEncodeError`
|
||||
* Fix the `Encode` implementation of `LinkedList`
|
||||
* Further split `DecodeError` into `BoolDecodeError`, `CharDecodeError`, `CStringDecodeError`, `NonZeroDecodeError`, `CollectionDecodeError`, `SystemTimeDecodeError`, `EnumDecodeError`, and `ItemDecodeError`
|
||||
* Honour custom discriminant types
|
||||
* Add `DecodeBorrowed` trait (implement appropriately)
|
||||
* Implement `Decode` for `Cow`
|
||||
* Refactor derive macros
|
||||
* Update lints
|
||||
* Rename test modules from `test` to `tests`
|
||||
* Restructure some trait implementations
|
||||
* Add `proc-macro` feature flag
|
||||
* Add `GenericEncodeError` and `GenericDecodeError` error types for derived traits
|
||||
* Add `PrimitiveDiscriminant` trait
|
||||
* Lock `Arc` implementations behind atomic widths
|
||||
* Add `?Sized` clauses to some `Encode` implementations
|
||||
* Update readme
|
||||
* Fix doc entry for `SizedStr::new`
|
||||
* Update atomic tests
|
||||
* Make `SizedEncode` a safe trait
|
||||
* Do not automatically implement `Encode` when deriving `SizedEncode`
|
||||
* Add `copy_from_slice` method to `SizedSlice`
|
||||
* Add `each_ref` and `each_mut` methods to `SizedSlice`
|
||||
* Add more benchmarks
|
||||
* Remove Ciborium benchmarks
|
||||
* Rename project to *Librum*
|
||||
* Rename `bzipper` crate to `librum`
|
||||
* Rename `bzipper_macros` crate to `librum-macros`
|
||||
* Rename `bzipper_benchmarks` crate to `librum-benchmarks`
|
||||
|
||||
## 0.11.0
|
||||
|
||||
* Add `into_bytes` destructor to `SizedStr`
|
||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -1,5 +1,5 @@
|
|||
[workspace]
|
||||
members = ["bzipper", "bzipper_benchmarks", "bzipper_macros"]
|
||||
members = ["librum", "librum-benchmarks", "librum-macros"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
|
@ -7,14 +7,14 @@ authors = ["Gabriel Bjørnager Jensen"]
|
|||
description = "Binary (de)serialiser."
|
||||
readme = "README.md"
|
||||
homepage = "https://achernar.dk/index.php?p=bzipper"
|
||||
repository = "https://mandelbrot.dk/bzipper/"
|
||||
repository = "https://mandelbrot.dk/librum/"
|
||||
license = "LGPL-3.0-or-later"
|
||||
keywords = ["api", "encoding", "io", "network", "no-std"]
|
||||
categories = ["encoding", "network-programming", "parsing"]
|
||||
|
||||
[workspace.lints.clippy]
|
||||
as_ptr_cast_mut = "forbid"
|
||||
as_underscore = "warn"
|
||||
as_underscore = "forbid"
|
||||
assertions_on_result_states = "warn"
|
||||
bool_to_int_with_if = "warn"
|
||||
borrow_as_ptr = "forbid"
|
||||
|
@ -90,8 +90,6 @@ mutex_integer = "deny"
|
|||
needless_bitwise_bool = "deny"
|
||||
needless_collect = "warn"
|
||||
needless_continue = "warn"
|
||||
needless_pass_by_ref_mut = "warn"
|
||||
needless_pass_by_value = "deny"
|
||||
needless_raw_string_hashes = "warn"
|
||||
needless_raw_strings = "warn"
|
||||
no_effect_underscore_binding = "deny"
|
||||
|
@ -111,7 +109,6 @@ read_zero_byte_vec = "deny"
|
|||
redundant_clone = "deny"
|
||||
redundant_closure_for_method_calls = "warn"
|
||||
redundant_else = "warn"
|
||||
redundant_pub_crate = "warn"
|
||||
redundant_type_annotations = "warn"
|
||||
ref_as_ptr = "deny"
|
||||
ref_binding_to_reference = "warn"
|
||||
|
@ -142,7 +139,6 @@ unnested_or_patterns = "warn"
|
|||
unused_async = "warn"
|
||||
unused_peekable = "warn"
|
||||
unused_rounding = "warn"
|
||||
unused_self = "warn"
|
||||
use_self = "deny"
|
||||
used_underscore_binding = "deny"
|
||||
useless_let_if_seq = "warn"
|
||||
|
|
140
README.md
140
README.md
|
@ -1,6 +1,6 @@
|
|||
# bZipper
|
||||
# Librum
|
||||
|
||||
bZipper is a Rust crate for cheaply serialising (encoding) and deserialising (decoding) data structures into binary streams
|
||||
Librum is a Rust crate for cheaply serialising (encoding) and deserialising (decoding) data structures into binary streams
|
||||
|
||||
What separates this crate from others such as [Bincode](https://crates.io/crates/bincode/) or [Postcard](https://crates.io/crates/postcard/) is that this crate is extensively optimised for *just* binary encodings (whilst the mentioned crates specifically use Serde and build on a more abstract data model).
|
||||
The original goal of this project was specifically to guarantee size constraints for encodings on a per-type basis at compile-time.
|
||||
|
@ -13,81 +13,102 @@ This crate is compatible with `no_std`.
|
|||
|
||||
## Performance
|
||||
|
||||
As bZipper is optimised exclusively for a single, binary format, it may outperform other libraries that are more generic in nature.
|
||||
As Librum is optimised exclusively for a single, binary format, it may outperform other libraries that are more generic in nature.
|
||||
|
||||
The `bzipper_benchmarks` binary compares multiple scenarios using bZipper and other, similar crates.
|
||||
According to my runs on an AMD Ryzen 7 3700X, these benchmarks indicate that bZipper outperform all of the tested crates -- as demonstrated in the following table:
|
||||
The `librum-benchmarks` binary compares multiple scenarios using Librum and other, similar crates.
|
||||
According to my runs on an AMD Ryzen 7 3700X, these benchmarks indicate that Librum outperform all of the tested crates -- as demonstrated in the following table:
|
||||
|
||||
| Benchmark | [Bincode] | [Borsh] | bZipper | [Ciborium] | [Postcard] |
|
||||
| :--------------------------------- | --------: | ------: | ------: | ---------: | ---------: |
|
||||
| `encode_u8` | 1.234 | 1.096 | 0.881 | 3.076 | 1.223 |
|
||||
| `encode_struct_unit` | 0.000 | 0.000 | 0.000 | 0.516 | 0.000 |
|
||||
| `encode_struct_unnamed` | 1.367 | 1.154 | 1.009 | 2.051 | 1.191 |
|
||||
| `encode_struct_named` | 4.101 | 1.271 | 1.181 | 9.342 | 1.182 |
|
||||
| `encode_enum_unit` | 0.306 | 0.008 | 0.000 | 2.304 | 0.004 |
|
||||
| **Total time** → | 7.009 | 3.528 | 3.071 | 17.289 | 3.599 |
|
||||
| **Total deviation (p.c.)** → | +128 | +15 | ±0 | +463 | +17 |
|
||||
| Benchmark | [Bincode] | [Borsh] | Librum | [Postcard] |
|
||||
| :--------------------------------- | --------: | ------: | ------: | ---------: |
|
||||
| `encode_u8` | 1.306 | 1.315 | 1.150 | 1.304 |
|
||||
| `encode_u32` | 1.321 | 1.317 | 1.146 | 3.016 |
|
||||
| `encode_u128` | 2.198 | 2.103 | 1.509 | 6.376 |
|
||||
| `encode_struct_unit` | 0.000 | 0.000 | 0.000 | 0.000 |
|
||||
| `encode_struct_unnamed` | 1.362 | 1.448 | 1.227 | 2.659 |
|
||||
| `encode_struct_named` | 3.114 | 1.530 | 0.969 | 3.036 |
|
||||
| `encode_enum_unit` | 0.252 | 0.297 | 0.000 | 0.299 |
|
||||
| **Total time** → | 9.553 | 8.010 | 6.001 | 16.691 |
|
||||
| **Total deviation (p.c.)** → | +59 | +33 | ±0 | +178 |
|
||||
|
||||
[Bincode]: https://crates.io/crates/bincode/
|
||||
[Borsh]: https://crates.io/crates/borsh/
|
||||
[Ciborium]: https://crates.io/crates/ciborium/
|
||||
[Postcard]: https://crates.io/crates/postcard/
|
||||
|
||||
All quantities are measured in seconds unless otherwise noted.
|
||||
Please feel free to conduct your own tests of bZipper.
|
||||
Please feel free to conduct your own tests of Librum.
|
||||
|
||||
## Data model
|
||||
|
||||
Most primitives encode losslessly, with the main exceptions being `usize` and `isize`.
|
||||
These are instead first cast as `u16` and `i16`, respectively, due to portability concerns (with respect to embedded systems).
|
||||
Most primitives encode losslessly, with the main exceptions being [`usize`] and [`isize`].
|
||||
These are instead first cast as [`u16`] and [`i16`], respectively, due to portability concerns (with respect to embedded systems).
|
||||
|
||||
See specific types' implementations for notes on their data models.
|
||||
|
||||
**Note that the data model is currently not stabilised,** and may not necessarily be in the near future (before [specialisation](https://github.com/rust-lang/rust/issues/31844/)).
|
||||
**Note that the data model is currently not stabilised,** and may not necessarily be in the near future (at least before [specialisation](https://github.com/rust-lang/rust/issues/31844/)).
|
||||
It may therefore be undesired to store encodings long-term.
|
||||
|
||||
## Usage
|
||||
|
||||
This crate revolves around the `Encode` and `Decode` traits which both handle conversions to and from byte streams.
|
||||
This crate revolves around the [`Encode`] and [`Decode`] traits which both handle conversions to and from byte streams.
|
||||
|
||||
Many standard types come implemented with bZipper, including most primitives as well as some standard library types such as `Option` and `Result`.
|
||||
Some [features](#features-flags) enable an extended set of implementations.
|
||||
Many standard types come implemented with Librum, including most primitives as well as some standard library types such as [`Option`] and [`Result`].
|
||||
Some [features](#feature-flags) enable an extended set of implementations.
|
||||
|
||||
It is recommended in most cases to simply derive these two traits for custom types (although this is only supported with enumerations and structures – not untagged unions).
|
||||
It is recommended in most cases to simply derive these two traits for custom types (although this is only supported with enumerations and structures -- not untagged unions).
|
||||
Here, each field is *chained* according to declaration order:
|
||||
|
||||
```rust
|
||||
use bzipper::{Buf, Decode, Encode, SizedEncode};
|
||||
```
|
||||
use librum::{Buf, Decode, Encode};
|
||||
|
||||
#[derive(Debug, Decode, PartialEq, SizedEncode)]
|
||||
struct IoRegister {
|
||||
addr: u32,
|
||||
value: u16,
|
||||
##[derive(Debug, Decode, Encode, PartialEq)]
|
||||
struct Ints {
|
||||
value0: u8,
|
||||
value1: u16,
|
||||
value2: u32,
|
||||
value3: u64,
|
||||
value4: u128,
|
||||
}
|
||||
|
||||
let mut buf = Buf::new();
|
||||
const VALUE: Ints = Ints {
|
||||
value0: 0x00,
|
||||
value1: 0x02_01,
|
||||
value2: 0x06_05_04_03,
|
||||
value3: 0x0E_0D_0C_0B_0A_09_08_07,
|
||||
value4: 0x1E_1D_1C_1B_1A_19_18_17_16_15_14_13_12_11_10_0F,
|
||||
};
|
||||
|
||||
buf.write(IoRegister { addr: 0x04000000, value: 0x0402 }).unwrap();
|
||||
let mut buf = Buf::with_capacity(0x100);
|
||||
|
||||
assert_eq!(buf.len(), 0x6);
|
||||
assert_eq!(buf, [0x04, 0x00, 0x00, 0x00, 0x04, 0x02].as_slice());
|
||||
buf.write(VALUE).unwrap();
|
||||
|
||||
assert_eq!(buf.read().unwrap(), IoRegister { addr: 0x04000000, value: 0x0402 });
|
||||
assert_eq!(buf.len(), 0x1F);
|
||||
|
||||
assert_eq!(
|
||||
buf,
|
||||
[
|
||||
0x00, 0x02, 0x01, 0x06, 0x05, 0x04, 0x03, 0x0E,
|
||||
0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x1E,
|
||||
0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16,
|
||||
0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F,
|
||||
].as_slice(),
|
||||
);
|
||||
|
||||
assert_eq!(buf.read().unwrap(), VALUE);
|
||||
```
|
||||
|
||||
### Buffer types
|
||||
|
||||
The `Encode` and `Decode` traits both rely on streams for carrying the manipulated byte streams.
|
||||
The [`Encode`] and [`Decode`] traits both rely on streams for carrying the manipulated bytes.
|
||||
|
||||
These streams are separated into two type: *O-streams* (output streams) and *i-streams* (input streams).
|
||||
Often, but not always, the `Buf` type is preferred over directly calling the `encode` and `decode` methods.
|
||||
These streams are separated into two type: [*O-streams*](OStream) (output streams) and [*i-streams*](IStream) (input streams).
|
||||
The [`Buf`] type can be used to handle these streams.
|
||||
|
||||
### Encoding
|
||||
|
||||
To encode an object directly using the `Encode` trait, simply allocate a buffer for the encoding and wrap it in an `OStream` object:
|
||||
To encode an object directly using the `Encode` trait, simply allocate a buffer for the encoding and wrap it in an [`OStream`] object:
|
||||
|
||||
```rust
|
||||
use bzipper::{Encode, OStream, SizedEncode};
|
||||
```
|
||||
use librum::{Encode, OStream, SizedEncode};
|
||||
|
||||
let mut buf = [0x00; char::MAX_ENCODED_SIZE];
|
||||
let mut stream = OStream::new(&mut buf);
|
||||
|
@ -99,8 +120,8 @@ assert_eq!(buf, [0x00, 0x00, 0x04, 0x16].as_slice());
|
|||
|
||||
Streams can also be used to chain multiple objects together:
|
||||
|
||||
```rust
|
||||
use bzipper::{Encode, OStream, SizedEncode};
|
||||
```
|
||||
use librum::{Encode, OStream, SizedEncode};
|
||||
|
||||
let mut buf = [0x0; char::MAX_ENCODED_SIZE * 0x5];
|
||||
let mut stream = OStream::new(&mut buf);
|
||||
|
@ -122,7 +143,7 @@ assert_eq!(buf, [
|
|||
]);
|
||||
```
|
||||
|
||||
If the encoded type additionally implements `SizedEncode`, then the maximum size of any encoding is guaranteed with the `MAX_ENCODED_SIZE` constant.
|
||||
If the encoded type additionally implements [`SizedEncode`], then the maximum size of any encoding is guaranteed with the [`MAX_ENCODED_SIZE`](SizedEncode::MAX_ENCODED_SIZE) constant.
|
||||
|
||||
Numerical primitives are encoded in big endian (a.k.a. [network order](https://en.wikipedia.org/wiki/Endianness#Networking)) for... reasons.
|
||||
It is recommended for implementors to follow this convention as well.
|
||||
|
@ -130,10 +151,10 @@ It is recommended for implementors to follow this convention as well.
|
|||
### Decoding
|
||||
|
||||
Decoding works with a similar syntax to encoding.
|
||||
To decode a byte array, simply call the `decode` method with an `IStream` object:
|
||||
To decode a byte array, simply call the [`decode`](Decode::decode) method with an [`IStream`] object:
|
||||
|
||||
```rust
|
||||
use bzipper::{Decode, IStream};
|
||||
```
|
||||
use librum::{Decode, IStream};
|
||||
|
||||
let data = [0x45, 0x54];
|
||||
let mut stream = IStream::new(&data);
|
||||
|
@ -158,14 +179,14 @@ assert_eq!(<(u8, u8)>::decode(&mut stream).unwrap(), (0x45, 0x54));
|
|||
|
||||
A UDP server/client for geographic data:
|
||||
|
||||
```rust
|
||||
use bzipper::{Buf, Decode, SizedEncode};
|
||||
```
|
||||
use librum::{Buf, Encode, Decode, SizedEncode};
|
||||
use std::io;
|
||||
use std::net::{SocketAddr, ToSocketAddrs, UdpSocket};
|
||||
use std::thread::spawn;
|
||||
|
||||
// City, region, etc.:
|
||||
#[derive(Clone, Copy, Debug, Decode, Eq, PartialEq, SizedEncode)]
|
||||
##[derive(Clone, Copy, Debug, Decode, Encode, Eq, PartialEq, SizedEncode)]
|
||||
enum Area {
|
||||
AlQuds,
|
||||
Byzantion,
|
||||
|
@ -175,7 +196,7 @@ enum Area {
|
|||
}
|
||||
|
||||
// Client-to-server message:
|
||||
#[derive(Debug, Decode, PartialEq, SizedEncode)]
|
||||
##[derive(Debug, Decode, Encode, PartialEq, SizedEncode)]
|
||||
enum Request {
|
||||
AtmosphericHumidity { area: Area },
|
||||
AtmosphericPressure { area: Area },
|
||||
|
@ -184,7 +205,7 @@ enum Request {
|
|||
}
|
||||
|
||||
// Server-to-client message:
|
||||
#[derive(Debug, Decode, PartialEq, SizedEncode)]
|
||||
##[derive(Debug, Decode, Encode, PartialEq, SizedEncode)]
|
||||
enum Response {
|
||||
AtmosphericHumidity(f64),
|
||||
AtmosphericPressure(f64), // Pascal
|
||||
|
@ -260,25 +281,28 @@ spawn(move || {
|
|||
|
||||
## Feature flags
|
||||
|
||||
bZipper defines the following features:
|
||||
Librum defines the following features:
|
||||
|
||||
* `alloc` (default): Enables the `Buf` type and implementations for e.g. `Box` and `Arc`
|
||||
* `std` (default): Enables implementations for types such as `Mutex` and `RwLock`
|
||||
* *`alloc`: Enables the [`Buf`] type and implementations for e.g. [`Box`](alloc::boxed::Box) and [`Arc`](alloc::sync::Arc)
|
||||
* *`proc-macro`: Pulls the procedural macros from the [`librum_macros`](https://crates.io/crates/librum_macros/) crate
|
||||
* *`std`: Enables implementations for types such as [`Mutex`](std::sync::Mutex) and [`RwLock`](std::sync::RwLock)
|
||||
|
||||
Features marked with * are enabled by default.
|
||||
|
||||
## Documentation
|
||||
|
||||
bZipper has its documentation written in-source for use by `rustdoc`.
|
||||
See [Docs.rs](https://docs.rs/bzipper/latest/bzipper/) for an on-line, rendered instance.
|
||||
Librum has its documentation written in-source for use by `rustdoc`.
|
||||
See [Docs.rs](https://docs.rs/librum/latest/librum/) for an on-line, rendered instance.
|
||||
|
||||
Currently, these docs make use of some unstable features for the sake of readability.
|
||||
The nightly toolchain is therefore required when rendering them.
|
||||
|
||||
## Contribution
|
||||
|
||||
bZipper does not accept source code contributions at the moment.
|
||||
Librum does not accept source code contributions at the moment.
|
||||
This is a personal choice by the maintainer and may be undone in the future.
|
||||
|
||||
Do however feel free to open up an issue on [`GitLab`](https://gitlab.com/bjoernager/bzipper/issues/) or (preferably) [`GitHub`](https://github.com/bjoernager/bzipper/issues/) if you feel the need to express any concerns over the project.
|
||||
Do however feel free to open up an issue on [`GitLab`](https://gitlab.com/bjoernager/librum/issues/) or (preferably) [`GitHub`](https://github.com/bjoernager/librum/issues/) if you feel the need to express any concerns over the project.
|
||||
|
||||
## Copyright & Licence
|
||||
|
||||
|
|
|
@ -1,139 +0,0 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::error::{SizeError, Utf8Error};
|
||||
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::boxed::Box;
|
||||
|
||||
/// Decode error variants.
|
||||
///
|
||||
/// These errors may be returned from implementation of [`Decode`](crate::Decode).
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum DecodeError {
|
||||
/// Bytes were requested on an empty stream.
|
||||
///
|
||||
/// This variant is different from [`SmallBuffer`](Self::SmallBuffer) in that this is exclusively for use by the stream types, whilst `SmallBuffer` is for any other array-like type.
|
||||
BadString(Utf8Error),
|
||||
|
||||
/// An unspecified error.
|
||||
///
|
||||
/// This is mainly useful by third-party implementors if none of the other predefined variants are appropriate.
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
CustomError(Box<dyn core::error::Error>),
|
||||
|
||||
/// A boolean encountered a value outside `0` and `1`.
|
||||
InvalidBoolean(u8),
|
||||
|
||||
/// An invalid code point was encountered.
|
||||
///
|
||||
/// This includes surrogate points in the inclusive range `U+D800` to `U+DFFF`, as well as all values larger than `U+10FFFF`.
|
||||
InvalidCodePoint(u32),
|
||||
|
||||
/// An invalid enumeration descriminant was provided.
|
||||
InvalidDiscriminant(isize),
|
||||
|
||||
/// The [`SystemTime`](std::time::SystemTime) type could not represent a UNIX timestamp.
|
||||
///
|
||||
/// This error should not occur on systems that represent timestamps with at least a signed 64-bits seconds counter.
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
NarrowSystemTime {
|
||||
/// The unrepresentable timestamp.
|
||||
timestamp: i64,
|
||||
},
|
||||
|
||||
/// A non-zero integer had the value `0`.
|
||||
NullInteger,
|
||||
|
||||
/// A C-like string encountered a null value within bounds.
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
NullCString {
|
||||
/// The index of the null value.
|
||||
index: usize,
|
||||
},
|
||||
|
||||
/// An array could not hold the requested amount of elements.
|
||||
SmallBuffer(SizeError),
|
||||
}
|
||||
|
||||
impl Display for DecodeError {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
use DecodeError::*;
|
||||
|
||||
match *self {
|
||||
BadString(ref source)
|
||||
=> write!(f, "bad string: {source}"),
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
CustomError(ref source)
|
||||
=> write!(f, "{source}"),
|
||||
|
||||
InvalidBoolean(value)
|
||||
=> write!(f, "expected boolean but got `{value:#02X}`"),
|
||||
|
||||
InvalidCodePoint(value)
|
||||
=> write!(f, "code point U+{value:04X} is not defined"),
|
||||
|
||||
InvalidDiscriminant(value)
|
||||
=> write!(f, "discriminant `{value}` is not valid for the given enumeration"),
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
NarrowSystemTime { timestamp }
|
||||
=> write!(f, "could not represent `{timestamp}` as a system timestamp"),
|
||||
|
||||
NullInteger
|
||||
=> write!(f, "expected non-zero integer but got `0`"),
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
NullCString { index }
|
||||
=> write!(f, "expected c string but found null value at '{index}`"),
|
||||
|
||||
SmallBuffer(ref source)
|
||||
=> write!(f, "buffer too small: {source}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for DecodeError {
|
||||
#[inline]
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
use DecodeError::*;
|
||||
|
||||
match *self {
|
||||
BadString(ref source) => Some(source),
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
CustomError(ref source) => Some(source.as_ref()),
|
||||
|
||||
SmallBuffer(ref source) => Some(source),
|
||||
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use core::cell::BorrowError;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::boxed::Box;
|
||||
|
||||
/// Encode error variants.
|
||||
///
|
||||
/// These errors may be returned from implementation of [`Encode`](crate::Encode).
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum EncodeError {
|
||||
/// A [`RefCell`](core::cell::RefCell) object could not be borrowed.
|
||||
BadBorrow(BorrowError),
|
||||
|
||||
/// An unspecified error.
|
||||
///
|
||||
/// This is mainly useful by third-party implementors if none of the other predefined variants are appropriate.
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
CustomError(Box<dyn core::error::Error>),
|
||||
|
||||
/// An `isize` value could not be cast as `i16`.
|
||||
IsizeOutOfRange(isize),
|
||||
|
||||
/// A `usize` value could not be cast as `u16`.
|
||||
UsizeOutOfRange(usize),
|
||||
}
|
||||
|
||||
impl Display for EncodeError {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
use EncodeError::*;
|
||||
|
||||
match *self {
|
||||
BadBorrow(ref source)
|
||||
=> write!(f, "could not borrow reference cell: {source}"),
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
CustomError(ref source)
|
||||
=> write!(f, "{source}"),
|
||||
|
||||
IsizeOutOfRange(value)
|
||||
=> write!(f, "signed size value ({value}) cannot be serialised: must be in the range ({}) to ({})", i16::MIN, i16::MAX),
|
||||
|
||||
UsizeOutOfRange(value)
|
||||
=> write!(f, "unsigned size value ({value}) cannot be serialised: must be at most ({})", u16::MAX),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for EncodeError {
|
||||
#[inline]
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
use EncodeError::*;
|
||||
|
||||
match *self {
|
||||
// In practice useless.
|
||||
BadBorrow(ref source) => Some(source),
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
CustomError(ref source) => Some(source.as_ref()),
|
||||
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Error variants.
|
||||
//!
|
||||
//! This module defines the error types used by bZipper.
|
||||
//! All of these types define the [`Error`](core::error::Error) trait.
|
||||
|
||||
use crate::use_mod;
|
||||
|
||||
use_mod!(pub decode_error);
|
||||
use_mod!(pub encode_error);
|
||||
use_mod!(pub size_error);
|
||||
use_mod!(pub string_error);
|
||||
use_mod!(pub utf16_error);
|
||||
use_mod!(pub utf8_error);
|
|
@ -1,552 +0,0 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
use crate::{
|
||||
Decode,
|
||||
Encode,
|
||||
IStream,
|
||||
OStream,
|
||||
SizedEncode,
|
||||
SizedIter,
|
||||
};
|
||||
use crate::error::{DecodeError, EncodeError, SizeError};
|
||||
|
||||
use core::borrow::{Borrow, BorrowMut};
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt::{self, Debug, Formatter};
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ops::{Deref, DerefMut, Index, IndexMut};
|
||||
use core::ptr::copy_nonoverlapping;
|
||||
use core::slice;
|
||||
use core::slice::{Iter, IterMut, SliceIndex};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::alloc::{alloc, Layout};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::boxed::Box;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::vec::Vec;
|
||||
|
||||
/// Stack-allocated vector with maximum length.
|
||||
///
|
||||
/// This type is intended as an [sized-encodable](SizedEncode) alternative to [`Vec`] for cases where [arrays](array) may not be wanted.
|
||||
///
|
||||
/// Note that this type is immutable in the sense that it does **not** define methods like `push` and `pop`, unlike `Vec`.
|
||||
///
|
||||
/// See [`SizedStr`](crate::SizedStr) for an equivalent alternative to [`String`](alloc::string::String).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// All instances of this type with the same `T` and `N` also have the exact same layout:
|
||||
///
|
||||
/// ```
|
||||
/// use bzipper::SizedSlice;
|
||||
///
|
||||
/// let vec0 = SizedSlice::<u8, 0x4>::try_from([0x3].as_slice()).unwrap();
|
||||
/// let vec1 = SizedSlice::<u8, 0x4>::try_from([0x3, 0x2].as_slice()).unwrap();
|
||||
/// let vec2 = SizedSlice::<u8, 0x4>::try_from([0x3, 0x2, 0x4].as_slice()).unwrap();
|
||||
/// let vec3 = SizedSlice::<u8, 0x4>::try_from([0x3, 0x2, 0x4, 0x3].as_slice()).unwrap();
|
||||
///
|
||||
/// assert_eq!(size_of_val(&vec0), size_of_val(&vec1));
|
||||
/// assert_eq!(size_of_val(&vec0), size_of_val(&vec2));
|
||||
/// assert_eq!(size_of_val(&vec0), size_of_val(&vec3));
|
||||
/// assert_eq!(size_of_val(&vec1), size_of_val(&vec2));
|
||||
/// assert_eq!(size_of_val(&vec1), size_of_val(&vec3));
|
||||
/// assert_eq!(size_of_val(&vec2), size_of_val(&vec3));
|
||||
/// ```
|
||||
pub struct SizedSlice<T, const N: usize> {
|
||||
buf: [MaybeUninit<T>; N],
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl<T, const N: usize> SizedSlice<T, N> {
|
||||
/// Constructs a fixed-size vector from raw parts.
|
||||
///
|
||||
/// The provided parts are not tested in any way.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The value of `len` may not exceed that of `N`.
|
||||
/// Additionally, all elements of `buf` in the range specified by `len` must be initialised.
|
||||
///
|
||||
/// If any of these requirements are violated, behaviour is undefined.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const unsafe fn from_raw_parts(buf: [MaybeUninit<T>; N], len: usize) -> Self {
|
||||
debug_assert!(len <= N, "cannot construct vector longer than its capacity");
|
||||
|
||||
Self { buf, len }
|
||||
}
|
||||
|
||||
/// Sets the length of the vector.
|
||||
///
|
||||
/// The provided length is not tested in any way.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The new length `len` may not be larger than `N`.
|
||||
///
|
||||
/// It is only valid to enlarge vectors if `T` supports being in a purely uninitialised state.
|
||||
/// Such is permitted with e.g. [`MaybeUninit`].
|
||||
#[inline(always)]
|
||||
pub const unsafe fn set_len(&mut self, len: usize) {
|
||||
debug_assert!(len <= N, "cannot set length past bounds");
|
||||
|
||||
self.len = len
|
||||
}
|
||||
|
||||
/// Gets a pointer to the first element.
|
||||
///
|
||||
/// The pointed-to element may not necessarily be initialised.
|
||||
/// See [`len`](Self::len) for more information.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn as_ptr(&self) -> *const T {
|
||||
self.buf.as_ptr().cast()
|
||||
}
|
||||
|
||||
/// Gets a mutable pointer to the first element.
|
||||
///
|
||||
/// The pointed-to element may not necessarily be initialised.
|
||||
/// See [`len`](Self::len) for more information.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn as_mut_ptr(&mut self) -> *mut T {
|
||||
self.buf.as_mut_ptr().cast()
|
||||
}
|
||||
|
||||
/// Borrows the vector as a slice.
|
||||
///
|
||||
/// The range of the returned slice only includes the elements specified by [`len`](Self::len).
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn as_slice(&self) -> &[T] {
|
||||
let ptr = self.as_ptr();
|
||||
let len = self.len();
|
||||
|
||||
unsafe { slice::from_raw_parts(ptr, len) }
|
||||
}
|
||||
|
||||
/// Borrows the vector as a mutable slice.
|
||||
///
|
||||
/// The range of the returned slice only includes the elements specified by [`len`](Self::len).
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn as_mut_slice(&mut self) -> &mut [T] {
|
||||
let ptr = self.as_mut_ptr();
|
||||
let len = self.len();
|
||||
|
||||
unsafe { slice::from_raw_parts_mut(ptr, len) }
|
||||
}
|
||||
|
||||
/// Returns the total capacity of the vector.
|
||||
///
|
||||
/// By definition, this is always exactly equal to the value of `N`.
|
||||
#[expect(clippy::unused_self)]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn capacity(&self) -> usize {
|
||||
N
|
||||
}
|
||||
|
||||
/// Returns the length of the vector.
|
||||
///
|
||||
/// This value may necessarily be smaller than `N`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
|
||||
/// Checks if the vector is empty, i.e. no elements are recorded.
|
||||
///
|
||||
/// Note that the internal buffer may still contain objects that have been "shadowed" by setting a smaller length with [`len`](Self::len).
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn is_empty(&self) -> bool {
|
||||
self.len() == 0x0
|
||||
}
|
||||
|
||||
/// Checks if the vector is full, i.e. it cannot hold any more elements.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn is_full(&self) -> bool {
|
||||
self.len() == self.capacity()
|
||||
}
|
||||
|
||||
/// Destructs the vector into its raw parts.
|
||||
///
|
||||
/// The returned values are valid to pass on to [`from_raw_parts`](Self::from_raw_parts).
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn into_raw_parts(self) -> ([MaybeUninit<T>; N], usize) {
|
||||
let Self { buf, len } = self;
|
||||
(buf, len)
|
||||
}
|
||||
|
||||
/// Converts the vector into a boxed slice.
|
||||
///
|
||||
/// The vector is reallocated using the global allocator.
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
#[must_use]
|
||||
pub fn into_boxed_slice(self) -> Box<[T]> {
|
||||
let (buf, len) = self.into_raw_parts();
|
||||
|
||||
unsafe {
|
||||
let layout = Layout::array::<T>(len).unwrap();
|
||||
let ptr = alloc(layout).cast::<T>();
|
||||
|
||||
assert!(!ptr.is_null(), "allocation failed");
|
||||
|
||||
copy_nonoverlapping(buf.as_ptr().cast(), ptr, len);
|
||||
|
||||
let slice = core::ptr::slice_from_raw_parts_mut(ptr, len);
|
||||
Box::from_raw(slice)
|
||||
|
||||
// `self.buf` is dropped without destructors being
|
||||
// run.
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the vector into a dynamic vector.
|
||||
///
|
||||
/// The vector is reallocated using the global allocator.
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn into_vec(self) -> Vec<T> {
|
||||
self.into_boxed_slice().into_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, const N: usize> SizedSlice<T, N> {
|
||||
/// Constructs an empty, fixed-size vector.
|
||||
#[inline]
|
||||
pub fn new(data: &[T]) -> Result<Self, SizeError> {
|
||||
let mut buf: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
|
||||
|
||||
let len = data.len();
|
||||
if len > N { return Err(SizeError { req: len, len: N }) };
|
||||
|
||||
for (item, value) in buf.iter_mut().zip(data.iter()) {
|
||||
item.write(value.clone());
|
||||
}
|
||||
|
||||
Ok(Self { buf, len })
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> AsMut<[T]> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.as_mut_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> AsRef<[T]> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Borrow<[T]> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &[T] {
|
||||
self.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> BorrowMut<[T]> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn borrow_mut(&mut self) -> &mut [T] {
|
||||
self.as_mut_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, const N: usize> Clone for SizedSlice<T, N> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let mut buf: [MaybeUninit<T>; N] = MaybeUninit::uninit().assume_init();
|
||||
|
||||
for i in 0x0..self.len() {
|
||||
let value = self.get_unchecked(i).clone();
|
||||
buf.get_unchecked_mut(i).write(value);
|
||||
}
|
||||
|
||||
Self { buf, len: self.len }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug, const N: usize> Debug for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
Debug::fmt(self.as_slice(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Default for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
Self { buf: unsafe { MaybeUninit::uninit().assume_init() }, len: 0x0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Deref for SizedSlice<T, N> {
|
||||
type Target = [T];
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> DerefMut for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.as_mut_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decode, const N: usize> Decode for SizedSlice<T, N> {
|
||||
#[inline]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
let len = Decode::decode(stream)?;
|
||||
if len > N { return Err(DecodeError::SmallBuffer(SizeError { req: len, len: N })) };
|
||||
|
||||
let mut buf: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
|
||||
|
||||
for item in &mut buf {
|
||||
let value = Decode::decode(stream)?;
|
||||
|
||||
item.write(value);
|
||||
}
|
||||
|
||||
Ok(Self { buf, len })
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Encode, const N: usize> Encode for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
self.as_slice().encode(stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq, const N: usize> Eq for SizedSlice<T, N> { }
|
||||
|
||||
impl<T, const N: usize> From<[T; N]> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn from(value: [T; N]) -> Self {
|
||||
unsafe {
|
||||
let buf = value.as_ptr().cast::<[MaybeUninit<T>; N]>().read();
|
||||
|
||||
Self { buf, len: N }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> FromIterator<T> for SizedSlice<T, N> {
|
||||
#[inline]
|
||||
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
|
||||
let mut iter = iter.into_iter();
|
||||
|
||||
let mut buf: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
|
||||
let mut len = 0x0;
|
||||
|
||||
for item in &mut buf {
|
||||
let Some(value) = iter.next() else { break };
|
||||
item.write(value);
|
||||
|
||||
len += 0x1;
|
||||
}
|
||||
|
||||
Self { buf, len }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash, const N: usize> Hash for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
for v in self {
|
||||
v.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I: SliceIndex<[T]>, const N: usize> Index<I> for SizedSlice<T, N> {
|
||||
type Output = I::Output;
|
||||
|
||||
#[inline(always)]
|
||||
fn index(&self, index: I) -> &Self::Output {
|
||||
self.get(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I: SliceIndex<[T]>, const N: usize> IndexMut<I> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn index_mut(&mut self, index: I) -> &mut Self::Output {
|
||||
self.get_mut(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> IntoIterator for SizedSlice<T, N> {
|
||||
type Item = T;
|
||||
|
||||
type IntoIter = SizedIter<T, N>;
|
||||
|
||||
#[inline(always)]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
let Self { buf, len } = self;
|
||||
|
||||
unsafe { SizedIter::new(buf, len) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, const N: usize> IntoIterator for &'a SizedSlice<T, N> {
|
||||
type Item = &'a T;
|
||||
|
||||
type IntoIter = Iter<'a, T>;
|
||||
|
||||
#[inline(always)]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, const N: usize> IntoIterator for &'a mut SizedSlice<T, N> {
|
||||
type Item = &'a mut T;
|
||||
|
||||
type IntoIter = IterMut<'a, T>;
|
||||
|
||||
#[inline(always)]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ord, const N: usize> Ord for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.as_slice().cmp(other.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq<U>, U: PartialEq<T>, const N: usize, const M: usize> PartialEq<SizedSlice<U, M>> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &SizedSlice<U, M>) -> bool {
|
||||
self.as_slice() == other.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq<U>, U: PartialEq<T>, const N: usize, const M: usize> PartialEq<[U; M]> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &[U; M]) -> bool {
|
||||
self.as_slice() == other.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq<U>, U: PartialEq<T>, const N: usize> PartialEq<&[U]> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &&[U]) -> bool {
|
||||
self.as_slice() == *other
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: PartialEq<U>, U: PartialEq<T>, const N: usize> PartialEq<Vec<U>> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Vec<U>) -> bool {
|
||||
self.as_slice() == other.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialOrd, const N: usize, const M: usize> PartialOrd<SizedSlice<T, M>> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &SizedSlice<T, M>) -> Option<Ordering> {
|
||||
self.as_slice().partial_cmp(other.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialOrd, const N: usize, const M: usize> PartialOrd<[T; M]> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &[T; M]) -> Option<Ordering> {
|
||||
self.as_slice().partial_cmp(other.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialOrd, const N: usize> PartialOrd<&[T]> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &&[T]) -> Option<Ordering> {
|
||||
self.as_slice().partial_cmp(*other)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: PartialOrd, const N: usize> PartialOrd<Vec<T>> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &Vec<T>) -> Option<Ordering> {
|
||||
self.as_slice().partial_cmp(other.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: SizedEncode, const N: usize> SizedEncode for SizedSlice<T, N> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE * N;
|
||||
}
|
||||
|
||||
impl<T: Clone, const N: usize> TryFrom<&[T]> for SizedSlice<T, N> {
|
||||
type Error = SizeError;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(value: &[T]) -> Result<Self, Self::Error> {
|
||||
Self::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T, const N: usize> From<SizedSlice<T, N>> for Box<[T]> {
|
||||
#[inline(always)]
|
||||
fn from(value: SizedSlice<T, N>) -> Self {
|
||||
value.into_boxed_slice()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T, const N: usize> From<SizedSlice<T, N>> for Vec<T> {
|
||||
#[inline(always)]
|
||||
fn from(value: SizedSlice<T, N>) -> Self {
|
||||
value.into_vec()
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use syn::{Expr, Lit};
|
||||
|
||||
/// An enumeration discriminant.
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
#[repr(transparent)]
|
||||
pub struct Discriminant(pub isize);
|
||||
|
||||
impl Discriminant {
|
||||
/// Parses the expression as a discriminant value.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This constructor will panic if the provided expression is not a valid `isize` literal.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn parse(expr: &Expr) -> Self {
|
||||
let Expr::Lit(ref expr) = *expr else {
|
||||
panic!("expected literal expression for discriminant value");
|
||||
};
|
||||
|
||||
let Lit::Int(ref expr) = expr.lit else {
|
||||
panic!("expected integer literal for discriminant value");
|
||||
};
|
||||
|
||||
let value = expr.base10_parse::<isize>()
|
||||
.expect("expected `isize` literal for discriminant value");
|
||||
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Discriminant {
|
||||
#[inline(always)]
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
self.0.to_tokens(tokens);
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::DiscriminantIter;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{DataEnum, Fields, Token, Variant};
|
||||
use syn::punctuated::Punctuated;
|
||||
|
||||
#[must_use]
|
||||
pub fn decode_enum(data: &DataEnum) -> TokenStream {
|
||||
let mut match_arms = Punctuated::<TokenStream, Token![,]>::new();
|
||||
|
||||
for (discriminant, variant) in DiscriminantIter::new(&data.variants) {
|
||||
let mut chain_commands = Punctuated::<TokenStream, Token![,]>::new();
|
||||
|
||||
for field in &variant.fields {
|
||||
let command = field.ident
|
||||
.as_ref()
|
||||
.map_or_else(
|
||||
|| quote! { ::bzipper::Decode::decode(stream)? },
|
||||
|field_name| quote! { #field_name: ::bzipper::Decode::decode(stream)? },
|
||||
);
|
||||
|
||||
chain_commands.push(command);
|
||||
}
|
||||
|
||||
let value = match *variant {
|
||||
Variant { ident: ref variant_name, fields: Fields::Named( ..), .. } => quote! { Self::#variant_name { #chain_commands } },
|
||||
Variant { ident: ref variant_name, fields: Fields::Unnamed(..), .. } => quote! { Self::#variant_name(#chain_commands) },
|
||||
Variant { ident: ref variant_name, fields: Fields::Unit, .. } => quote! { Self::#variant_name },
|
||||
};
|
||||
|
||||
match_arms.push(quote! { #discriminant => #value });
|
||||
}
|
||||
|
||||
match_arms.push(quote! {
|
||||
value => return ::core::result::Result::Err(::bzipper::error::DecodeError::InvalidDiscriminant(value))
|
||||
});
|
||||
|
||||
quote! {
|
||||
#[inline]
|
||||
fn decode(stream: &mut ::bzipper::IStream) -> ::core::result::Result<Self, ::bzipper::error::DecodeError> {
|
||||
let value = match (<isize as ::bzipper::Decode>::decode(stream)?) { #match_arms };
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{DataStruct, Fields, Token};
|
||||
use syn::punctuated::Punctuated;
|
||||
|
||||
#[must_use]
|
||||
pub fn decode_struct(data: &DataStruct) -> TokenStream {
|
||||
let mut chain_commands = Punctuated::<TokenStream, Token![,]>::new();
|
||||
|
||||
for field in &data.fields {
|
||||
let command = field.ident
|
||||
.as_ref()
|
||||
.map_or_else(
|
||||
|| quote! { ::bzipper::Decode::decode(stream)? },
|
||||
|field_name| quote! { #field_name: ::bzipper::Decode::decode(stream)? },
|
||||
);
|
||||
|
||||
chain_commands.push(command);
|
||||
}
|
||||
|
||||
let value = match data.fields {
|
||||
Fields::Named( ..) => quote! { Self { #chain_commands } },
|
||||
Fields::Unnamed(..) => quote! { Self(#chain_commands) },
|
||||
Fields::Unit => quote! { Self },
|
||||
};
|
||||
|
||||
quote! {
|
||||
#[inline]
|
||||
fn decode(stream: &mut ::bzipper::IStream) -> ::core::result::Result<Self, ::bzipper::error::DecodeError> {
|
||||
let value = #value;
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::DiscriminantIter;
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
DataEnum,
|
||||
Fields,
|
||||
Ident,
|
||||
Variant,
|
||||
};
|
||||
|
||||
#[must_use]
|
||||
pub fn encode_enum(data: &DataEnum) -> TokenStream {
|
||||
let mut match_arms = Vec::new();
|
||||
|
||||
// Iterate over each variant and give it a unique
|
||||
// encoding scheme.
|
||||
for (discriminant, variant) in DiscriminantIter::new(&data.variants) {
|
||||
// The original identifiers of the fields:
|
||||
let mut field_names = Vec::new();
|
||||
|
||||
// The captured field identifiers:
|
||||
let mut field_captures = Vec::new();
|
||||
|
||||
for (index, field) in variant.fields.iter().enumerate() {
|
||||
let capture = Ident::new(&format!("value{index}"), Span::call_site());
|
||||
|
||||
field_names.push(&field.ident);
|
||||
field_captures.push(capture);
|
||||
}
|
||||
|
||||
let pattern = match *variant {
|
||||
Variant { ident: ref variant_name, fields: Fields::Named( ..), .. } => quote! { Self::#variant_name { #(#field_names: ref #field_captures, )* } },
|
||||
Variant { ident: ref variant_name, fields: Fields::Unnamed(..), .. } => quote! { Self::#variant_name(#(ref #field_captures)*) },
|
||||
Variant { ident: ref variant_name, fields: Fields::Unit, .. } => quote! { Self::#variant_name },
|
||||
};
|
||||
|
||||
match_arms.push(quote! {
|
||||
#pattern => {
|
||||
::bzipper::Encode::encode(&#discriminant, stream)?;
|
||||
#(::bzipper::Encode::encode(#field_captures, stream)?;)*
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
quote! {
|
||||
#[inline]
|
||||
fn encode(&self, stream: &mut ::bzipper::OStream) -> ::core::result::Result<(), ::bzipper::error::EncodeError> {
|
||||
match *self {
|
||||
#(#match_arms)*
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::{DataStruct, Index};
|
||||
|
||||
#[must_use]
|
||||
pub fn encode_struct(data: &DataStruct) -> TokenStream {
|
||||
let mut fields = Vec::new();
|
||||
|
||||
for (index, field) in data.fields.iter().enumerate() {
|
||||
let name = field.ident
|
||||
.as_ref()
|
||||
.map_or_else(|| Index::from(index).to_token_stream(), ToTokens::to_token_stream);
|
||||
|
||||
fields.push(name);
|
||||
}
|
||||
|
||||
quote! {
|
||||
#[inline]
|
||||
fn encode(&self, stream: &mut ::bzipper::OStream) -> ::core::result::Result<(), ::bzipper::error::EncodeError> {
|
||||
#(::bzipper::Encode::encode(&self.#fields, stream)?;)*
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::DataEnum;
|
||||
|
||||
#[must_use]
|
||||
pub fn sized_encode_enum(data: &DataEnum) -> TokenStream {
|
||||
let mut sizes = Vec::new();
|
||||
|
||||
// Iterate over each variant and give it a unique
|
||||
// encoding scheme.
|
||||
for variant in &data.variants {
|
||||
let mut field_tys = Vec::new();
|
||||
|
||||
for field in &variant.fields {
|
||||
field_tys.push(&field.ty);
|
||||
}
|
||||
|
||||
sizes.push(quote! {
|
||||
<isize as ::bzipper::SizedEncode>::MAX_ENCODED_SIZE
|
||||
#(+ <#field_tys as ::bzipper::SizedEncode>::MAX_ENCODED_SIZE)*
|
||||
});
|
||||
}
|
||||
|
||||
quote! {
|
||||
const MAX_ENCODED_SIZE: usize = const {
|
||||
let mut max_encoded_size = 0x0usize;
|
||||
|
||||
#(if #sizes > max_encoded_size { max_encoded_size = #sizes };)*
|
||||
|
||||
max_encoded_size
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#![doc(html_logo_url = "https://gitlab.com/bjoernager/bzipper/-/raw/master/doc-icon.svg")]
|
||||
|
||||
//! This crate implements procedural macros for [`bZipper`](https://crates.io/crates/bzipper/).
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{Data, DeriveInput};
|
||||
|
||||
macro_rules! use_mod {
|
||||
($vis:vis $name:ident) => {
|
||||
mod $name;
|
||||
$vis use $name::*;
|
||||
};
|
||||
}
|
||||
pub(crate) use use_mod;
|
||||
|
||||
use_mod!(discriminant);
|
||||
use_mod!(discriminant_iter);
|
||||
use_mod!(generic_name);
|
||||
|
||||
mod impls;
|
||||
|
||||
#[proc_macro_derive(Decode)]
|
||||
pub fn derive_decode(input: TokenStream) -> TokenStream {
|
||||
let input = syn::parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let impl_body = match input.data {
|
||||
Data::Enum( ref data) => impls::decode_enum( data),
|
||||
Data::Struct(ref data) => impls::decode_struct(data),
|
||||
|
||||
Data::Union(..) => panic!("unions cannot derive `Decode`"),
|
||||
};
|
||||
|
||||
let ty_name = &input.ident;
|
||||
|
||||
let generic_params = &input.generics.params;
|
||||
let generic_where = &input.generics.where_clause;
|
||||
|
||||
let generic_names = GenericName::extract_from(&input.generics);
|
||||
|
||||
let output = quote! {
|
||||
impl<#generic_params> ::bzipper::Decode for #ty_name<#generic_names>
|
||||
#generic_where {
|
||||
#impl_body
|
||||
}
|
||||
};
|
||||
|
||||
//panic!("{output}");
|
||||
|
||||
output.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(Encode)]
|
||||
pub fn derive_encode(input: TokenStream) -> TokenStream {
|
||||
let input = syn::parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let impl_body = match input.data {
|
||||
Data::Enum( ref data) => impls::encode_enum( data),
|
||||
Data::Struct(ref data) => impls::encode_struct(data),
|
||||
|
||||
Data::Union(..) => panic!("unions cannot derive `Encode`"),
|
||||
};
|
||||
|
||||
let ty_name = &input.ident;
|
||||
|
||||
let generic_params = &input.generics.params;
|
||||
let generic_where = &input.generics.where_clause;
|
||||
|
||||
let generic_names = GenericName::extract_from(&input.generics);
|
||||
|
||||
let output = quote! {
|
||||
impl<#generic_params> ::bzipper::Encode for #ty_name<#generic_names>
|
||||
#generic_where {
|
||||
#impl_body
|
||||
}
|
||||
};
|
||||
|
||||
//panic!("{output}");
|
||||
|
||||
output.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(SizedEncode)]
|
||||
pub fn derive_sized_encode(input: TokenStream) -> TokenStream {
|
||||
let input = syn::parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let encode_impl_body = match input.data {
|
||||
Data::Enum( ref data) => impls::encode_enum( data),
|
||||
Data::Struct(ref data) => impls::encode_struct(data),
|
||||
|
||||
Data::Union(..) => panic!("unions can neither derive `Encode` nor `SizedEncode`"),
|
||||
};
|
||||
|
||||
let sized_encode_impl_body = match input.data {
|
||||
Data::Enum( ref data) => impls::sized_encode_enum( data),
|
||||
Data::Struct(ref data) => impls::sized_encode_struct(data),
|
||||
|
||||
Data::Union(..) => unreachable!(),
|
||||
};
|
||||
|
||||
let ty_name = &input.ident;
|
||||
|
||||
let generic_params = &input.generics.params;
|
||||
let generic_where = &input.generics.where_clause;
|
||||
|
||||
let generic_names = GenericName::extract_from(&input.generics);
|
||||
|
||||
let output = quote! {
|
||||
impl<#generic_params> ::bzipper::Encode for #ty_name<#generic_names>
|
||||
#generic_where {
|
||||
#encode_impl_body
|
||||
}
|
||||
|
||||
unsafe impl<#generic_params> ::bzipper::SizedEncode for #ty_name<#generic_names>
|
||||
#generic_where {
|
||||
#sized_encode_impl_body
|
||||
}
|
||||
};
|
||||
|
||||
//panic!("{output}");
|
||||
|
||||
output.into()
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
[package]
|
||||
name = "bzipper_benchmarks"
|
||||
version = "0.11.0"
|
||||
name = "librum-benchmarks"
|
||||
version = "0.12.0"
|
||||
edition = "2021"
|
||||
description = "bZipper benchmarks."
|
||||
description = "Librum benchmarks."
|
||||
|
||||
authors.workspace = true
|
||||
readme.workspace = true
|
||||
|
@ -10,11 +10,10 @@ homepage.workspace = true
|
|||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bzipper = { path = "../bzipper", version = "0.11.0" }
|
||||
librum = { path = "../librum", version = "0.12.0", features = ["proc-macro"]}
|
||||
|
||||
bincode = "1.3.3"
|
||||
ciborium = "0.2.2"
|
||||
rand = "0.8.5"
|
||||
bincode = "1.3.3"
|
||||
rand = "0.8.5"
|
||||
|
||||
borsh = { version = "1.5.1", features = ["derive"] }
|
||||
postcard = { version = "1.0.10", features = ["use-std"] }
|
|
@ -15,9 +15,7 @@ macro_rules! benchmark {
|
|||
|
||||
borsh: $borsh_op:block$(,)?
|
||||
|
||||
bzipper: $bzipper_op:block$(,)?
|
||||
|
||||
ciborium: $ciborium_op:block$(,)?
|
||||
librum: $librum_op:block$(,)?
|
||||
|
||||
postcard: $postcard_op:block$(,)?
|
||||
}$(,)?)+
|
||||
|
@ -53,8 +51,7 @@ macro_rules! benchmark {
|
|||
|
||||
let mut total_bincode_duration = 0.0;
|
||||
let mut total_borsh_duration = 0.0;
|
||||
let mut total_bzipper_duration = 0.0;
|
||||
let mut total_ciborium_duration = 0.0;
|
||||
let mut total_librum_duration = 0.0;
|
||||
let mut total_postcard_duration = 0.0;
|
||||
|
||||
$({
|
||||
|
@ -64,41 +61,37 @@ macro_rules! benchmark {
|
|||
|
||||
let bincode_duration = time! { $bincode_op };
|
||||
let borsh_duration = time! { $borsh_op };
|
||||
let bzipper_duration = time! { $bzipper_op };
|
||||
let ciborium_duration = time! { $ciborium_op };
|
||||
let librum_duration = time! { $librum_op };
|
||||
let postcard_duration = time! { $postcard_op };
|
||||
|
||||
eprint!("\u{001B}[000m");
|
||||
eprintln!("bincode: {}", format_score(bincode_duration, bzipper_duration));
|
||||
eprintln!("borsh: {}", format_score(borsh_duration, bzipper_duration));
|
||||
eprintln!("bzipper: {}", format_score(bzipper_duration, bzipper_duration));
|
||||
eprintln!("ciborium: {}", format_score(ciborium_duration, bzipper_duration));
|
||||
eprintln!("postcard: {}", format_score(postcard_duration, bzipper_duration));
|
||||
eprintln!("bincode: {}", format_score(bincode_duration, librum_duration));
|
||||
eprintln!("borsh: {}", format_score(borsh_duration, librum_duration));
|
||||
eprintln!("librum: {}", format_score(librum_duration, librum_duration));
|
||||
eprintln!("postcard: {}", format_score(postcard_duration, librum_duration));
|
||||
|
||||
total_bincode_duration += bincode_duration;
|
||||
total_borsh_duration += borsh_duration;
|
||||
total_bzipper_duration += bzipper_duration;
|
||||
total_ciborium_duration += ciborium_duration;
|
||||
total_librum_duration += librum_duration;
|
||||
total_postcard_duration += postcard_duration;
|
||||
})*
|
||||
|
||||
eprintln!();
|
||||
eprintln!("\u{001B}[001mtotal score:\u{001B}[022m");
|
||||
eprintln!("bincode: {}", format_score(total_bincode_duration, total_bzipper_duration));
|
||||
eprintln!("borsh: {}", format_score(total_borsh_duration, total_bzipper_duration));
|
||||
eprintln!("bzipper: {}", format_score(total_bzipper_duration, total_bzipper_duration));
|
||||
eprintln!("ciborium: {}", format_score(total_ciborium_duration, total_bzipper_duration));
|
||||
eprintln!("postcard: {}", format_score(total_postcard_duration, total_bzipper_duration));
|
||||
eprintln!("bincode: {}", format_score(total_bincode_duration, total_librum_duration));
|
||||
eprintln!("borsh: {}", format_score(total_borsh_duration, total_librum_duration));
|
||||
eprintln!("librum: {}", format_score(total_librum_duration, total_librum_duration));
|
||||
eprintln!("postcard: {}", format_score(total_postcard_duration, total_librum_duration));
|
||||
}};
|
||||
}
|
||||
|
||||
#[derive(bzipper::Decode, bzipper::SizedEncode)]
|
||||
#[derive(librum::Decode, librum::Encode, librum::SizedEncode)]
|
||||
#[derive(borsh::BorshSerialize)]
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[repr(transparent)]
|
||||
struct Unit;
|
||||
|
||||
#[derive(bzipper::Decode, bzipper::SizedEncode)]
|
||||
#[derive(librum::Decode, librum::Encode, librum::SizedEncode)]
|
||||
#[derive(borsh::BorshSerialize)]
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[repr(transparent)]
|
||||
|
@ -112,13 +105,13 @@ impl Unnamed {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(bzipper::Decode, bzipper::SizedEncode)]
|
||||
#[derive(librum::Decode, librum::Encode, librum::SizedEncode)]
|
||||
#[derive(borsh::BorshSerialize)]
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[repr(transparent)]
|
||||
struct Named { buf: [u8; 0x8] }
|
||||
|
||||
#[derive(bzipper::Decode, bzipper::SizedEncode)]
|
||||
#[derive(librum::Decode, librum::Encode, librum::SizedEncode)]
|
||||
#[derive(borsh::BorshSerialize)]
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
enum Enum {
|
||||
|
@ -147,16 +140,15 @@ impl Named {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
println!("######################");
|
||||
println!("# BZIPPER BENCHMARKS #");
|
||||
println!("######################");
|
||||
println!("#####################");
|
||||
println!("# LIBRUM BENCHMARKS #");
|
||||
println!("#####################");
|
||||
println!();
|
||||
println!("Each benchmark has a version written for the following crates:");
|
||||
println!();
|
||||
println!("- Bincode: <https://crates.io/crates/bincode/>");
|
||||
println!("- Borsh: <https://crates.io/crates/borsh/>");
|
||||
println!("- Ciborium: <https://crates.io/crates/ciborium/>");
|
||||
println!("- bzipper: <https://crates.io/crates/bzipper/>");
|
||||
println!("- Librum: <https://crates.io/crates/librum/>");
|
||||
println!("- Postcard: <https://crates.io/crates/postcard/>");
|
||||
println!();
|
||||
println!("The total time the benchmark took (including memory allocations and dealloca-");
|
||||
|
@ -166,10 +158,10 @@ fn main() {
|
|||
println!();
|
||||
println!("When every benchmark has concluded, the total run time and vps is listed for");
|
||||
println!("each crate. A percantage additionally compares the run time between the listed");
|
||||
println!("crate and bzipper (which should always be `0%` for bzipper itself). DO NOTE THAT");
|
||||
println!("THESE FINAL RESULTS INDICATE A NON-WEIGHTED AVERAGE ACROSS BENCHMARKS. It can");
|
||||
println!("therefore be skewed relative to real-world performance by the similarity of some");
|
||||
println!("benchmarks.");
|
||||
println!("crate and librum (which should always be c. `0%` for Librum itself). DO NOTE");
|
||||
println!("THAT THESE FINAL RESULTS INDICATE A NON-WEIGHTED AVERAGE ACROSS BENCHMARKS. It");
|
||||
println!("can therefore be skewed relative to real-world performance by the similarity of");
|
||||
println!("some benchmarks.");
|
||||
println!();
|
||||
|
||||
eprintln!("value_count: {VALUE_COUNT}");
|
||||
|
@ -180,11 +172,10 @@ fn main() {
|
|||
// Requires `std`.
|
||||
|
||||
use bincode::serialize_into;
|
||||
use std::io::Cursor;
|
||||
|
||||
let buf_size = size_of::<u8>(); // value
|
||||
let buf_size = size_of::<u8>();
|
||||
|
||||
let mut buf = Cursor::new(vec![0x00; buf_size * VALUE_COUNT]);
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
serialize_into(&mut buf, &random::<u8>()).unwrap();
|
||||
|
@ -192,21 +183,19 @@ fn main() {
|
|||
}
|
||||
|
||||
borsh: {
|
||||
use std::io::Cursor;
|
||||
let buf_size = size_of::<u8>();
|
||||
|
||||
let buf_size = size_of::<u8>(); // value
|
||||
|
||||
let mut buf = Cursor::new(vec![0x00; buf_size * VALUE_COUNT]);
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
borsh::to_writer(&mut buf, &random::<u8>()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
bzipper: {
|
||||
use bzipper::{Encode, OStream, SizedEncode};
|
||||
librum: {
|
||||
use librum::{Encode, OStream, SizedEncode};
|
||||
|
||||
let buf_size = u8::MAX_ENCODED_SIZE; // value
|
||||
let buf_size = u8::MAX_ENCODED_SIZE;
|
||||
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT].into_boxed_slice();
|
||||
let mut stream = OStream::new(&mut buf);
|
||||
|
@ -216,26 +205,10 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
ciborium: {
|
||||
use std::io::Cursor;
|
||||
|
||||
let buf_size =
|
||||
size_of::<u8>() // header
|
||||
+ size_of::<u8>(); // value
|
||||
|
||||
let mut buf = Cursor::new(vec![0x00; buf_size * VALUE_COUNT]);
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
ciborium::into_writer(&random::<u8>(), &mut buf).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
postcard: {
|
||||
use std::io::Cursor;
|
||||
let buf_size = size_of::<u8>();
|
||||
|
||||
let buf_size = size_of::<u8>(); // value
|
||||
|
||||
let mut buf = Cursor::new(vec![0x00; buf_size * VALUE_COUNT]);
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
postcard::to_io(&random::<u8>(), &mut buf).unwrap();
|
||||
|
@ -243,14 +216,107 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
encode_u32: {
|
||||
bincode: {
|
||||
use bincode::serialize_into;
|
||||
|
||||
let buf_size = size_of::<u32>();
|
||||
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
serialize_into(&mut buf, &random::<u32>()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
borsh: {
|
||||
let buf_size = size_of::<u32>();
|
||||
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
borsh::to_writer(&mut buf, &random::<u32>()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
librum: {
|
||||
use librum::{Encode, OStream, SizedEncode};
|
||||
|
||||
let buf_size = u32::MAX_ENCODED_SIZE;
|
||||
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT].into_boxed_slice();
|
||||
let mut stream = OStream::new(&mut buf);
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
random::<u32>().encode(&mut stream).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
postcard: {
|
||||
let buf_size = size_of::<u32>();
|
||||
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
postcard::to_io(&random::<u32>(), &mut buf).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
encode_u128: {
|
||||
bincode: {
|
||||
use bincode::serialize_into;
|
||||
|
||||
let buf_size = size_of::<u128>();
|
||||
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
serialize_into(&mut buf, &random::<u128>()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
borsh: {
|
||||
let buf_size = size_of::<u128>();
|
||||
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
borsh::to_writer(&mut buf, &random::<u128>()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
librum: {
|
||||
use librum::{Encode, OStream, SizedEncode};
|
||||
|
||||
let buf_size = u128::MAX_ENCODED_SIZE;
|
||||
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT].into_boxed_slice();
|
||||
let mut stream = OStream::new(&mut buf);
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
random::<u128>().encode(&mut stream).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
postcard: {
|
||||
let buf_size = size_of::<u128>();
|
||||
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
postcard::to_io(&random::<u128>(), &mut buf).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
encode_struct_unit: {
|
||||
bincode: {
|
||||
use bincode::serialize_into;
|
||||
use std::io::Cursor;
|
||||
|
||||
let buf_size = size_of::<Unit>(); // value
|
||||
let buf_size = size_of::<Unit>();
|
||||
|
||||
let mut buf = Cursor::new(vec![0x00; buf_size * VALUE_COUNT]);
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
serialize_into(&mut buf, &Unit).unwrap();
|
||||
|
@ -258,21 +324,19 @@ fn main() {
|
|||
}
|
||||
|
||||
borsh: {
|
||||
use std::io::Cursor;
|
||||
let buf_size = size_of::<Unit>();
|
||||
|
||||
let buf_size = size_of::<Unit>(); // value
|
||||
|
||||
let mut buf = Cursor::new(vec![0x00; buf_size * VALUE_COUNT].into_boxed_slice());
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
borsh::to_writer(&mut buf, &Unit).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
bzipper: {
|
||||
use bzipper::{Encode, OStream, SizedEncode};
|
||||
librum: {
|
||||
use librum::{Encode, OStream, SizedEncode};
|
||||
|
||||
let buf_size = Unit::MAX_ENCODED_SIZE; // value
|
||||
let buf_size = Unit::MAX_ENCODED_SIZE;
|
||||
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT].into_boxed_slice();
|
||||
let mut stream = OStream::new(&mut buf);
|
||||
|
@ -282,26 +346,10 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
ciborium: {
|
||||
use std::io::Cursor;
|
||||
|
||||
let buf_size =
|
||||
size_of::<u8>() // header
|
||||
+ size_of::<Unit>(); // value
|
||||
|
||||
let mut buf = Cursor::new(vec![0x00; buf_size * VALUE_COUNT].into_boxed_slice());
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
ciborium::into_writer(&Unit, &mut buf).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
postcard: {
|
||||
use std::io::Cursor;
|
||||
let buf_size = size_of::<Unit>();
|
||||
|
||||
let buf_size = size_of::<Unit>(); // value
|
||||
|
||||
let mut buf = Cursor::new(vec![0x00; buf_size * VALUE_COUNT].into_boxed_slice());
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
postcard::to_io(&Unit, &mut buf).unwrap();
|
||||
|
@ -312,11 +360,10 @@ fn main() {
|
|||
encode_struct_unnamed: {
|
||||
bincode: {
|
||||
use bincode::serialize_into;
|
||||
use std::io::Cursor;
|
||||
|
||||
let buf_size = size_of::<Unnamed>(); // value
|
||||
let buf_size = size_of::<Unnamed>();
|
||||
|
||||
let mut buf = Cursor::new(vec![0x00; buf_size * VALUE_COUNT]);
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
serialize_into(&mut buf, &Unnamed::from_char(random())).unwrap();
|
||||
|
@ -324,21 +371,19 @@ fn main() {
|
|||
}
|
||||
|
||||
borsh: {
|
||||
use std::io::Cursor;
|
||||
let buf_size = size_of::<Unnamed>();
|
||||
|
||||
let buf_size = size_of::<Unnamed>(); // value
|
||||
|
||||
let mut buf = Cursor::new(vec![0x00; buf_size * VALUE_COUNT].into_boxed_slice());
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
borsh::to_writer(&mut buf, &Unnamed::from_char(random())).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
bzipper: {
|
||||
use bzipper::{Encode, OStream, SizedEncode};
|
||||
librum: {
|
||||
use librum::{Encode, OStream, SizedEncode};
|
||||
|
||||
let buf_size = Unnamed::MAX_ENCODED_SIZE; // value
|
||||
let buf_size = Unnamed::MAX_ENCODED_SIZE;
|
||||
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT].into_boxed_slice();
|
||||
let mut stream = OStream::new(&mut buf);
|
||||
|
@ -348,26 +393,10 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
ciborium: {
|
||||
use std::io::Cursor;
|
||||
|
||||
let buf_size =
|
||||
size_of::<u8>() // header
|
||||
+ size_of::<Unnamed>(); // value
|
||||
|
||||
let mut buf = Cursor::new(vec![0x00; buf_size * VALUE_COUNT].into_boxed_slice());
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
ciborium::into_writer(&Unnamed::from_char(random()), &mut buf).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
postcard: {
|
||||
use std::io::Cursor;
|
||||
let buf_size = size_of::<Unnamed>();
|
||||
|
||||
let buf_size = size_of::<Unnamed>(); // value
|
||||
|
||||
let mut buf = Cursor::new(vec![0x00; buf_size * VALUE_COUNT].into_boxed_slice());
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
postcard::to_io(&Unnamed::from_char(random()), &mut buf).unwrap();
|
||||
|
@ -378,11 +407,10 @@ fn main() {
|
|||
encode_struct_named: {
|
||||
bincode: {
|
||||
use bincode::serialize_into;
|
||||
use std::io::Cursor;
|
||||
|
||||
let buf_size = size_of::<Named>(); // value
|
||||
let buf_size = size_of::<Named>();
|
||||
|
||||
let mut buf = Cursor::new(vec![0x00; buf_size * VALUE_COUNT]);
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
serialize_into(&mut buf, &Named::from_u64(random())).unwrap();
|
||||
|
@ -390,21 +418,19 @@ fn main() {
|
|||
}
|
||||
|
||||
borsh: {
|
||||
use std::io::Cursor;
|
||||
let buf_size = size_of::<Named>();
|
||||
|
||||
let buf_size = size_of::<Named>(); // value
|
||||
|
||||
let mut buf = Cursor::new(vec![0x00; buf_size * VALUE_COUNT].into_boxed_slice());
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
borsh::to_writer(&mut buf, &Named::from_u64(random())).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
bzipper: {
|
||||
use bzipper::{Encode, OStream, SizedEncode};
|
||||
librum: {
|
||||
use librum::{Encode, OStream, SizedEncode};
|
||||
|
||||
let buf_size = Named::MAX_ENCODED_SIZE; // value
|
||||
let buf_size = Named::MAX_ENCODED_SIZE;
|
||||
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT].into_boxed_slice();
|
||||
let mut stream = OStream::new(&mut buf);
|
||||
|
@ -414,28 +440,10 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
ciborium: {
|
||||
use std::io::Cursor;
|
||||
|
||||
let buf_size =
|
||||
size_of::<u8>() // header
|
||||
+ size_of::<u64>() // tag
|
||||
+ size_of::<u8>() // header
|
||||
+ size_of::<Named>(); // value
|
||||
|
||||
let mut buf = Cursor::new(vec![0x00; buf_size * VALUE_COUNT].into_boxed_slice());
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
ciborium::into_writer(&Named::from_u64(random()), &mut buf).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
postcard: {
|
||||
use std::io::Cursor;
|
||||
let buf_size = size_of::<Named>();
|
||||
|
||||
let buf_size = size_of::<Named>(); // value
|
||||
|
||||
let mut buf = Cursor::new(vec![0x00; buf_size * VALUE_COUNT].into_boxed_slice());
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
postcard::to_io(&Named::from_u64(random()), &mut buf).unwrap();
|
||||
|
@ -446,13 +454,12 @@ fn main() {
|
|||
encode_enum_unit: {
|
||||
bincode: {
|
||||
use bincode::serialize_into;
|
||||
use std::io::Cursor;
|
||||
|
||||
let buf_size =
|
||||
size_of::<u32>() // discriminant
|
||||
+ size_of::<Unit>(); // value
|
||||
size_of::<u32>() // discriminant
|
||||
+ size_of::<Unit>();
|
||||
|
||||
let mut buf = Cursor::new(vec![0x00; buf_size * VALUE_COUNT]);
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
serialize_into(&mut buf, &Enum::Unit(Unit)).unwrap();
|
||||
|
@ -460,25 +467,23 @@ fn main() {
|
|||
}
|
||||
|
||||
borsh: {
|
||||
use std::io::Cursor;
|
||||
|
||||
let buf_size =
|
||||
size_of::<u8>() // discriminant
|
||||
+ size_of::<Unit>(); // value
|
||||
size_of::<u8>() // discriminant
|
||||
+ size_of::<Unit>();
|
||||
|
||||
let mut buf = Cursor::new(vec![0x00; buf_size * VALUE_COUNT].into_boxed_slice());
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
borsh::to_writer(&mut buf, &Enum::Unit(Unit)).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
bzipper: {
|
||||
use bzipper::{Encode, OStream, SizedEncode};
|
||||
librum: {
|
||||
use librum::{Encode, OStream, SizedEncode};
|
||||
|
||||
let buf_size =
|
||||
isize::MAX_ENCODED_SIZE // discriminant
|
||||
+ Unit::MAX_ENCODED_SIZE; // value
|
||||
isize::MAX_ENCODED_SIZE // discriminant
|
||||
+ Unit::MAX_ENCODED_SIZE;
|
||||
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT].into_boxed_slice();
|
||||
let mut stream = OStream::new(&mut buf);
|
||||
|
@ -488,30 +493,12 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
ciborium: {
|
||||
use std::io::Cursor;
|
||||
|
||||
let buf_size =
|
||||
size_of::<u8>() // header
|
||||
+ size_of::<u64>() // tag (discriminant)
|
||||
+ size_of::<u8>() // header
|
||||
+ size_of::<Unit>(); // value
|
||||
|
||||
let mut buf = Cursor::new(vec![0x00; buf_size * VALUE_COUNT].into_boxed_slice());
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
ciborium::into_writer(&Enum::Unit(Unit), &mut buf).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
postcard: {
|
||||
use std::io::Cursor;
|
||||
|
||||
let buf_size =
|
||||
size_of::<u32>() // discriminant
|
||||
+ size_of::<Unit>(); // value
|
||||
size_of::<u32>() // discriminant
|
||||
+ size_of::<Unit>();
|
||||
|
||||
let mut buf = Cursor::new(vec![0x00; buf_size * VALUE_COUNT].into_boxed_slice());
|
||||
let mut buf = vec![0x00; buf_size * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
postcard::to_io(&Enum::Unit(Unit), &mut buf).unwrap();
|
|
@ -1,8 +1,8 @@
|
|||
[package]
|
||||
name = "bzipper_macros"
|
||||
version = "0.11.0"
|
||||
name = "librum-macros"
|
||||
version = "0.12.0"
|
||||
edition = "2021"
|
||||
documentation = "https://docs.rs/bzipper_macros/"
|
||||
documentation = "https://docs.rs/librum-macros/"
|
||||
|
||||
authors.workspace = true
|
||||
description.workspace = true
|
|
@ -1,35 +1,34 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::Discriminant;
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use syn::Variant;
|
||||
use proc_macro2::Span;
|
||||
use syn::{Expr, Lit, LitInt, Variant};
|
||||
|
||||
pub struct DiscriminantIter<I: IntoIterator<Item: Borrow<Variant>>> {
|
||||
pub struct Discriminants<I: IntoIterator<Item: Borrow<Variant>>> {
|
||||
variants: I::IntoIter,
|
||||
prev: Option<Discriminant>,
|
||||
prev: Option<u128>,
|
||||
}
|
||||
|
||||
impl<I: IntoIterator<Item: Borrow<Variant>>> DiscriminantIter<I> {
|
||||
impl<I: IntoIterator<Item: Borrow<Variant>>> Discriminants<I> {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn new(variants: I) -> Self {
|
||||
|
@ -40,28 +39,43 @@ impl<I: IntoIterator<Item: Borrow<Variant>>> DiscriminantIter<I> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<I: IntoIterator<Item: Borrow<Variant>>> Iterator for DiscriminantIter<I> {
|
||||
type Item = (Discriminant, I::Item);
|
||||
impl<I: IntoIterator<Item: Borrow<Variant>>> Iterator for Discriminants<I> {
|
||||
type Item = LitInt;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let variant = self.variants.next()?;
|
||||
|
||||
let discriminant = if let Some((_, ref discriminant)) = variant.borrow().discriminant {
|
||||
Discriminant::parse(discriminant)
|
||||
} else if let Some(discriminant) = self.prev {
|
||||
let value = discriminant.0
|
||||
.checked_add(0x1)
|
||||
.unwrap_or_else(|| panic!("overflow following discriminant `{discriminant:?}`"));
|
||||
let discriminant = if let Some((_, ref expr)) = variant.borrow().discriminant {
|
||||
let Expr::Lit(ref expr) = *expr else {
|
||||
panic!("expected literal expression for discriminant value");
|
||||
};
|
||||
|
||||
Discriminant(value)
|
||||
let Lit::Int(ref expr) = expr.lit else {
|
||||
panic!("expected (potentially signed) integer literal for discriminant value`");
|
||||
};
|
||||
|
||||
let expr = expr.base10_digits();
|
||||
|
||||
let value: u128 = expr
|
||||
.parse()
|
||||
.or_else(|_| expr.parse::<i128>().map(|v| v as u128))
|
||||
.unwrap();
|
||||
|
||||
value
|
||||
} else if let Some(prev) = self.prev {
|
||||
prev
|
||||
.checked_add(0x1)
|
||||
.unwrap_or_else(|| panic!("overflow following discriminant `{prev:?}`"))
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
|
||||
self.prev = Some(discriminant);
|
||||
|
||||
Some((discriminant, variant))
|
||||
let discriminant = LitInt::new(&discriminant.to_string(), Span::call_site());
|
||||
|
||||
Some(discriminant)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
|
@ -1,22 +1,22 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use proc_macro2::TokenStream;
|
80
librum-macros/src/impl_derive_macro.rs
Normal file
80
librum-macros/src/impl_derive_macro.rs
Normal file
|
@ -0,0 +1,80 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{GenericName, Repr};
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
Data,
|
||||
DataEnum,
|
||||
DataStruct,
|
||||
DeriveInput,
|
||||
Path,
|
||||
Token,
|
||||
};
|
||||
|
||||
pub fn impl_derive_macro<S, E>(
|
||||
input: DeriveInput,
|
||||
trait_path: Path,
|
||||
r#unsafe_token: Option<Token![unsafe]>,
|
||||
struct_body: S,
|
||||
enum_body: E,
|
||||
) -> TokenStream
|
||||
where
|
||||
S: FnOnce(DataStruct) -> TokenStream,
|
||||
E: FnOnce(DataEnum, Repr) -> TokenStream,
|
||||
{
|
||||
let trait_name = &trait_path
|
||||
.segments
|
||||
.last()
|
||||
.expect("expected non-empty path for derived trait")
|
||||
.ident;
|
||||
|
||||
let self_name = &input.ident;
|
||||
|
||||
let body = match input.data {
|
||||
Data::Struct(data) => struct_body(data),
|
||||
|
||||
Data::Enum(data) => {
|
||||
let repr = Repr::get(&input.attrs).unwrap_or_default();
|
||||
|
||||
enum_body(data, repr)
|
||||
}
|
||||
|
||||
Data::Union(..) => panic!("unions cannot derive `{trait_name:?}`"),
|
||||
};
|
||||
|
||||
let generic_params = &input.generics.params;
|
||||
let generic_where = &input.generics.where_clause;
|
||||
|
||||
let generic_names = GenericName::extract_from(&input.generics);
|
||||
|
||||
let output = quote! {
|
||||
#unsafe_token impl<#generic_params> #trait_path for #self_name<#generic_names>
|
||||
#generic_where
|
||||
{
|
||||
#body
|
||||
}
|
||||
};
|
||||
|
||||
output
|
||||
}
|
82
librum-macros/src/impls/decode_enum.rs
Normal file
82
librum-macros/src/impls/decode_enum.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{Discriminants, Repr};
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use std::iter;
|
||||
use syn::{DataEnum, Fields};
|
||||
|
||||
#[must_use]
|
||||
pub fn decode_enum(data: DataEnum, repr: Repr) -> TokenStream {
|
||||
let discriminants: Vec<_> = Discriminants::new(&data.variants).collect();
|
||||
|
||||
let values = data
|
||||
.variants
|
||||
.into_iter()
|
||||
.map(|variant| {
|
||||
let variant_name = variant.ident;
|
||||
|
||||
let commands = iter::repeat_n(
|
||||
quote! {
|
||||
::librum::Decode::decode(stream)
|
||||
.map_err(::core::convert::Into::<::librum::error::GenericDecodeError>::into)
|
||||
.map_err(::librum::error::EnumDecodeError::Field)?
|
||||
},
|
||||
variant.fields.len(),
|
||||
);
|
||||
|
||||
match variant.fields {
|
||||
Fields::Unit => quote! { Self::#variant_name },
|
||||
|
||||
Fields::Unnamed(_fields) => quote! { Self::#variant_name (#(#commands, )*) },
|
||||
|
||||
Fields::Named(fields) => {
|
||||
let field_names = fields
|
||||
.named
|
||||
.into_iter()
|
||||
.map(|field| field.ident.unwrap());
|
||||
|
||||
quote! { Self::#variant_name { #(#field_names: #commands, )* } }
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
type Error = ::librum::error::EnumDecodeError<#repr, ::librum::error::GenericDecodeError>;
|
||||
|
||||
#[inline]
|
||||
fn decode(stream: &mut ::librum::IStream) -> ::core::result::Result<Self, Self::Error> {
|
||||
let discriminant = <#repr as ::librum::Decode>::decode(stream)
|
||||
.map_err(::core::convert::Into::<::core::convert::Infallible>::into)
|
||||
.map_err(::librum::error::EnumDecodeError::InvalidDiscriminant)?;
|
||||
|
||||
let this = match discriminant {
|
||||
#(#discriminants => #values,)*
|
||||
|
||||
value => return ::core::result::Result::Err(::librum::error::EnumDecodeError::UnassignedDiscriminant { value }),
|
||||
};
|
||||
|
||||
::core::result::Result::Ok(this)
|
||||
}
|
||||
}
|
||||
}
|
61
librum-macros/src/impls/decode_struct.rs
Normal file
61
librum-macros/src/impls/decode_struct.rs
Normal file
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{DataStruct, Fields};
|
||||
use std::iter;
|
||||
|
||||
#[must_use]
|
||||
pub fn decode_struct(data: DataStruct) -> TokenStream {
|
||||
let commands = iter::repeat_n(
|
||||
quote! {
|
||||
::librum::Decode::decode(stream)
|
||||
.map_err(::core::convert::Into::<::librum::error::GenericDecodeError>::into)?
|
||||
},
|
||||
data.fields.len(),
|
||||
);
|
||||
|
||||
let value = match data.fields {
|
||||
Fields::Unit => quote! { Self },
|
||||
|
||||
Fields::Unnamed(_fields) => quote! { Self (#(#commands, )*) },
|
||||
|
||||
Fields::Named(fields) => {
|
||||
let field_names = fields
|
||||
.named
|
||||
.into_iter()
|
||||
.map(|field| field.ident.unwrap());
|
||||
|
||||
quote! { Self { #(#field_names: #commands, )* } }
|
||||
},
|
||||
};
|
||||
|
||||
quote! {
|
||||
type Error = ::librum::error::GenericDecodeError;
|
||||
|
||||
#[inline]
|
||||
fn decode(stream: &mut ::librum::IStream) -> ::core::result::Result<Self, Self::Error> {
|
||||
let this = #value;
|
||||
::core::result::Result::Ok(this)
|
||||
}
|
||||
}
|
||||
}
|
94
librum-macros/src/impls/encode_enum.rs
Normal file
94
librum-macros/src/impls/encode_enum.rs
Normal file
|
@ -0,0 +1,94 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{Discriminants, Repr};
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::{DataEnum, Fields, Ident, LitInt};
|
||||
|
||||
#[must_use]
|
||||
pub fn encode_enum(data: DataEnum, repr: Repr) -> TokenStream {
|
||||
let discriminants: Vec<LitInt> = Discriminants::new(&data.variants).collect();
|
||||
|
||||
let captures: Vec<Vec<Ident>> = data
|
||||
.variants
|
||||
.iter()
|
||||
.map(|variant| {
|
||||
variant
|
||||
.fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, _)| Ident::new(&format!("value{index}"), Span::call_site()))
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let patterns = data
|
||||
.variants
|
||||
.into_iter()
|
||||
.zip(&captures)
|
||||
.map(|(variant, captures)| {
|
||||
let variant_name = variant.ident;
|
||||
|
||||
match variant.fields {
|
||||
Fields::Unit => quote! { Self::#variant_name },
|
||||
|
||||
Fields::Unnamed(_fields) => quote! { Self::#variant_name (#(ref #captures, )*) },
|
||||
|
||||
Fields::Named(fields) => {
|
||||
let field_names = fields
|
||||
.named
|
||||
.into_iter()
|
||||
.map(|field| field.ident.unwrap());
|
||||
|
||||
quote! { Self::#variant_name { #(#field_names: ref #captures, )* } }
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
type Error = ::librum::error::EnumEncodeError<#repr, ::librum::error::GenericEncodeError>;
|
||||
|
||||
#[allow(unreachable_patterns)]
|
||||
#[inline]
|
||||
fn encode(&self, stream: &mut ::librum::OStream) -> ::core::result::Result<(), Self::Error> {
|
||||
match *self {
|
||||
#(
|
||||
#patterns => {
|
||||
<#repr as ::librum::Encode>::encode(&#discriminants, stream)
|
||||
.map_err(::librum::error::EnumEncodeError::Discriminant)?;
|
||||
|
||||
#(
|
||||
::librum::Encode::encode(#captures, stream)
|
||||
.map_err(::core::convert::Into::<::librum::error::GenericEncodeError>::into)
|
||||
.map_err(::librum::error::EnumEncodeError::Field)?;
|
||||
)*
|
||||
}
|
||||
)*
|
||||
|
||||
_ => ::core::unreachable!("no variants defined for this enumeration"),
|
||||
}
|
||||
|
||||
::core::result::Result::Ok(())
|
||||
}
|
||||
}
|
||||
}
|
65
librum-macros/src/impls/encode_struct.rs
Normal file
65
librum-macros/src/impls/encode_struct.rs
Normal file
|
@ -0,0 +1,65 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::{DataStruct, Fields, Ident};
|
||||
|
||||
#[must_use]
|
||||
pub fn encode_struct(data: DataStruct) -> TokenStream {
|
||||
let captures: Vec<_> = data
|
||||
.fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, _)| Ident::new(&format!("value{index}"), Span::call_site()))
|
||||
.collect();
|
||||
|
||||
let pattern = match data.fields {
|
||||
Fields::Unit => quote! { Self },
|
||||
|
||||
Fields::Unnamed(_fields) => quote! { Self(#(ref #captures, )*) },
|
||||
|
||||
Fields::Named(fields) => {
|
||||
let field_names = fields
|
||||
.named
|
||||
.into_iter()
|
||||
.map(|field| field.ident.unwrap());
|
||||
|
||||
quote! { Self { #(#field_names: ref #captures, )* } }
|
||||
},
|
||||
};
|
||||
|
||||
quote! {
|
||||
type Error = ::librum::error::GenericEncodeError;
|
||||
|
||||
#[inline]
|
||||
fn encode(&self, stream: &mut ::librum::OStream) -> ::core::result::Result<(), Self::Error> {
|
||||
let #pattern = self;
|
||||
|
||||
#(
|
||||
::librum::Encode::encode(#captures, stream)
|
||||
.map_err(::core::convert::Into::<::librum::error::GenericEncodeError>::into)?;
|
||||
)*
|
||||
|
||||
::core::result::Result::Ok(())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +1,22 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::use_mod;
|
59
librum-macros/src/impls/sized_encode_enum.rs
Normal file
59
librum-macros/src/impls/sized_encode_enum.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::Repr;
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::quote;
|
||||
use std::iter;
|
||||
use syn::DataEnum;
|
||||
|
||||
#[must_use]
|
||||
pub fn sized_encode_enum(data: DataEnum, repr: Repr) -> TokenStream {
|
||||
let tys: Vec<Vec<_>> = data
|
||||
.variants
|
||||
.iter()
|
||||
.map(|variant| {
|
||||
variant
|
||||
.fields
|
||||
.iter()
|
||||
.map(|field| field.ty.clone())
|
||||
.chain(iter::once(repr.to_type(Span::call_site())))
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
|
||||
quote! {
|
||||
const MAX_ENCODED_SIZE: usize = {
|
||||
let mut total_size = 0x0usize;
|
||||
|
||||
let mut current_size = 0x0usize;
|
||||
|
||||
#(
|
||||
current_size = 0x0 #(+ <#tys as ::librum::SizedEncode>::MAX_ENCODED_SIZE)*;
|
||||
|
||||
if current_size > total_size { total_size = current_size };
|
||||
)*
|
||||
|
||||
total_size
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,22 +1,22 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
|
@ -24,14 +24,13 @@ use quote::quote;
|
|||
use syn::DataStruct;
|
||||
|
||||
#[must_use]
|
||||
pub fn sized_encode_struct(data: &DataStruct) -> TokenStream {
|
||||
let mut field_tys = Vec::new();
|
||||
|
||||
for field in &data.fields {
|
||||
field_tys.push(&field.ty);
|
||||
}
|
||||
pub fn sized_encode_struct(data: DataStruct) -> TokenStream {
|
||||
let tys: Vec<_> = data.fields
|
||||
.into_iter()
|
||||
.map(|field| field.ty)
|
||||
.collect();
|
||||
|
||||
quote! {
|
||||
const MAX_ENCODED_SIZE: usize = 0x0 #( + <#field_tys as ::bzipper::SizedEncode>::MAX_ENCODED_SIZE)*;
|
||||
const MAX_ENCODED_SIZE: usize = 0x0 #( + <#tys as ::librum::SizedEncode>::MAX_ENCODED_SIZE)*;
|
||||
}
|
||||
}
|
97
librum-macros/src/lib.rs
Normal file
97
librum-macros/src/lib.rs
Normal file
|
@ -0,0 +1,97 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#![doc(html_logo_url = "https://gitlab.com/bjoernager/librum/-/raw/master/doc-icon.svg")]
|
||||
|
||||
//! This crate implements procedural macros for [`Librum`](https://crates.io/crates/librum/).
|
||||
|
||||
// For use in macros:
|
||||
extern crate self as librum_macros;
|
||||
|
||||
macro_rules! use_mod {
|
||||
($vis:vis $name:ident) => {
|
||||
mod $name;
|
||||
$vis use $name::*;
|
||||
};
|
||||
}
|
||||
pub(crate) use use_mod;
|
||||
|
||||
use_mod!(discriminants);
|
||||
use_mod!(generic_name);
|
||||
use_mod!(impl_derive_macro);
|
||||
use_mod!(repr);
|
||||
|
||||
mod impls;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{DeriveInput, parse2};
|
||||
|
||||
#[proc_macro_derive(Decode)]
|
||||
pub fn derive_decode(input: TokenStream) -> TokenStream {
|
||||
let input = syn::parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let output = impl_derive_macro(
|
||||
input,
|
||||
parse2(quote! { ::librum::Decode }).unwrap(),
|
||||
None,
|
||||
impls::decode_struct,
|
||||
impls::decode_enum,
|
||||
);
|
||||
|
||||
//panic!("{output}");
|
||||
|
||||
output.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(Encode)]
|
||||
pub fn derive_encode(input: TokenStream) -> TokenStream {
|
||||
let input = syn::parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let output = impl_derive_macro(
|
||||
input,
|
||||
parse2(quote! { ::librum::Encode }).unwrap(),
|
||||
None,
|
||||
impls::encode_struct,
|
||||
impls::encode_enum,
|
||||
);
|
||||
|
||||
//panic!("{output}");
|
||||
|
||||
output.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(SizedEncode)]
|
||||
pub fn derive_sized_encode(input: TokenStream) -> TokenStream {
|
||||
let input = syn::parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let output = impl_derive_macro(
|
||||
input,
|
||||
parse2(quote! { ::librum::SizedEncode }).unwrap(),
|
||||
None,
|
||||
impls::sized_encode_struct,
|
||||
impls::sized_encode_enum,
|
||||
);
|
||||
|
||||
//panic!("{output}");
|
||||
|
||||
output.into()
|
||||
}
|
157
librum-macros/src/repr/mod.rs
Normal file
157
librum-macros/src/repr/mod.rs
Normal file
|
@ -0,0 +1,157 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::ToTokens;
|
||||
use std::iter;
|
||||
use syn::{
|
||||
Attribute,
|
||||
Ident,
|
||||
Path,
|
||||
PathSegment,
|
||||
Type,
|
||||
TypePath,
|
||||
};
|
||||
|
||||
/// A derivable enumeration representation.
|
||||
///
|
||||
/// Any type can, *in theory*, be used as a discriminant.
|
||||
/// This type, however, only includes primitives.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[repr(u8)]
|
||||
pub enum Repr {
|
||||
U8,
|
||||
I8,
|
||||
U16,
|
||||
I16,
|
||||
U32,
|
||||
I32,
|
||||
U64,
|
||||
I64,
|
||||
U128,
|
||||
I128,
|
||||
Usize,
|
||||
Isize,
|
||||
}
|
||||
|
||||
impl Repr {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn get(attrs: &[Attribute]) -> Option<Self> {
|
||||
let mut this = None;
|
||||
|
||||
for attr in attrs {
|
||||
if attr.path().is_ident("repr") {
|
||||
attr.parse_nested_meta(|meta| {
|
||||
use Repr::*;
|
||||
|
||||
let ident = meta.path.require_ident()?;
|
||||
|
||||
if ident == "u8" { this = Some(U8) }
|
||||
else if ident == "i8" { this = Some(I8) }
|
||||
else if ident == "u16" { this = Some(U16) }
|
||||
else if ident == "i16" { this = Some(I16) }
|
||||
else if ident == "u32" { this = Some(U32) }
|
||||
else if ident == "i32" { this = Some(I32) }
|
||||
else if ident == "u64" { this = Some(U64) }
|
||||
else if ident == "i64" { this = Some(I64) }
|
||||
else if ident == "u128" { this = Some(U128) }
|
||||
else if ident == "i128" { this = Some(I128) }
|
||||
else if ident == "usize" { this = Some(Usize) }
|
||||
else if ident == "isize" { this = Some(Isize) }
|
||||
else { panic!("`{ident}` is not a derivable enumeration representation") };
|
||||
|
||||
Ok(())
|
||||
}).unwrap();
|
||||
}
|
||||
|
||||
// Ignore all other attributes.
|
||||
}
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn to_str(self) -> &'static str {
|
||||
use Repr::*;
|
||||
|
||||
match self {
|
||||
U8 => "u8",
|
||||
I8 => "i8",
|
||||
U16 => "u16",
|
||||
I16 => "i16",
|
||||
U32 => "u32",
|
||||
I32 => "i32",
|
||||
U64 => "u64",
|
||||
I64 => "i64",
|
||||
U128 => "u128",
|
||||
I128 => "i128",
|
||||
Usize => "usize",
|
||||
Isize => "isize",
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn to_ident(self, span: Span) -> Ident {
|
||||
let ident = self.to_str();
|
||||
|
||||
Ident::new(ident, span)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn to_path(self, span: Span) -> Path {
|
||||
let ident = self.to_ident(span);
|
||||
|
||||
Path {
|
||||
leading_colon: None,
|
||||
segments: iter::once(PathSegment {
|
||||
ident,
|
||||
arguments: Default::default(),
|
||||
}).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn to_type(self, span: Span) -> Type {
|
||||
Type::Path(TypePath {
|
||||
qself: None,
|
||||
path: self.to_path(span),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Repr {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
Self::Isize
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Repr {
|
||||
#[inline(always)]
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
self.to_ident(Span::call_site()).to_tokens(tokens);
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 432 B After Width: | Height: | Size: 432 B |
|
@ -1,9 +1,9 @@
|
|||
[package]
|
||||
name = "bzipper"
|
||||
version = "0.11.0"
|
||||
name = "librum"
|
||||
version = "0.12.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.83"
|
||||
documentation = "https://docs.rs/bzipper/"
|
||||
documentation = "https://docs.rs/librum/"
|
||||
|
||||
authors.workspace = true
|
||||
description.workspace = true
|
||||
|
@ -18,13 +18,14 @@ categories.workspace = true
|
|||
all-features = true
|
||||
|
||||
[features]
|
||||
default = ["alloc", "std"]
|
||||
default = ["alloc", "proc-macro", "std"]
|
||||
|
||||
alloc = []
|
||||
std = []
|
||||
alloc = []
|
||||
proc-macro = ["librum-macros"]
|
||||
std = []
|
||||
|
||||
[dependencies]
|
||||
bzipper_macros = { path = "../bzipper_macros", version = "0.11.0" }
|
||||
librum-macros = { path = "../librum-macros", version = "0.12.0", optional = true}
|
||||
|
||||
[lints]
|
||||
workspace = true
|
|
@ -1,26 +1,26 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
mod tests;
|
||||
|
||||
use crate::{
|
||||
Decode,
|
||||
|
@ -29,7 +29,6 @@ use crate::{
|
|||
OStream,
|
||||
SizedEncode,
|
||||
};
|
||||
use crate::error::{DecodeError, EncodeError};
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec;
|
||||
|
@ -52,9 +51,15 @@ use core::slice::{self, SliceIndex};
|
|||
/// Create a buffer for holding a `Request` enumeration:
|
||||
///
|
||||
/// ```
|
||||
/// use bzipper::{Buf, SizedEncode, SizedStr};
|
||||
/// use librum::{
|
||||
/// Buf,
|
||||
/// Encode,
|
||||
/// OStream,
|
||||
/// SizedEncode,
|
||||
/// SizedStr,
|
||||
/// };
|
||||
///
|
||||
/// #[derive(SizedEncode)]
|
||||
/// #[derive(Debug, Encode, SizedEncode)]
|
||||
/// enum Request {
|
||||
/// Join { username: SizedStr<0x40> },
|
||||
///
|
||||
|
@ -264,13 +269,13 @@ impl<T> Buf<T> {
|
|||
impl<T: Encode> Buf<T> {
|
||||
/// Encodes an object into the buffer.
|
||||
///
|
||||
/// The object is encoded as by being passed to <code><T as [Encode]>::[encode](Encode::encode)</code>.
|
||||
/// The object is encoded as by being passed to <code><T as [Encode]>::[encode](Encode::encode)</code>.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Any error that occurs during encoding is passed on and returned from this method.
|
||||
#[inline]
|
||||
pub fn write<U: Borrow<T>>(&mut self, value: U) -> Result<(), EncodeError> {
|
||||
pub fn write<U: Borrow<T>>(&mut self, value: U) -> Result<(), T::Error> {
|
||||
let mut stream = OStream::new(&mut self.buf);
|
||||
|
||||
value.borrow().encode(&mut stream)?;
|
||||
|
@ -285,7 +290,7 @@ impl<T: Encode> Buf<T> {
|
|||
impl<T: Decode> Buf<T> {
|
||||
/// Decodes an object from the buffer.
|
||||
///
|
||||
/// This is done as by passing the contained bytes to <code><T as [Decode]>::[decode](Decode::decode)</code>.
|
||||
/// This is done as by passing the contained bytes to <code><T as [Decode]>::[decode](Decode::decode)</code>.
|
||||
///
|
||||
/// Note that only the bytes specified by [`len`](Self::len) are passed in this call.
|
||||
/// See [`as_slice`](Self::as_slice) for more information.
|
||||
|
@ -294,7 +299,7 @@ impl<T: Decode> Buf<T> {
|
|||
///
|
||||
/// Any error that occurs during decoding is passed on and returned from this method.
|
||||
#[inline]
|
||||
pub fn read(&self) -> Result<T, DecodeError> {
|
||||
pub fn read(&self) -> Result<T, T::Error> {
|
||||
// We should only pass the used part of the buffer
|
||||
// to `deserialise`.
|
||||
|
|
@ -1,26 +1,26 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use bzipper::Buf;
|
||||
use bzipper::error::DecodeError;
|
||||
use librum::Buf;
|
||||
use librum::error::CharDecodeError;
|
||||
|
||||
#[test]
|
||||
fn test_buf_write_read() {
|
||||
|
@ -40,7 +40,7 @@ fn test_buf_write_read() {
|
|||
assert_eq!(buf, [0x00, 0x01, 0xF4, 0x4D].as_slice());
|
||||
|
||||
buf.copy_from_slice(&[0x00, 0x00, 0xD8, 0x00]);
|
||||
test_read!(Err(DecodeError::InvalidCodePoint(0xD800)));
|
||||
test_read!(Err(CharDecodeError { code_point: 0xD800 }));
|
||||
|
||||
buf.copy_from_slice(&[0x00, 0x00, 0xFF, 0x3A]);
|
||||
test_read!(Ok('\u{FF3A}'));
|
|
@ -1,29 +1,38 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
mod tests;
|
||||
|
||||
use crate::{IStream, SizedEncode};
|
||||
use crate::error::{DecodeError, Utf8Error};
|
||||
use crate::{DecodeBorrowed, IStream, SizedEncode};
|
||||
use crate::error::{
|
||||
BoolDecodeError,
|
||||
CStringDecodeError,
|
||||
CharDecodeError,
|
||||
CollectionDecodeError,
|
||||
EnumDecodeError,
|
||||
ItemDecodeError,
|
||||
SystemTimeDecodeError,
|
||||
Utf8Error,
|
||||
};
|
||||
|
||||
use core::cell::{Cell, RefCell};
|
||||
use core::convert::Infallible;
|
||||
|
@ -52,6 +61,9 @@ use core::ptr::copy_nonoverlapping;
|
|||
use core::str;
|
||||
use core::time::Duration;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::borrow::{Cow, ToOwned};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::boxed::Box;
|
||||
|
||||
|
@ -70,7 +82,7 @@ use alloc::vec::Vec;
|
|||
#[cfg(feature = "alloc")]
|
||||
use alloc::rc::Rc;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
|
||||
use alloc::sync::Arc;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
|
@ -89,41 +101,47 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
|||
|
||||
/// Denotes a type capable of being decoded.
|
||||
pub trait Decode: Sized {
|
||||
type Error;
|
||||
|
||||
/// Decodes an object from the provided stream.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If decoding fails due to e.g. an invalid byte sequence in the stream, then an error should be returned.
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError>;
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error>;
|
||||
}
|
||||
|
||||
/// Implemented for tuples with up to twelve members.
|
||||
#[cfg_attr(doc, doc(fake_variadic))]
|
||||
impl<T> Decode for (T, )
|
||||
where
|
||||
T: Decode, {
|
||||
impl<T: Decode> Decode for (T, ) {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let value = (Decode::decode(stream)?, );
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decode, const N: usize> Decode for [T; N] {
|
||||
type Error = CollectionDecodeError<Infallible, ItemDecodeError<usize, T::Error>>;
|
||||
|
||||
#[inline]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
// Initialise the array incrementally.
|
||||
|
||||
// SAFETY: Always safe.
|
||||
let mut buf: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
|
||||
|
||||
for item in &mut buf {
|
||||
let value = Decode::decode(stream)?;
|
||||
for (i, item) in buf.iter_mut().enumerate() {
|
||||
let value = Decode::decode(stream)
|
||||
.map_err(|e| CollectionDecodeError::Item(ItemDecodeError { index: i, error: e }))?;
|
||||
|
||||
item.write(value);
|
||||
}
|
||||
|
||||
// This should be safe as `MaybeUninit<T>` is
|
||||
// transparent to `T`, and we have initialised
|
||||
// SAFETY: This should be safe as `MaybeUninit<T>`
|
||||
// is transparent to `T` and we have initialised
|
||||
// every element. The original buffer is NOT
|
||||
// dropped automatically, so we can just forget
|
||||
// about it from this point on. `transmute` cannot
|
||||
|
@ -134,47 +152,60 @@ impl<T: Decode, const N: usize> Decode for [T; N] {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
|
||||
#[cfg_attr(doc, doc(cfg(all(feature = "alloc", target_has_atomic = "ptr"))))]
|
||||
impl<T: Decode> Decode for Arc<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
Ok(Self::new(Decode::decode(stream)?))
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let value = Decode::decode(stream)?;
|
||||
|
||||
let this = Self::new(value);
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for bool {
|
||||
type Error = BoolDecodeError;
|
||||
|
||||
#[inline]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
let value = u8::decode(stream)?;
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let value = u8::decode(stream).unwrap();
|
||||
|
||||
match value {
|
||||
0x0 => Ok(false),
|
||||
0x1 => Ok(true),
|
||||
_ => Err(DecodeError::InvalidBoolean(value))
|
||||
_ => Err(BoolDecodeError { value })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decode> Decode for Bound<T> {
|
||||
type Error = EnumDecodeError<u8, T::Error>;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
let discriminant = u8::decode(stream)?;
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let discriminant = u8::decode(stream).unwrap();
|
||||
|
||||
let this = match discriminant {
|
||||
0x0 => {
|
||||
let bound = Decode::decode(stream)?;
|
||||
let bound = Decode::decode(stream)
|
||||
.map_err(EnumDecodeError::Field)?;
|
||||
|
||||
Self::Included(bound)
|
||||
}
|
||||
|
||||
0x1 => {
|
||||
let bound = Decode::decode(stream)?;
|
||||
let bound = Decode::decode(stream)
|
||||
.map_err(EnumDecodeError::Field)?;
|
||||
|
||||
Self::Excluded(bound)
|
||||
}
|
||||
|
||||
0x2 => Self::Unbounded,
|
||||
|
||||
_ => return Err(DecodeError::InvalidDiscriminant(discriminant.into())),
|
||||
value => return Err(EnumDecodeError::UnassignedDiscriminant { value }),
|
||||
};
|
||||
|
||||
Ok(this)
|
||||
|
@ -184,8 +215,10 @@ impl<T: Decode> Decode for Bound<T> {
|
|||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: Decode> Decode for Box<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let value = Decode::decode(stream)?;
|
||||
|
||||
let this = Self::new(value);
|
||||
|
@ -194,8 +227,10 @@ impl<T: Decode> Decode for Box<T> {
|
|||
}
|
||||
|
||||
impl<T: Decode> Decode for Cell<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let value = Decode::decode(stream)?;
|
||||
|
||||
let this = Self::new(value);
|
||||
|
@ -204,13 +239,15 @@ impl<T: Decode> Decode for Cell<T> {
|
|||
}
|
||||
|
||||
impl Decode for char {
|
||||
#[inline]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
let value = u32::decode(stream)?;
|
||||
type Error = CharDecodeError;
|
||||
|
||||
let this = value
|
||||
#[inline]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let code_point = u32::decode(stream).unwrap();
|
||||
|
||||
let this = code_point
|
||||
.try_into()
|
||||
.map_err(|_| DecodeError::InvalidCodePoint(value))?;
|
||||
.map_err(|_| CharDecodeError { code_point })?;
|
||||
|
||||
Ok(this)
|
||||
}
|
||||
|
@ -218,15 +255,35 @@ impl Decode for char {
|
|||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl Decode for CString {
|
||||
impl<T, B> Decode for Cow<'_, B>
|
||||
where
|
||||
T: DecodeBorrowed<B>,
|
||||
B: ToOwned<Owned = T>,
|
||||
{
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
let len = Decode::decode(stream)?;
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let value = Decode::decode(stream)?;
|
||||
|
||||
let this = Self::Owned(value);
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl Decode for CString {
|
||||
type Error = CStringDecodeError;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let len = Decode::decode(stream).unwrap();
|
||||
|
||||
let data = stream.read(len);
|
||||
|
||||
for (i, c) in data.iter().enumerate() {
|
||||
if *c == b'\x00' { return Err(DecodeError::NullCString { index: i }) };
|
||||
if *c == b'\x00' { return Err(CStringDecodeError { index: i }) };
|
||||
}
|
||||
|
||||
let mut buf = Vec::with_capacity(len);
|
||||
|
@ -246,8 +303,10 @@ impl Decode for CString {
|
|||
}
|
||||
|
||||
impl Decode for Duration {
|
||||
type Error = Infallible;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let secs = Decode::decode(stream)?;
|
||||
let nanos = Decode::decode(stream)?;
|
||||
|
||||
|
@ -258,21 +317,26 @@ impl Decode for Duration {
|
|||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl<K, V, S> Decode for HashMap<K, V, S>
|
||||
impl<K, V, S, E> Decode for HashMap<K, V, S>
|
||||
where
|
||||
K: Decode + Eq + Hash,
|
||||
V: Decode,
|
||||
K: Decode<Error = E> + Eq + Hash,
|
||||
V: Decode<Error = E>,
|
||||
S: BuildHasher + Default,
|
||||
{
|
||||
{
|
||||
type Error = CollectionDecodeError<Infallible, ItemDecodeError<usize, E>>;
|
||||
|
||||
#[inline]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
let len = Decode::decode(stream)?;
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let len = Decode::decode(stream).unwrap();
|
||||
|
||||
let mut this = Self::with_capacity_and_hasher(len, Default::default());
|
||||
|
||||
for _ in 0x0..len {
|
||||
let key = Decode::decode(stream)?;
|
||||
let value = Decode::decode(stream)?;
|
||||
for i in 0x0..len {
|
||||
let key= Decode::decode(stream)
|
||||
.map_err(|e| CollectionDecodeError::Item(ItemDecodeError { index: i, error: e }))?;
|
||||
|
||||
let value = Decode::decode(stream)
|
||||
.map_err(|e| CollectionDecodeError::Item(ItemDecodeError { index: i, error: e }))?;
|
||||
|
||||
this.insert(key, value);
|
||||
}
|
||||
|
@ -283,19 +347,22 @@ where
|
|||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl<T, S> Decode for HashSet<T, S>
|
||||
impl<K, S> Decode for HashSet<K, S>
|
||||
where
|
||||
T: Decode + Eq + Hash,
|
||||
K: Decode + Eq + Hash,
|
||||
S: BuildHasher + Default,
|
||||
{
|
||||
{
|
||||
type Error = CollectionDecodeError<Infallible, ItemDecodeError<usize, K::Error>>;
|
||||
|
||||
#[inline]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
let len = Decode::decode(stream)?;
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let len = Decode::decode(stream).unwrap();
|
||||
|
||||
let mut this = Self::with_capacity_and_hasher(len, Default::default());
|
||||
|
||||
for _ in 0x0..len {
|
||||
let key = Decode::decode(stream)?;
|
||||
for i in 0x0..len {
|
||||
let key = Decode::decode(stream)
|
||||
.map_err(|e| CollectionDecodeError::Item(ItemDecodeError { index: i, error: e }) )?;
|
||||
|
||||
this.insert(key);
|
||||
}
|
||||
|
@ -305,23 +372,27 @@ where
|
|||
}
|
||||
|
||||
impl Decode for Infallible {
|
||||
#[expect(clippy::panic_in_result_fn)]
|
||||
type Error = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(_stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(_stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
panic!("cannot deserialise `Infallible` as it cannot be serialised to begin with")
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for IpAddr {
|
||||
type Error = EnumDecodeError<u8, Infallible>;
|
||||
|
||||
#[inline]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
let discriminant = u8::decode(stream)?;
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let discriminant = u8::decode(stream)
|
||||
.map_err(EnumDecodeError::InvalidDiscriminant)?;
|
||||
|
||||
let this = match discriminant {
|
||||
0x4 => Self::V4(Decode::decode(stream)?),
|
||||
0x6 => Self::V6(Decode::decode(stream)?),
|
||||
0x4 => Self::V4(Decode::decode(stream).unwrap()),
|
||||
0x6 => Self::V6(Decode::decode(stream).unwrap()),
|
||||
|
||||
_ => return Err(DecodeError::InvalidDiscriminant(discriminant.into()))
|
||||
value => return Err(EnumDecodeError::UnassignedDiscriminant { value })
|
||||
};
|
||||
|
||||
Ok(this)
|
||||
|
@ -329,24 +400,30 @@ impl Decode for IpAddr {
|
|||
}
|
||||
|
||||
impl Decode for Ipv4Addr {
|
||||
type Error = Infallible;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let value = Decode::decode(stream)?;
|
||||
Ok(Self::from_bits(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for Ipv6Addr {
|
||||
type Error = Infallible;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let value = Decode::decode(stream)?;
|
||||
Ok(Self::from_bits(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for isize {
|
||||
type Error = Infallible;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let value = i16::decode(stream)?;
|
||||
Ok(value as Self)
|
||||
}
|
||||
|
@ -355,14 +432,17 @@ impl Decode for isize {
|
|||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: Decode> Decode for LinkedList<T> {
|
||||
type Error = CollectionDecodeError<Infallible, ItemDecodeError<usize, T::Error>>;
|
||||
|
||||
#[inline]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
let len = usize::decode(stream)?;
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let len = usize::decode(stream).unwrap();
|
||||
|
||||
let mut this = Self::new();
|
||||
|
||||
for _ in 0x0..len {
|
||||
let value = T::decode(stream)?;
|
||||
for i in 0x0..len {
|
||||
let value = T::decode(stream)
|
||||
.map_err(|e| CollectionDecodeError::Item(ItemDecodeError { index: i, error: e }))?;
|
||||
|
||||
this.push_back(value);
|
||||
}
|
||||
|
@ -374,17 +454,21 @@ impl<T: Decode> Decode for LinkedList<T> {
|
|||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl<T: Decode> Decode for Mutex<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
Ok(Self::new(Decode::decode(stream)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decode> Decode for Option<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[expect(clippy::if_then_some_else_none)] // ???
|
||||
#[inline]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
let sign = bool::decode(stream)?;
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let sign = bool::decode(stream).unwrap();
|
||||
|
||||
let this = if sign {
|
||||
Some(Decode::decode(stream)?)
|
||||
|
@ -397,22 +481,28 @@ impl<T: Decode> Decode for Option<T> {
|
|||
}
|
||||
|
||||
impl<T> Decode for PhantomData<T> {
|
||||
type Error = Infallible;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(_stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(_stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
Ok(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for PhantomPinned {
|
||||
type Error = Infallible;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(_stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(_stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
Ok(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decode> Decode for Range<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let start = Decode::decode(stream)?;
|
||||
let end = Decode::decode(stream)?;
|
||||
|
||||
|
@ -421,8 +511,10 @@ impl<T: Decode> Decode for Range<T> {
|
|||
}
|
||||
|
||||
impl<T: Decode> Decode for RangeFrom<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let start = Decode::decode(stream)?;
|
||||
|
||||
Ok(start..)
|
||||
|
@ -430,15 +522,19 @@ impl<T: Decode> Decode for RangeFrom<T> {
|
|||
}
|
||||
|
||||
impl Decode for RangeFull {
|
||||
type Error = Infallible;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(_stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(_stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
Ok(..)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decode> Decode for RangeInclusive<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let start = Decode::decode(stream)?;
|
||||
let end = Decode::decode(stream)?;
|
||||
|
||||
|
@ -447,8 +543,10 @@ impl<T: Decode> Decode for RangeInclusive<T> {
|
|||
}
|
||||
|
||||
impl<T: Decode> Decode for RangeTo<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let end = Decode::decode(stream)?;
|
||||
|
||||
Ok(..end)
|
||||
|
@ -456,8 +554,10 @@ impl<T: Decode> Decode for RangeTo<T> {
|
|||
}
|
||||
|
||||
impl<T: Decode> Decode for RangeToInclusive<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let end = Decode::decode(stream)?;
|
||||
|
||||
Ok(..=end)
|
||||
|
@ -467,15 +567,19 @@ impl<T: Decode> Decode for RangeToInclusive<T> {
|
|||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: Decode> Decode for Rc<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
Ok(Self::new(Decode::decode(stream)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decode> Decode for RefCell<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let value = Decode::decode(stream)?;
|
||||
|
||||
let this = Self::new(value);
|
||||
|
@ -483,15 +587,28 @@ impl<T: Decode> Decode for RefCell<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Decode, E: Decode> Decode for core::result::Result<T, E> {
|
||||
impl<T, E, Err> Decode for core::result::Result<T, E>
|
||||
where
|
||||
T: Decode<Error = Err>,
|
||||
E: Decode<Error = Err>,
|
||||
{
|
||||
type Error = EnumDecodeError<bool, Err>;
|
||||
|
||||
#[inline]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
let sign = bool::decode(stream)?;
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let sign = bool::decode(stream)
|
||||
.map_err(EnumDecodeError::InvalidDiscriminant)?;
|
||||
|
||||
let this = if sign {
|
||||
Err(E::decode(stream)?)
|
||||
let value = Decode::decode(stream)
|
||||
.map_err(EnumDecodeError::Field)?;
|
||||
|
||||
Err(value)
|
||||
} else {
|
||||
Ok(Decode::decode(stream)?)
|
||||
let value = Decode::decode(stream)
|
||||
.map_err(EnumDecodeError::Field)?;
|
||||
|
||||
Ok(value)
|
||||
};
|
||||
|
||||
Ok(this)
|
||||
|
@ -501,29 +618,41 @@ impl<T: Decode, E: Decode> Decode for core::result::Result<T, E> {
|
|||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl<T: Decode> Decode for RwLock<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
Ok(Self::new(Decode::decode(stream)?))
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let value = Decode::decode(stream)?;
|
||||
|
||||
let this = Self::new(value);
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decode> Decode for Saturating<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
Ok(Self(Decode::decode(stream)?))
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let value = Decode::decode(stream)?;
|
||||
|
||||
let this = Self(value);
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for SocketAddr {
|
||||
type Error = EnumDecodeError<u8, Infallible>;
|
||||
|
||||
#[inline]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
let discriminant = u8::decode(stream)?;
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let discriminant = u8::decode(stream).unwrap();
|
||||
|
||||
let this = match discriminant {
|
||||
0x4 => Self::V4(Decode::decode(stream)?),
|
||||
0x6 => Self::V6(Decode::decode(stream)?),
|
||||
0x4 => Self::V4(Decode::decode(stream).unwrap()),
|
||||
0x6 => Self::V6(Decode::decode(stream).unwrap()),
|
||||
|
||||
_ => return Err(DecodeError::InvalidDiscriminant(discriminant.into()))
|
||||
value => return Err(EnumDecodeError::UnassignedDiscriminant { value })
|
||||
};
|
||||
|
||||
Ok(this)
|
||||
|
@ -531,8 +660,10 @@ impl Decode for SocketAddr {
|
|||
}
|
||||
|
||||
impl Decode for SocketAddrV4 {
|
||||
type Error = Infallible;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let ip = Decode::decode(stream)?;
|
||||
let port = Decode::decode(stream)?;
|
||||
|
||||
|
@ -542,8 +673,10 @@ impl Decode for SocketAddrV4 {
|
|||
}
|
||||
|
||||
impl Decode for SocketAddrV6 {
|
||||
type Error = Infallible;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let ip = Decode::decode(stream)?;
|
||||
let port = Decode::decode(stream)?;
|
||||
let flow_info = Decode::decode(stream)?;
|
||||
|
@ -557,9 +690,11 @@ impl Decode for SocketAddrV6 {
|
|||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl Decode for String {
|
||||
type Error = CollectionDecodeError<Infallible, Utf8Error>;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
let len = Decode::decode(stream)?;
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let len = Decode::decode(stream).unwrap();
|
||||
|
||||
let data = stream.read(len);
|
||||
|
||||
|
@ -568,7 +703,7 @@ impl Decode for String {
|
|||
let i = e.valid_up_to();
|
||||
let c = data[i];
|
||||
|
||||
DecodeError::BadString(Utf8Error { value: c, index: i })
|
||||
CollectionDecodeError::Item(Utf8Error { value: c, index: i })
|
||||
})?;
|
||||
|
||||
let mut v = Vec::with_capacity(len);
|
||||
|
@ -590,9 +725,11 @@ impl Decode for String {
|
|||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl Decode for SystemTime {
|
||||
type Error = SystemTimeDecodeError;
|
||||
|
||||
#[inline]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
let time = i64::decode(stream)?;
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let time = i64::decode(stream).unwrap();
|
||||
|
||||
let this = if time.is_positive() {
|
||||
let time = time as u64;
|
||||
|
@ -604,20 +741,24 @@ impl Decode for SystemTime {
|
|||
UNIX_EPOCH.checked_sub(Duration::from_secs(time))
|
||||
};
|
||||
|
||||
this.ok_or_else(|| DecodeError::NarrowSystemTime { timestamp: time })
|
||||
this.ok_or(SystemTimeDecodeError { timestamp: time })
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for () {
|
||||
type Error = Infallible;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(_stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(_stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for usize {
|
||||
type Error = Infallible;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let value = u16::decode(stream)?;
|
||||
Ok(value as Self)
|
||||
}
|
||||
|
@ -626,15 +767,18 @@ impl Decode for usize {
|
|||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: Decode> Decode for Vec<T> {
|
||||
type Error = CollectionDecodeError<Infallible, ItemDecodeError<usize, T::Error>>;
|
||||
|
||||
#[inline]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
let len = Decode::decode(stream)?;
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let len = Decode::decode(stream).unwrap();
|
||||
|
||||
let mut this = Self::with_capacity(len);
|
||||
|
||||
let buf = this.as_mut_ptr();
|
||||
for i in 0x0..len {
|
||||
let value = Decode::decode(stream)?;
|
||||
let value = Decode::decode(stream)
|
||||
.map_err(|e| CollectionDecodeError::Item(ItemDecodeError { index: i, error: e }))?;
|
||||
|
||||
// SAFETY: Each index is within bounds (i.e. capac-
|
||||
// ity).
|
||||
|
@ -649,17 +793,24 @@ impl<T: Decode> Decode for Vec<T> {
|
|||
}
|
||||
|
||||
impl<T: Decode> Decode for Wrapping<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
Ok(Self(Decode::decode(stream)?))
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let value = Decode::decode(stream)?;
|
||||
|
||||
let this = Self(value);
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_numeric {
|
||||
($ty:ty$(,)?) => {
|
||||
impl ::bzipper::Decode for $ty {
|
||||
impl ::librum::Decode for $ty {
|
||||
type Error = Infallible;
|
||||
|
||||
#[inline]
|
||||
fn decode(stream: &mut IStream) -> ::core::result::Result<Self, ::bzipper::error::DecodeError> {
|
||||
fn decode(stream: &mut IStream) -> ::core::result::Result<Self, Self::Error> {
|
||||
let data = stream
|
||||
.read(Self::MAX_ENCODED_SIZE)
|
||||
.try_into()
|
||||
|
@ -677,11 +828,15 @@ macro_rules! impl_tuple {
|
|||
$($tys:ident),+$(,)?
|
||||
} => {
|
||||
#[doc(hidden)]
|
||||
impl<$($tys: ::bzipper::Decode, )*> ::bzipper::Decode for ($($tys, )*) {
|
||||
impl<$($tys, )* E> ::librum::Decode for ($($tys, )*)
|
||||
where
|
||||
$($tys: Decode<Error = E>, )* {
|
||||
type Error = E;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut ::bzipper::IStream) -> ::core::result::Result<Self, ::bzipper::error::DecodeError> {
|
||||
fn decode(stream: &mut ::librum::IStream) -> ::core::result::Result<Self, Self::Error> {
|
||||
let this = (
|
||||
$( <$tys as ::bzipper::Decode>::decode(stream)?, )*
|
||||
$( <$tys as ::librum::Decode>::decode(stream)?, )*
|
||||
);
|
||||
|
||||
Ok(this)
|
||||
|
@ -692,13 +847,15 @@ macro_rules! impl_tuple {
|
|||
|
||||
macro_rules! impl_non_zero {
|
||||
($ty:ty$(,)?) => {
|
||||
impl ::bzipper::Decode for NonZero<$ty> {
|
||||
impl ::librum::Decode for NonZero<$ty> {
|
||||
type Error = ::librum::error::NonZeroDecodeError;
|
||||
|
||||
#[inline]
|
||||
fn decode(stream: &mut IStream) -> ::core::result::Result<Self, ::bzipper::error::DecodeError> {
|
||||
let value = <$ty as ::bzipper::Decode>::decode(stream)?;
|
||||
fn decode(stream: &mut IStream) -> ::core::result::Result<Self, Self::Error> {
|
||||
let value = <$ty as ::librum::Decode>::decode(stream).unwrap();
|
||||
|
||||
let this = NonZero::new(value)
|
||||
.ok_or(::bzipper::error::DecodeError::NullInteger)?;
|
||||
.ok_or(::librum::error::NonZeroDecodeError)?;
|
||||
|
||||
Ok(this)
|
||||
}
|
||||
|
@ -713,10 +870,15 @@ macro_rules! impl_atomic {
|
|||
} => {
|
||||
#[cfg(target_has_atomic = $width)]
|
||||
#[cfg_attr(doc, doc(cfg(target_has_atomic = $width)))]
|
||||
impl ::bzipper::Decode for $ty {
|
||||
impl ::librum::Decode for $ty {
|
||||
type Error = ::core::convert::Infallible;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(stream: &mut ::bzipper::IStream) -> ::core::result::Result<Self, ::bzipper::error::DecodeError> {
|
||||
Ok(Self::new(::bzipper::Decode::decode(stream)?))
|
||||
fn decode(stream: &mut ::librum::IStream) -> ::core::result::Result<Self, Self::Error> {
|
||||
let value = ::librum::Decode::decode(stream).unwrap();
|
||||
|
||||
let this = Self::new(value);
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -819,32 +981,32 @@ impl_tuple! {
|
|||
}
|
||||
|
||||
impl_tuple! {
|
||||
T0,
|
||||
T1,
|
||||
T2,
|
||||
T3,
|
||||
T4,
|
||||
T5,
|
||||
T6,
|
||||
T7,
|
||||
T8,
|
||||
T9,
|
||||
T10,
|
||||
T0,
|
||||
T1,
|
||||
T2,
|
||||
T3,
|
||||
T4,
|
||||
T5,
|
||||
T6,
|
||||
T7,
|
||||
T8,
|
||||
T9,
|
||||
T10,
|
||||
}
|
||||
|
||||
impl_tuple! {
|
||||
T0,
|
||||
T1,
|
||||
T2,
|
||||
T3,
|
||||
T4,
|
||||
T5,
|
||||
T6,
|
||||
T7,
|
||||
T8,
|
||||
T9,
|
||||
T10,
|
||||
T11,
|
||||
T0,
|
||||
T1,
|
||||
T2,
|
||||
T3,
|
||||
T4,
|
||||
T5,
|
||||
T6,
|
||||
T7,
|
||||
T8,
|
||||
T9,
|
||||
T10,
|
||||
T11,
|
||||
}
|
||||
|
||||
impl_non_zero!(i128);
|
|
@ -1,61 +1,42 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use alloc::string::String;
|
||||
use bzipper::{Decode, IStream, SizedEncode};
|
||||
use core::char;
|
||||
use librum::{Decode, Encode, IStream, SizedEncode};
|
||||
use std::char;
|
||||
use std::vec::Vec;
|
||||
use std::string::String;
|
||||
|
||||
macro_rules! test {
|
||||
($ty:ty: $data:expr => $value:expr) => {{
|
||||
let mut stream = IStream::new(&$data);
|
||||
|
||||
let left = <$ty as Decode>::decode(&mut stream).unwrap();
|
||||
let right = $value;
|
||||
|
||||
assert_eq!(left, right);
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode() {
|
||||
#[derive(Debug, Decode, PartialEq, SizedEncode)]
|
||||
struct ProcExit {
|
||||
exit_code: i32,
|
||||
timestmap: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Decode, PartialEq, SizedEncode)]
|
||||
struct NewByte(u8);
|
||||
|
||||
#[derive(Debug, Decode, PartialEq, SizedEncode)]
|
||||
struct Unit;
|
||||
|
||||
#[derive(Debug, Decode, PartialEq, SizedEncode)]
|
||||
enum UnitOrFields {
|
||||
Unit,
|
||||
Unnamed(i32),
|
||||
Named { timestamp: u64 },
|
||||
}
|
||||
|
||||
macro_rules! test {
|
||||
($ty:ty: $data:expr => $value:expr) => {{
|
||||
let mut stream = IStream::new(&$data);
|
||||
|
||||
let left = <$ty as Decode>::decode(&mut stream).unwrap();
|
||||
let right = $value;
|
||||
|
||||
assert_eq!(left, right);
|
||||
}};
|
||||
}
|
||||
|
||||
test!(i8: [0x00] => 0x00);
|
||||
test!(i8: [0x7F] => 0x7F);
|
||||
test!(i8: [0x80] => -0x80);
|
||||
|
@ -95,6 +76,31 @@ fn test_decode() {
|
|||
test!(Result<(), i8>: [0x00, 0x00] => Ok(()));
|
||||
test!(Result<(), i8>: [0x01, 0x7F] => Err(i8::MAX));
|
||||
|
||||
test!(Vec<u16>: [0x00, 0x02, 0xAA, 0xBB, 0xCC, 0xDD] => [0xAA_BB, 0xCC_DD].as_slice());
|
||||
|
||||
test!(String: [0x00, 0x06, 0xE6, 0x97, 0xA5, 0xE6, 0x9C, 0xAC] => "\u{65E5}\u{672C}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_derive() {
|
||||
#[derive(Debug, Decode, Encode, PartialEq, SizedEncode)]
|
||||
struct ProcExit {
|
||||
exit_code: i32,
|
||||
timestmap: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Decode, Encode, PartialEq, SizedEncode)]
|
||||
struct NewByte(u8);
|
||||
|
||||
#[derive(Debug, Decode, Encode, PartialEq, SizedEncode)]
|
||||
struct Unit;
|
||||
|
||||
#[derive(Debug, Decode, Encode, PartialEq, SizedEncode)]
|
||||
enum UnitOrFields {
|
||||
Unit,
|
||||
Unnamed(i32),
|
||||
Named { timestamp: u64 },
|
||||
}
|
||||
test!(ProcExit: [
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x5E, 0x0B, 0xE1, 0x00,
|
||||
|
@ -118,8 +124,4 @@ fn test_decode() {
|
|||
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x66, 0xC5,
|
||||
0xC8, 0x4C,
|
||||
] => UnitOrFields::Named { timestamp: 1724237900 });
|
||||
|
||||
test!(Vec<u16>: [0x00, 0x02, 0xAA, 0xBB, 0xCC, 0xDD] => [0xAA_BB, 0xCC_DD].as_slice());
|
||||
|
||||
test!(String: [0x00, 0x06, 0xE6, 0x97, 0xA5, 0xE6, 0x9C, 0xAC] => "\u{65E5}\u{672C}");
|
||||
}
|
84
librum/src/decode_borrowed/mod.rs
Normal file
84
librum/src/decode_borrowed/mod.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::Decode;
|
||||
|
||||
use core::borrow::Borrow;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::boxed::Box;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::ffi::CString;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::rc::Rc;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::vec::Vec;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::string::String;
|
||||
|
||||
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
|
||||
use alloc::sync::Arc;
|
||||
|
||||
/// Indicates a scheme relationship between borrowed and owned types.
|
||||
///
|
||||
/// Implementing this trait is a promise that <code><Self as [Decode]>::[decode](Decode::decode)</code> can handle any encoding of `B`.
|
||||
/// This is mainly useful for types that implement [`Encode`](crate::Encode::encode) but do not implement `Decode` for whatever reason (mostly the act of being unsized).
|
||||
///
|
||||
/// The primary user of this trait is the `Decode` implementation of [`Cow`](alloc::borrow::Cow).
|
||||
///
|
||||
/// # Arrays
|
||||
///
|
||||
/// This trait in the form <code>DecodeBorrowed<[\[T\]]></code> is not implemented for [`[T; N]`](array) for the simple reason that arrays they do not encode their length (as it is hard coded into the type), thus rendering their scheme incompatible with that of slices.
|
||||
///
|
||||
/// [\[T\]]: array
|
||||
///
|
||||
/// An alternative to using arrays would be to use [`SizedSlice`](crate::SizedSlice).
|
||||
pub trait DecodeBorrowed<B: ?Sized>: Borrow<B> + Decode { }
|
||||
|
||||
impl<T: Decode> DecodeBorrowed<T> for T { }
|
||||
|
||||
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
|
||||
#[cfg_attr(doc, doc(cfg(all(feature = "alloc", target_has_atomic = "ptr"))))]
|
||||
impl<T: Decode> DecodeBorrowed<T> for Arc<T> { }
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: Decode> DecodeBorrowed<T> for Box<T> { }
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl DecodeBorrowed<core::ffi::CStr> for CString { }
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: Decode> DecodeBorrowed<T> for Rc<T> { }
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl DecodeBorrowed<str> for String { }
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: Decode> DecodeBorrowed<[T]> for Vec<T> { }
|
|
@ -1,29 +1,36 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
mod tests;
|
||||
|
||||
use crate::OStream;
|
||||
use crate::error::EncodeError;
|
||||
use crate::error::{
|
||||
CollectionEncodeError,
|
||||
EnumEncodeError,
|
||||
IsizeEncodeError,
|
||||
ItemEncodeError,
|
||||
RefCellEncodeError,
|
||||
UsizeEncodeError,
|
||||
};
|
||||
|
||||
use core::cell::{Cell, LazyCell, RefCell};
|
||||
use core::convert::Infallible;
|
||||
|
@ -72,7 +79,7 @@ use alloc::vec::Vec;
|
|||
#[cfg(feature = "alloc")]
|
||||
use alloc::rc::Rc;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
|
||||
use alloc::sync::Arc;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
|
@ -87,7 +94,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
|||
/// Denotes a type capable of being encoded.
|
||||
///
|
||||
/// It is recommended to simply derive this trait for custom types.
|
||||
/// It can, however, also be manually implemented.
|
||||
/// It can, of course, also just be manually implemented.
|
||||
///
|
||||
/// If all possible encodings have a known maximum size, then the [`SizedEncode`](crate::SizedEncode) trait should additionally be implemented.
|
||||
///
|
||||
|
@ -100,8 +107,8 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
|||
/// // plementation is equivalent to what would have
|
||||
/// // been derived.
|
||||
///
|
||||
/// use bzipper::{Encode, OStream};
|
||||
/// use bzipper::error::EncodeError;
|
||||
/// use librum::{Encode, OStream};
|
||||
/// use core::convert::Infallible;
|
||||
///
|
||||
/// struct Foo {
|
||||
/// bar: u16,
|
||||
|
@ -109,7 +116,11 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
|||
/// }
|
||||
///
|
||||
/// impl Encode for Foo {
|
||||
/// fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
/// // Both `u16` and `f32` encode infallibly.
|
||||
///
|
||||
/// type Error = Infallible;
|
||||
///
|
||||
/// fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
/// // Encode fields using chaining.
|
||||
///
|
||||
/// self.bar.encode(stream)?;
|
||||
|
@ -120,6 +131,8 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
|||
/// }
|
||||
/// ```
|
||||
pub trait Encode {
|
||||
type Error;
|
||||
|
||||
/// Encodes `self` into the provided stream.
|
||||
///
|
||||
/// # Errors
|
||||
|
@ -129,19 +142,23 @@ pub trait Encode {
|
|||
/// # Panics
|
||||
///
|
||||
/// If `stream` cannot contain the entirety of the resulting encoding, then this method should panic.
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError>;
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
impl<T: Encode> Encode for &T {
|
||||
impl<T: Encode + ?Sized> Encode for &T {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
T::encode(self, stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Encode> Encode for &mut T {
|
||||
impl<T: Encode + ?Sized> Encode for &mut T {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
T::encode(self, stream)
|
||||
}
|
||||
}
|
||||
|
@ -149,19 +166,25 @@ impl<T: Encode> Encode for &mut T {
|
|||
/// Implemented for tuples with up to twelve members.
|
||||
#[cfg_attr(doc, doc(fake_variadic))]
|
||||
impl<T: Encode> Encode for (T, ) {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
self.0.encode(stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Encode, const N: usize> Encode for [T; N] {
|
||||
type Error = CollectionEncodeError<Infallible, ItemEncodeError<usize, T::Error>>;
|
||||
|
||||
/// Encodes each element sequentially.
|
||||
/// The length is hard-coded into the type and is therefore not encoded.
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
for value in self {
|
||||
value.encode(stream)?;
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
for (i, v) in self.iter().enumerate() {
|
||||
v
|
||||
.encode(stream)
|
||||
.map_err(|e| CollectionEncodeError::Item(ItemEncodeError { index: i, error: e }))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -169,51 +192,64 @@ impl<T: Encode, const N: usize> Encode for [T; N] {
|
|||
}
|
||||
|
||||
impl<T: Encode> Encode for [T] {
|
||||
type Error = CollectionEncodeError<UsizeEncodeError, ItemEncodeError<usize, T::Error>>;
|
||||
|
||||
/// Encodes each element sequentially with an extra length specifier (of type [`usize`]) prepended first.
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
self.len().encode(stream)?;
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
self
|
||||
.len()
|
||||
.encode(stream)
|
||||
.map_err(CollectionEncodeError::Length)?;
|
||||
|
||||
for value in self {
|
||||
value.encode(stream)?;
|
||||
for (i,v) in self.iter().enumerate() {
|
||||
v
|
||||
.encode(stream)
|
||||
.map_err(|e| CollectionEncodeError::Item(ItemEncodeError { index: i, error: e }))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: Encode> Encode for Arc<T> {
|
||||
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
|
||||
#[cfg_attr(doc, doc(cfg(all(feature = "alloc", target_has_atomic = "ptr"))))]
|
||||
impl<T: Encode + ?Sized> Encode for Arc<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
T::encode(self, stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for bool {
|
||||
type Error = <u8 as Encode>::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
u8::from(*self).encode(stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Encode> Encode for Bound<T> {
|
||||
type Error = EnumEncodeError<u8, T::Error>;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
match *self {
|
||||
Self::Included(ref bound) => {
|
||||
0x0u8.encode(stream)?;
|
||||
bound.encode(stream)?;
|
||||
0x0u8.encode(stream).unwrap();
|
||||
bound.encode(stream).map_err(EnumEncodeError::Field)?;
|
||||
}
|
||||
|
||||
Self::Excluded(ref bound) => {
|
||||
0x1u8.encode(stream)?;
|
||||
bound.encode(stream)?;
|
||||
0x1u8.encode(stream).unwrap();
|
||||
bound.encode(stream).map_err(EnumEncodeError::Field)?;
|
||||
}
|
||||
|
||||
Self::Unbounded => {
|
||||
0x2u8.encode(stream)?;
|
||||
0x2u8.encode(stream).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,40 +259,50 @@ impl<T: Encode> Encode for Bound<T> {
|
|||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: Encode> Encode for Box<T> {
|
||||
impl<T: Encode + ?Sized> Encode for Box<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
T::encode(self, stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Encode + Copy> Encode for Cell<T> {
|
||||
impl<T: Copy + Encode> Encode for Cell<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
self.get().encode(stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for char {
|
||||
type Error = <u32 as Encode>::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
u32::from(*self).encode(stream)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: Encode + ToOwned> Encode for Cow<'_, T> {
|
||||
impl<T: Encode + ?Sized + ToOwned> Encode for Cow<'_, T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
T::encode(self, stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for CStr {
|
||||
type Error = <[u8] as Encode>::Error;
|
||||
|
||||
/// Encodes the string identically to [a byte slice](slice) containing the string's byte values **excluding** the null terminator.
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
self.to_bytes().encode(stream)
|
||||
}
|
||||
}
|
||||
|
@ -264,19 +310,23 @@ impl Encode for CStr {
|
|||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl Encode for CString {
|
||||
type Error = <CStr as Encode>::Error;
|
||||
|
||||
/// See the the implementation of [`CStr`].
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
self.as_c_str().encode(stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for Duration {
|
||||
type Error = Infallible;
|
||||
|
||||
/// Encodes the duration's seconds and nanoseconds counters sequentially.
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
self.as_secs().encode(stream)?;
|
||||
self.subsec_nanos().encode(stream)?;
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
self.as_secs().encode(stream).unwrap();
|
||||
self.subsec_nanos().encode(stream).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -284,14 +334,16 @@ impl Encode for Duration {
|
|||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl<K, V, S> Encode for HashMap<K, V, S>
|
||||
impl<K, V, S, E> Encode for HashMap<K, V, S>
|
||||
where
|
||||
K: Encode,
|
||||
V: Encode,
|
||||
K: Encode<Error = E>,
|
||||
V: Encode<Error = E>,
|
||||
S: BuildHasher,
|
||||
{
|
||||
{
|
||||
type Error = E;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
for (key, value) in self {
|
||||
key.encode(stream)?;
|
||||
value.encode(stream)?;
|
||||
|
@ -303,13 +355,15 @@ where
|
|||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl<T, S> Encode for HashSet<T, S>
|
||||
impl<K, S> Encode for HashSet<K, S>
|
||||
where
|
||||
T: Encode,
|
||||
K: Encode,
|
||||
S: BuildHasher,
|
||||
{
|
||||
{
|
||||
type Error = K::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
for key in self {
|
||||
key.encode(stream)?;
|
||||
}
|
||||
|
@ -321,8 +375,10 @@ where
|
|||
// Especially useful for `Result<T, Infallible>`.
|
||||
// **If** that is even needed, of course.
|
||||
impl Encode for Infallible {
|
||||
type Error = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, _stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, _stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
// SAFETY: `Infallible` can **never** be construct-
|
||||
// ed.
|
||||
unsafe { unreachable_unchecked() }
|
||||
|
@ -330,22 +386,24 @@ impl Encode for Infallible {
|
|||
}
|
||||
|
||||
impl Encode for IpAddr {
|
||||
type Error = EnumEncodeError<u8, Infallible>;
|
||||
|
||||
/// Encodes a the address with a preceding discriminant denoting the IP version of the address (i.e. `4` for IPv4 and `6` for IPv6).
|
||||
///
|
||||
/// See also the implementations of [`Ipv4Addr`] and [`Ipv6Addr`].
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
// The discriminant here is the IP version.
|
||||
|
||||
match *self {
|
||||
Self::V4(ref addr) => {
|
||||
0x4u8.encode(stream)?;
|
||||
addr.encode(stream)?;
|
||||
0x4u8.encode(stream).map_err(EnumEncodeError::Discriminant)?;
|
||||
addr.encode(stream).map_err(EnumEncodeError::Field)?;
|
||||
}
|
||||
|
||||
Self::V6(ref addr) => {
|
||||
0x6u8.encode(stream)?;
|
||||
addr.encode(stream)?;
|
||||
0x6u8.encode(stream).map_err(EnumEncodeError::Discriminant)?;
|
||||
addr.encode(stream).map_err(EnumEncodeError::Field)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -354,39 +412,46 @@ impl Encode for IpAddr {
|
|||
}
|
||||
|
||||
impl Encode for Ipv4Addr {
|
||||
type Error = Infallible;
|
||||
|
||||
/// Encodes the address's bits in big-endian.
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
let value = self.to_bits();
|
||||
value.encode(stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for Ipv6Addr {
|
||||
type Error = Infallible;
|
||||
|
||||
/// Encodes the address's bits in big-endian.
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
let value = self.to_bits();
|
||||
value.encode(stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for isize {
|
||||
/// Casts `self` to [`i16`] and encodes.
|
||||
///
|
||||
/// If this conversion isn't possible for the given value, then the [`IsizeOutOfRange`](EncodeError::IsizeOutOfRange) error is returned.
|
||||
#[inline]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
let value = i16::try_from(*self)
|
||||
.map_err(|_| EncodeError::IsizeOutOfRange(*self))?;
|
||||
type Error = IsizeEncodeError;
|
||||
|
||||
value.encode(stream)
|
||||
/// Casts `self` to [`i16`] and encodes the result.
|
||||
#[inline]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
let value = i16::try_from(*self)
|
||||
.map_err(|_| IsizeEncodeError(*self))?;
|
||||
|
||||
value.encode(stream).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Encode> Encode for LazyCell<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
T::encode(self, stream)
|
||||
}
|
||||
}
|
||||
|
@ -394,19 +459,30 @@ impl<T: Encode> Encode for LazyCell<T> {
|
|||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl<T: Encode> Encode for LazyLock<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
T::encode(self, stream)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: Encode> Encode for LinkedList<T> {
|
||||
impl<T: Encode<Error = E>, E> Encode for LinkedList<T> {
|
||||
type Error = CollectionEncodeError<UsizeEncodeError, (usize, E)>;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
for value in self {
|
||||
value.encode(stream)?;
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
self
|
||||
.len()
|
||||
.encode(stream)
|
||||
.map_err(CollectionEncodeError::Length)?;
|
||||
|
||||
for (i, v) in self.iter().enumerate() {
|
||||
v
|
||||
.encode(stream)
|
||||
.map_err(|e| CollectionEncodeError::Item((i, e)))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -415,9 +491,11 @@ impl<T: Encode> Encode for LinkedList<T> {
|
|||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl<T: Encode> Encode for Mutex<T> {
|
||||
impl<T: Encode + ?Sized> Encode for Mutex<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
self
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
|
@ -426,16 +504,20 @@ impl<T: Encode> Encode for Mutex<T> {
|
|||
}
|
||||
|
||||
impl<T: Encode> Encode for Option<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
/// Encodes a sign denoting the optional's variant.
|
||||
/// This is `false` for `None` instances and `true` for `Some` instances.
|
||||
///
|
||||
/// If `Some`, then the contained value is encoded after this sign..
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
match *self {
|
||||
None => false.encode(stream)?,
|
||||
None => {
|
||||
false.encode(stream).unwrap();
|
||||
}
|
||||
|
||||
Some(ref v) => {
|
||||
true.encode(stream)?;
|
||||
true.encode(stream).unwrap();
|
||||
v.encode(stream)?;
|
||||
}
|
||||
};
|
||||
|
@ -445,22 +527,28 @@ impl<T: Encode> Encode for Option<T> {
|
|||
}
|
||||
|
||||
impl<T> Encode for PhantomData<T> {
|
||||
type Error = Infallible;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, _stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, _stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for PhantomPinned {
|
||||
type Error = Infallible;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, _stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, _stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Encode> Encode for Range<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
self.start.encode(stream)?;
|
||||
self.end.encode(stream)?;
|
||||
|
||||
|
@ -469,22 +557,28 @@ impl<T: Encode> Encode for Range<T> {
|
|||
}
|
||||
|
||||
impl<T: Encode> Encode for RangeFrom<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
self.start.encode(stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for RangeFull {
|
||||
type Error = Infallible;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, _stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, _stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Encode> Encode for RangeInclusive<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
self.start().encode(stream)?;
|
||||
self.end().encode(stream)?;
|
||||
|
||||
|
@ -493,15 +587,19 @@ impl<T: Encode> Encode for RangeInclusive<T> {
|
|||
}
|
||||
|
||||
impl<T: Encode> Encode for RangeTo<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
self.end.encode(stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Encode> Encode for RangeToInclusive<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
self.end.encode(stream)?;
|
||||
|
||||
Ok(())
|
||||
|
@ -510,42 +608,54 @@ impl<T: Encode> Encode for RangeToInclusive<T> {
|
|||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: Encode> Encode for Rc<T> {
|
||||
impl<T: Encode + ?Sized> Encode for Rc<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
T::encode(self, stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Encode> Encode for RefCell<T> {
|
||||
impl<T: Encode + ?Sized> Encode for RefCell<T> {
|
||||
type Error = RefCellEncodeError<T::Error>;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
let value = self
|
||||
.try_borrow()
|
||||
.map_err(EncodeError::BadBorrow)?;
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
let value = self.try_borrow()
|
||||
.map_err(RefCellEncodeError::Borrow)?;
|
||||
|
||||
T::encode(&value, stream)
|
||||
.map_err(RefCellEncodeError::Value)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Encode, E: Encode> Encode for core::result::Result<T, E> {
|
||||
impl<T, E, Err> Encode for core::result::Result<T, E>
|
||||
where
|
||||
T: Encode<Error = Err>,
|
||||
E: Encode<Error = Err>,
|
||||
{
|
||||
type Error = Err;
|
||||
|
||||
/// Encodes a sign denoting the result's variant.
|
||||
/// This is `false` for `Ok` instances and `true` for `Err` instances.
|
||||
///
|
||||
/// If `Ok`, then the contained value is encoded after this sign.
|
||||
#[inline]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
// The sign here is `false` for `Ok` objects and
|
||||
// `true` for `Err` objects.
|
||||
|
||||
match *self {
|
||||
Ok(ref v) => {
|
||||
false.encode(stream)?;
|
||||
false.encode(stream).unwrap();
|
||||
v.encode(stream)?;
|
||||
}
|
||||
|
||||
Err(ref e) => {
|
||||
true.encode(stream)?;
|
||||
true.encode(stream).unwrap();
|
||||
e.encode(stream)?;
|
||||
}
|
||||
};
|
||||
|
@ -556,9 +666,11 @@ impl<T: Encode, E: Encode> Encode for core::result::Result<T, E> {
|
|||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl<T: Encode> Encode for RwLock<T> {
|
||||
impl<T: Encode + ?Sized> Encode for RwLock<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
self
|
||||
.read()
|
||||
.or_else(|e| Ok(e.into_inner()))?
|
||||
|
@ -567,17 +679,21 @@ impl<T: Encode> Encode for RwLock<T> {
|
|||
}
|
||||
|
||||
impl<T: Encode> Encode for Saturating<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
self.0.encode(stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for SocketAddr {
|
||||
type Error = Infallible;
|
||||
|
||||
/// This implementation encoded as discriminant denoting the IP version of the address (i.e. `4` for IPv4 and `6` for IPv6).
|
||||
/// This is then followed by the respective address' own encoding (either [`SocketAddrV4`] or [`SocketAddrV6`]).
|
||||
#[inline]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
// The discriminant here is the IP version.
|
||||
|
||||
match *self {
|
||||
|
@ -597,9 +713,11 @@ impl Encode for SocketAddr {
|
|||
}
|
||||
|
||||
impl Encode for SocketAddrV4 {
|
||||
type Error = Infallible;
|
||||
|
||||
/// Encodes the address's bits followed by the port number, both of which in big-endian.
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
self.ip().encode(stream)?;
|
||||
self.port().encode(stream)?;
|
||||
|
||||
|
@ -608,9 +726,11 @@ impl Encode for SocketAddrV4 {
|
|||
}
|
||||
|
||||
impl Encode for SocketAddrV6 {
|
||||
type Error = Infallible;
|
||||
|
||||
/// Encodes the address's bits followed by the port number, flow information, and scope identifier -- all of which in big-endian.
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
self.ip().encode(stream)?;
|
||||
self.port().encode(stream)?;
|
||||
self.flowinfo().encode(stream)?;
|
||||
|
@ -621,24 +741,23 @@ impl Encode for SocketAddrV6 {
|
|||
}
|
||||
|
||||
impl Encode for str {
|
||||
type Error = <[u8] as Encode>::Error;
|
||||
|
||||
/// Encodes the string identically to [a byte slice](slice) containing the string's byte values.
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
// Optimised encode. Don't just rely on `[char]`.
|
||||
|
||||
self.len().encode(stream)?;
|
||||
stream.write(self.as_bytes());
|
||||
|
||||
Ok(())
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
self.as_bytes().encode(stream)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl Encode for String {
|
||||
type Error = <str as Encode>::Error;
|
||||
|
||||
/// See [`str`].
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
self.as_str().encode(stream)
|
||||
}
|
||||
}
|
||||
|
@ -646,11 +765,13 @@ impl Encode for String {
|
|||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl Encode for SystemTime {
|
||||
type Error = Infallible;
|
||||
|
||||
/// Encodes the time point as the nearest, signed UNIX timestamp.
|
||||
///
|
||||
/// Examples of some timestamps and their encodings include:
|
||||
///
|
||||
/// | ISO 8601 | UNIX / bZipper |
|
||||
/// | ISO 8601 | UNIX / Librum |
|
||||
/// | :-------------------------- | -------------: |
|
||||
/// | `2024-11-03T12:02:01+01:00` | +1730631721 |
|
||||
/// | `1989-06-03T20:00:00+09:00` | +13258800 |
|
||||
|
@ -658,7 +779,7 @@ impl Encode for SystemTime {
|
|||
/// | `1945-05-04T18:30:00+02:00` | -778231800 |
|
||||
#[expect(clippy::cast_possible_wrap)]
|
||||
#[inline]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
let time = if *self >= UNIX_EPOCH {
|
||||
let duration = self
|
||||
.duration_since(UNIX_EPOCH)
|
||||
|
@ -673,51 +794,61 @@ impl Encode for SystemTime {
|
|||
0x0 - duration.as_secs() as i64
|
||||
};
|
||||
|
||||
time.encode(stream)
|
||||
time.encode(stream).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for () {
|
||||
type Error = Infallible;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, _stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, _stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for usize {
|
||||
/// Casts `self` to [`u16`] and encodes.
|
||||
///
|
||||
/// If this conversion isn't possible for the given value, then the [`IsizeOutOfRange`](EncodeError::UsizeOutOfRange) error is returned.
|
||||
#[inline]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
let value = u16::try_from(*self)
|
||||
.map_err(|_| EncodeError::UsizeOutOfRange(*self))?;
|
||||
type Error = UsizeEncodeError;
|
||||
|
||||
value.encode(stream)
|
||||
/// Casts `self` to [`u16`] and encodes the result.
|
||||
#[inline]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
let value = u16::try_from(*self)
|
||||
.map_err(|_| UsizeEncodeError(*self))?;
|
||||
|
||||
value.encode(stream).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: Encode> Encode for Vec<T> {
|
||||
type Error = <[T] as Encode>::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
self.as_slice().encode(stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Encode> Encode for Wrapping<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
self.0.encode(stream)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_numeric {
|
||||
($ty:ty$(,)?) => {
|
||||
impl ::bzipper::Encode for $ty {
|
||||
impl ::librum::Encode for $ty {
|
||||
type Error = ::core::convert::Infallible;
|
||||
|
||||
#[inline]
|
||||
fn encode(&self, stream: &mut OStream) -> ::core::result::Result<(), ::bzipper::error::EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> ::core::result::Result<(), Self::Error> {
|
||||
stream.write(&self.to_be_bytes());
|
||||
|
||||
Ok(())
|
||||
|
@ -731,9 +862,13 @@ macro_rules! impl_tuple {
|
|||
$($captures:ident: $tys:ident),+$(,)?
|
||||
} => {
|
||||
#[doc(hidden)]
|
||||
impl<$($tys: ::bzipper::Encode, )*> ::bzipper::Encode for ($($tys, )*) {
|
||||
impl<$($tys, )* E> ::librum::Encode for ($($tys, )*)
|
||||
where
|
||||
$($tys: Encode<Error = E>, )* {
|
||||
type Error = E;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut ::bzipper::OStream) -> ::core::result::Result<(), ::bzipper::error::EncodeError> {
|
||||
fn encode(&self, stream: &mut ::librum::OStream) -> ::core::result::Result<(), Self::Error> {
|
||||
let ($(ref $captures, )*) = *self;
|
||||
|
||||
$(
|
||||
|
@ -748,9 +883,11 @@ macro_rules! impl_tuple {
|
|||
|
||||
macro_rules! impl_non_zero {
|
||||
($ty:ty$(,)?) => {
|
||||
impl ::bzipper::Encode for ::core::num::NonZero<$ty> {
|
||||
impl ::librum::Encode for ::core::num::NonZero<$ty> {
|
||||
type Error = <$ty as ::librum::Encode>::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> ::core::result::Result<(), ::bzipper::error::EncodeError> {
|
||||
fn encode(&self, stream: &mut OStream) -> ::core::result::Result<(), Self::Error> {
|
||||
self.get().encode(stream)
|
||||
}
|
||||
}
|
||||
|
@ -765,12 +902,14 @@ macro_rules! impl_atomic {
|
|||
} => {
|
||||
#[cfg(target_has_atomic = $width)]
|
||||
#[cfg_attr(doc, doc(cfg(target_has_atomic = $width)))]
|
||||
impl ::bzipper::Encode for $atomic_ty {
|
||||
impl ::librum::Encode for $atomic_ty {
|
||||
type Error = <$ty as ::librum::Encode>::Error;
|
||||
|
||||
/// Encodes the atomic with the same scheme as that of the atomic type's primitive counterpart.
|
||||
///
|
||||
/// The atomic object itself is read with the [`Relaxed`](core::sync::atomic::Ordering) ordering scheme.
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut ::bzipper::OStream) -> ::core::result::Result<(), ::bzipper::error::EncodeError> {
|
||||
fn encode(&self, stream: &mut ::librum::OStream) -> ::core::result::Result<(), Self::Error> {
|
||||
self.load(::std::sync::atomic::Ordering::Relaxed).encode(stream)
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bzipper.
|
||||
// This file is part of librum.
|
||||
//test!(you can redistribut => []);
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
|
@ -8,52 +8,38 @@
|
|||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bzipper is distributed in the hope that it will
|
||||
// librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bzipper. If
|
||||
// er General Public License along with librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use core::time::Duration;
|
||||
use librum::{Encode, OStream, SizedEncode, SizedStr};
|
||||
use std::time::Duration;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use std::vec;
|
||||
use std::vec::Vec;
|
||||
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use bzipper::{Encode, OStream, SizedEncode, SizedStr};
|
||||
macro_rules! test {
|
||||
($ty:ty: $value:expr => $data:expr) => {{
|
||||
let data = $data;
|
||||
|
||||
#[derive(SizedEncode)]
|
||||
struct Foo(char);
|
||||
let mut buf = vec![0x00; data.len()];
|
||||
|
||||
#[derive(SizedEncode)]
|
||||
#[repr(u8)] // Not honoured.
|
||||
enum Bar {
|
||||
Unit = 0x45,
|
||||
let mut stream = OStream::new(&mut buf);
|
||||
<$ty as Encode>::encode(&$value, &mut stream).unwrap();
|
||||
|
||||
Pretty(bool) = 127,
|
||||
|
||||
Teacher { initials: [char; 0x3] },
|
||||
let len = stream.close();
|
||||
assert_eq!(&buf[..len], data.as_slice());
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode() {
|
||||
macro_rules! test {
|
||||
($ty:ty: $value:expr => $data:expr) => {{
|
||||
let data = $data;
|
||||
|
||||
let mut buf = vec![0x00; data.len()];
|
||||
|
||||
let mut stream = OStream::new(&mut buf);
|
||||
<$ty as Encode>::encode(&$value, &mut stream).unwrap();
|
||||
|
||||
let len = stream.close();
|
||||
assert_eq!(&buf[..len], data.as_slice());
|
||||
}};
|
||||
}
|
||||
|
||||
test!(u8: 0x00 => [0x00]);
|
||||
test!(u8: 0xFF => [0xFF]);
|
||||
test!(u8: 0x7F => [0x7F]);
|
||||
|
@ -88,17 +74,6 @@ fn test_encode() {
|
|||
test!(Option<()>: None => [0x00]);
|
||||
test!(Option<()>: Some(()) => [0x01]);
|
||||
|
||||
test!(Foo: Foo('\u{FDF2}') => [0x00, 0x00, 0xFD, 0xF2]);
|
||||
|
||||
test!(Bar: Bar::Unit => [0x00, 0x45]);
|
||||
|
||||
test!(Bar: Bar::Pretty(true) => [0x00, 0x7F, 0x01]);
|
||||
|
||||
test!(Bar: Bar::Teacher { initials: ['T', 'L', '\0'] } => [
|
||||
0x00, 0x80, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00,
|
||||
0x00, 0x4C, 0x00, 0x00, 0x00, 0x00,
|
||||
]);
|
||||
|
||||
test!(Vec<u16>: From::from([0x8000, 0x8000, 0x8000]) => [0x00, 0x03, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00]);
|
||||
|
||||
test!(SystemTime: UNIX_EPOCH => [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
|
||||
|
@ -107,3 +82,31 @@ fn test_encode() {
|
|||
|
||||
test!(SystemTime: UNIX_EPOCH + Duration::from_secs(0x1) => [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_derive() {
|
||||
#[derive(Encode, SizedEncode)]
|
||||
#[repr(transparent)]
|
||||
struct Foo(char);
|
||||
|
||||
#[derive(Encode, SizedEncode)]
|
||||
#[repr(u8)]
|
||||
enum Bar {
|
||||
Unit = 0x45,
|
||||
|
||||
Pretty(bool) = 127,
|
||||
|
||||
Teacher { initials: [char; 0x3] },
|
||||
}
|
||||
|
||||
test!(Foo: Foo('\u{FDF2}') => [0x00, 0x00, 0xFD, 0xF2]);
|
||||
|
||||
test!(Bar: Bar::Unit => [0x45]);
|
||||
|
||||
test!(Bar: Bar::Pretty(true) => [0x7F, 0x01]);
|
||||
|
||||
test!(Bar: Bar::Teacher { initials: ['T', 'L', '\0'] } => [
|
||||
0x80, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
|
||||
0x4C, 0x00, 0x00, 0x00, 0x00,
|
||||
]);
|
||||
}
|
43
librum/src/error/bool_decode_error.rs
Normal file
43
librum/src/error/bool_decode_error.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
|
||||
/// A boolean could not be decoded.
|
||||
///
|
||||
/// The encoding scheme for [`bool`] only defines the 8-bit values `0` and `1` (as `false` and `true`, respectively).
|
||||
/// If any other 8-bit is read by <code><bool as [Decode](crate::Decode)>::[decode](crate::Decode::decode)</code>, then an instance of this type is returned.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub struct BoolDecodeError {
|
||||
/// The invalid value.
|
||||
pub value: u8,
|
||||
}
|
||||
|
||||
impl Display for BoolDecodeError {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "expected boolean but got `{:#02X}`", self.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for BoolDecodeError { }
|
46
librum/src/error/c_string_decode_error.rs
Normal file
46
librum/src/error/c_string_decode_error.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
|
||||
/// A C-like string could not be decoded.
|
||||
///
|
||||
/// This error is generatead when <code><[CString](alloc::ffi::CString) as [Decode](crate::Decode)>::[decode](crate::Decode::decode)</code> encounteres a null byte within bounds.
|
||||
///
|
||||
/// Note that although any null value is *theoretically* also the string's null terminator, the implementations for [`CStr`](core::ffi::CStr) and `CString` use the same encoding scheme as [`[u8]`](slice).
|
||||
/// This is mainly for efficiency's sake (as to allow the entire stream to be read at once), but this also allows for the aforementioned case to happen.
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub struct CStringDecodeError {
|
||||
/// The index of the null value.
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
impl Display for CStringDecodeError {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "expected c string but found null value within bounds at '{}`", self.index)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for CStringDecodeError { }
|
46
librum/src/error/char_decode_error.rs
Normal file
46
librum/src/error/char_decode_error.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
|
||||
/// A character could not be decoded.
|
||||
///
|
||||
/// Unicode defines only the code points inclusively between `U+0000` and `U+D7FFF` as well between `U+E0000` and `U+10FFFF` as being valid.
|
||||
/// UTF-32 (the format used by the [`char`] data type) additionally specifies that these code points are padded to 32 bits.
|
||||
///
|
||||
/// The encoding scheme used by `char` yields an untransformed representation (disregarding endian corrections), but this regrettably also leads to many bit patterns being undefined with respect to UTF-32.
|
||||
/// If any of these values is read by <code><char as [Decode](crate::Decode)>::[decode](crate::Decode::decode)</code>, then an instance of this error type is returned.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub struct CharDecodeError {
|
||||
/// The undefined code point.
|
||||
pub code_point: u32,
|
||||
}
|
||||
|
||||
impl Display for CharDecodeError {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "code point U+{:04X} is not defined", self.code_point)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for CharDecodeError { }
|
96
librum/src/error/collection_decode_error.rs
Normal file
96
librum/src/error/collection_decode_error.rs
Normal file
|
@ -0,0 +1,96 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
|
||||
/// A collection could not be decoded.
|
||||
///
|
||||
/// This type is intended as a partially-generic decode error for collections.
|
||||
/// It supports denoting an error for when the collection's length is invalid -- see the [`Length`](Self::Length) variant -- and when an element is invalid -- see the [`Item`](Self::Item)) variant.
|
||||
///
|
||||
/// The most common form of this type is <code>CollectionDecodeError<[Infallible](core::convert::Infallible), [ItemDecodeError](crate::error::ItemDecodeError)<[usize], ..></code>, but this may not always necessarily be the preferred form.
|
||||
///
|
||||
/// An example of a type using a different form is [`SizedStr`](crate::SizedStr), which uses <code>CollectionDecodeError<[`SizeError`](crate::error::SizeError), [Utf8Error](crate::error::Utf8Error)></code>.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub enum CollectionDecodeError<L, I> {
|
||||
/// The collection length could not be decoded or was invalid.
|
||||
///
|
||||
/// For most dynamically-sized collections, the suitable type here is [`Infallible`] due to there basically being no restriction on the collection's size (depending on the data type used for denoting lengths).
|
||||
///
|
||||
/// Sometimes the length isn't even encoded in the stream (instead lying in the type signature), and in these cases the appropriate type would also be `Infallible`.
|
||||
Length(L),
|
||||
|
||||
/// A collection item could not be decoded.
|
||||
///
|
||||
/// Sometimes the index of the item may be desired.
|
||||
/// In these cases the [`ItemDecodeError`](crate::error::ItemDecodeError) could be used here.
|
||||
Item(I),
|
||||
}
|
||||
|
||||
impl<L, I> Display for CollectionDecodeError<L, I>
|
||||
where
|
||||
L: Display,
|
||||
I: Display,
|
||||
{
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
use CollectionDecodeError::*;
|
||||
|
||||
match *self {
|
||||
Length(ref e)
|
||||
=> write!(f, "unable to decode collection length: {e}"),
|
||||
|
||||
Item(ref e)
|
||||
=> write!(f, "unable to decode collection item: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L, I> Error for CollectionDecodeError<L, I>
|
||||
where
|
||||
L: Error + 'static,
|
||||
I: Error + 'static,
|
||||
{
|
||||
#[inline]
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
use CollectionDecodeError::*;
|
||||
|
||||
match *self {
|
||||
Length(ref e) => Some(e),
|
||||
|
||||
Item(ref e) => Some(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L, I> From<CollectionDecodeError<L, I>> for Infallible
|
||||
where
|
||||
L: Into<Self>,
|
||||
I: Into<Self>,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn from(_value: CollectionDecodeError<L, I>) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
86
librum/src/error/collection_encode_error.rs
Normal file
86
librum/src/error/collection_encode_error.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
|
||||
/// A collection could not be encoded.
|
||||
///
|
||||
/// This type is intended as a partially-generic encode error for collections.
|
||||
/// It supports denoting an error for when the collection's length is invalid -- see the [`Length`](Self::Length) variant -- and when an element is invalid -- see the [`Item`](Self::Item)) variant.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub enum CollectionEncodeError<L, I> {
|
||||
/// The collection length could not be encoded.
|
||||
Length(L),
|
||||
|
||||
/// A collection item could not be encoded.
|
||||
Item(I),
|
||||
}
|
||||
|
||||
impl<L, I> Display for CollectionEncodeError<L, I>
|
||||
where
|
||||
L: Display,
|
||||
I: Display,
|
||||
{
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
use CollectionEncodeError::*;
|
||||
|
||||
match *self {
|
||||
Length(ref e)
|
||||
=> write!(f, "unable to encode collection length: {e}"),
|
||||
|
||||
Item(ref e)
|
||||
=> write!(f, "unable to encode collection item: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L, I> Error for CollectionEncodeError<L, I>
|
||||
where
|
||||
L: Error + 'static,
|
||||
I: Error + 'static,
|
||||
{
|
||||
#[inline]
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
use CollectionEncodeError::*;
|
||||
|
||||
match *self {
|
||||
Length(ref e) => Some(e),
|
||||
|
||||
Item(ref e) => Some(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L, I> From<CollectionEncodeError<L, I>> for Infallible
|
||||
where
|
||||
L: Into<Self>,
|
||||
I: Into<Self>,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn from(_value: CollectionEncodeError<L, I>) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
98
librum/src/error/enum_decode_error.rs
Normal file
98
librum/src/error/enum_decode_error.rs
Normal file
|
@ -0,0 +1,98 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::Decode;
|
||||
|
||||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Debug, Display, Formatter};
|
||||
|
||||
/// An invalid enumeration descriminant was provided.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub enum EnumDecodeError<D: Decode, F> {
|
||||
/// The discriminant could not be decoded.
|
||||
InvalidDiscriminant(D::Error),
|
||||
|
||||
/// An otherwise valid discriminant has not been assigned.
|
||||
///
|
||||
/// Remember that this error does **not** indicate that the discriminant couldn't be decoded, merely that it does not match with that of any variant.
|
||||
/// See also [`InvalidDiscriminant`](Self::InvalidDiscriminant).
|
||||
UnassignedDiscriminant {
|
||||
/// The unassigned discriminant value.
|
||||
value: D
|
||||
},
|
||||
|
||||
/// A field could not be encoded.
|
||||
Field(F),
|
||||
}
|
||||
|
||||
impl<D, F> Display for EnumDecodeError<D, F>
|
||||
where
|
||||
D: Decode<Error: Display> + Display,
|
||||
F: Display,
|
||||
{
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
use EnumDecodeError::*;
|
||||
|
||||
match *self {
|
||||
InvalidDiscriminant(ref e)
|
||||
=> write!(f, "discriminant could not be decoded: {e}"),
|
||||
|
||||
UnassignedDiscriminant { ref value }
|
||||
=> write!(f, "`{value}` is not an assigned discriminant for the given enumeration"),
|
||||
|
||||
Field(ref e)
|
||||
=> write!(f, "variant could not be decoded: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, F> Error for EnumDecodeError<D, F>
|
||||
where
|
||||
D: Debug + Decode<Error: Error + 'static> + Display,
|
||||
F: Error + 'static,
|
||||
{
|
||||
#[inline]
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
use EnumDecodeError::*;
|
||||
|
||||
match *self {
|
||||
InvalidDiscriminant(ref e) => Some(e),
|
||||
|
||||
Field(ref e) => Some(e),
|
||||
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, F> From<EnumDecodeError<D, F>> for Infallible
|
||||
where
|
||||
D: Decode<Error: Into<Self>>,
|
||||
F: Into<Self>,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn from(_value: EnumDecodeError<D, F>) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
84
librum/src/error/enum_encode_error.rs
Normal file
84
librum/src/error/enum_encode_error.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::Encode;
|
||||
|
||||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Debug, Display, Formatter};
|
||||
|
||||
/// An invalid enumeration descriminant was provided.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub enum EnumEncodeError<D: Encode, F> {
|
||||
/// The discriminant could not be encoded.
|
||||
Discriminant(D::Error),
|
||||
|
||||
/// A field could not be encoded.
|
||||
Field(F),
|
||||
}
|
||||
|
||||
impl<D, F> Display for EnumEncodeError<D, F>
|
||||
where
|
||||
D: Display + Encode<Error: Display>,
|
||||
F: Display,
|
||||
{
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
use EnumEncodeError::*;
|
||||
|
||||
match *self {
|
||||
Discriminant(ref e)
|
||||
=> write!(f, "discriminant could not be encoded: {e}"),
|
||||
|
||||
Field(ref e)
|
||||
=> write!(f, "field could not be encoded: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, F> Error for EnumEncodeError<D, F>
|
||||
where
|
||||
D: Debug + Display + Encode<Error: Error + 'static>,
|
||||
F: Error + 'static,
|
||||
{
|
||||
#[inline]
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
use EnumEncodeError::*;
|
||||
|
||||
match *self {
|
||||
Discriminant(ref e) => Some(e),
|
||||
|
||||
Field(ref e) => Some(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, F> From<EnumEncodeError<D, F>> for Infallible
|
||||
where
|
||||
D: Encode<Error: Into<Self>>,
|
||||
F: Into<Self>,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn from(_value: EnumEncodeError<D, F>) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
229
librum/src/error/generic_decode_error.rs
Normal file
229
librum/src/error/generic_decode_error.rs
Normal file
|
@ -0,0 +1,229 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{Decode, PrimitiveDiscriminant};
|
||||
use crate::error::{
|
||||
BoolDecodeError,
|
||||
CollectionDecodeError,
|
||||
CStringDecodeError,
|
||||
EnumDecodeError,
|
||||
ItemDecodeError,
|
||||
NonZeroDecodeError,
|
||||
SizeError,
|
||||
Utf8Error,
|
||||
SystemTimeDecodeError,
|
||||
};
|
||||
|
||||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
use core::hint::unreachable_unchecked;
|
||||
|
||||
/// A decoding failed.
|
||||
///
|
||||
/// The intended use of this type is by [derived](derive@Decode) implementations of [`Decode`].
|
||||
/// Manual implementors are recommended to use a custom or less generic type for the sake of efficiency.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
#[non_exhaustive]
|
||||
pub enum GenericDecodeError {
|
||||
/// A string contained a non-UTF-8 sequence.
|
||||
BadString(Utf8Error),
|
||||
|
||||
/// A boolean was neither `false` nor `true`.
|
||||
InvalidBool(BoolDecodeError),
|
||||
|
||||
/// A C-like string contained a null byte.
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
NullString(CStringDecodeError),
|
||||
|
||||
/// A non-null integer was null.
|
||||
NullInteger(NonZeroDecodeError),
|
||||
|
||||
/// A statically-sized buffer was too small.
|
||||
SmallBuffer(SizeError),
|
||||
|
||||
/// An unassigned discriminant value was encountered.
|
||||
///
|
||||
/// The contained value denotes the raw, numerical value of the discriminant.
|
||||
UnassignedDiscriminant {
|
||||
/// The raw value of the discriminant.
|
||||
value: u128
|
||||
},
|
||||
|
||||
/// The [`SystemTime`](std::time::SystemTime) type was too narrow.
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
NarrowSystemTime(SystemTimeDecodeError),
|
||||
}
|
||||
|
||||
impl Display for GenericDecodeError {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
use GenericDecodeError::*;
|
||||
|
||||
match *self {
|
||||
BadString(ref e)
|
||||
=> write!(f, "{e}"),
|
||||
|
||||
InvalidBool(ref e)
|
||||
=> write!(f, "{e}"),
|
||||
|
||||
NullString(ref e)
|
||||
=> write!(f, "{e}"),
|
||||
|
||||
NullInteger(ref e)
|
||||
=> write!(f, "{e}"),
|
||||
|
||||
SmallBuffer(ref e)
|
||||
=> write!(f, "{e}"),
|
||||
|
||||
UnassignedDiscriminant { value }
|
||||
=> write!(f, "discriminant value `{value:#X} has not been assigned"),
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
NarrowSystemTime(ref e)
|
||||
=> write!(f, "{e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for GenericDecodeError {
|
||||
#[inline]
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
use GenericDecodeError::*;
|
||||
|
||||
match *self {
|
||||
BadString(ref e) => Some(e),
|
||||
|
||||
InvalidBool(ref e) => Some(e),
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
NullString(ref e) => Some(e),
|
||||
|
||||
NullInteger(ref e) => Some(e),
|
||||
|
||||
SmallBuffer(ref e) => Some(e),
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
NarrowSystemTime(ref e) => Some(e),
|
||||
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BoolDecodeError> for GenericDecodeError {
|
||||
#[inline(always)]
|
||||
fn from(value: BoolDecodeError) -> Self {
|
||||
Self::InvalidBool(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<L, I> From<CollectionDecodeError<L, I>> for GenericDecodeError
|
||||
where
|
||||
L: Into<Self>,
|
||||
I: Into<Self>,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn from(value: CollectionDecodeError<L, I>) -> Self {
|
||||
use CollectionDecodeError::*;
|
||||
|
||||
match value {
|
||||
Length(e) => e.into(),
|
||||
|
||||
Item(e) => e.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, F> From<EnumDecodeError<D, F>> for GenericDecodeError
|
||||
where
|
||||
D: Decode<Error: Into<Self>> + PrimitiveDiscriminant,
|
||||
F: Into<Self>,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn from(value: EnumDecodeError<D, F>) -> Self {
|
||||
use EnumDecodeError::*;
|
||||
|
||||
match value {
|
||||
InvalidDiscriminant(e) => e.into(),
|
||||
|
||||
UnassignedDiscriminant { value } => Self::UnassignedDiscriminant { value: value.to_u128() },
|
||||
|
||||
Field(e) => e.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Infallible> for GenericDecodeError {
|
||||
#[inline(always)]
|
||||
fn from(_value: Infallible) -> Self {
|
||||
unsafe { unreachable_unchecked() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, E: Into<Self>> From<ItemDecodeError<I, E>> for GenericDecodeError {
|
||||
#[inline(always)]
|
||||
fn from(value: ItemDecodeError<I, E>) -> Self {
|
||||
value.error.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl From<CStringDecodeError> for GenericDecodeError {
|
||||
#[inline(always)]
|
||||
fn from(value: CStringDecodeError) -> Self {
|
||||
Self::NullString(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NonZeroDecodeError> for GenericDecodeError {
|
||||
#[inline(always)]
|
||||
fn from(value: NonZeroDecodeError) -> Self {
|
||||
Self::NullInteger(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SizeError> for GenericDecodeError {
|
||||
#[inline(always)]
|
||||
fn from(value: SizeError) -> Self {
|
||||
Self::SmallBuffer(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl From<SystemTimeDecodeError> for GenericDecodeError {
|
||||
#[inline(always)]
|
||||
fn from(value: SystemTimeDecodeError) -> Self {
|
||||
Self::NarrowSystemTime(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Utf8Error> for GenericDecodeError {
|
||||
#[inline(always)]
|
||||
fn from(value: Utf8Error) -> Self {
|
||||
Self::BadString(value)
|
||||
}
|
||||
}
|
154
librum/src/error/generic_encode_error.rs
Normal file
154
librum/src/error/generic_encode_error.rs
Normal file
|
@ -0,0 +1,154 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::Encode;
|
||||
use crate::error::{
|
||||
CollectionEncodeError,
|
||||
EnumEncodeError,
|
||||
IsizeEncodeError,
|
||||
ItemEncodeError,
|
||||
UsizeEncodeError,
|
||||
};
|
||||
|
||||
use core::cell::BorrowError;
|
||||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
use core::hint::unreachable_unchecked;
|
||||
|
||||
/// A decoding failed.
|
||||
///
|
||||
/// The intended use of this type is by [derived](derive@crate::Encode) implementations of [`Encode`](crate::Encode).
|
||||
/// Manual implementors are recommended to use a custom or less generic type for the sake of efficiency.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
#[non_exhaustive]
|
||||
pub enum GenericEncodeError {
|
||||
/// A [`RefCell`](core::cell::RefCell) object could not be borrowed.
|
||||
BadBorrow(BorrowError),
|
||||
|
||||
/// An `isize` object was outside the allowed domain.
|
||||
LargeIsize(IsizeEncodeError),
|
||||
|
||||
/// A `usize` object was outside the allowed domain.
|
||||
LargeUsize(UsizeEncodeError),
|
||||
}
|
||||
|
||||
impl Display for GenericEncodeError {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
use GenericEncodeError::*;
|
||||
|
||||
let e: &dyn Display = match *self {
|
||||
BadBorrow(ref e) => e,
|
||||
|
||||
LargeIsize(ref e) => e,
|
||||
|
||||
LargeUsize(ref e) => e,
|
||||
};
|
||||
|
||||
e.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for GenericEncodeError {
|
||||
#[inline]
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
use GenericEncodeError::*;
|
||||
|
||||
match *self {
|
||||
BadBorrow(ref e) => Some(e),
|
||||
|
||||
LargeIsize(ref e) => Some(e),
|
||||
|
||||
LargeUsize(ref e) => Some(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BorrowError> for GenericEncodeError {
|
||||
#[inline(always)]
|
||||
fn from(value: BorrowError) -> Self {
|
||||
Self::BadBorrow(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<L, I> From<CollectionEncodeError<L, I>> for GenericEncodeError
|
||||
where
|
||||
L: Into<Self>,
|
||||
I: Into<Self>,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn from(value: CollectionEncodeError<L, I>) -> Self {
|
||||
use CollectionEncodeError::*;
|
||||
|
||||
match value {
|
||||
Length(e) => e.into(),
|
||||
|
||||
Item(e) => e.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, F> From<EnumEncodeError<D, F>> for GenericEncodeError
|
||||
where
|
||||
D: Encode<Error: Into<Self>>,
|
||||
F: Into<Self>,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn from(value: EnumEncodeError<D, F>) -> Self {
|
||||
use EnumEncodeError::*;
|
||||
|
||||
match value {
|
||||
Discriminant(e) => e.into(),
|
||||
|
||||
Field(e) => e.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Infallible> for GenericEncodeError {
|
||||
#[inline(always)]
|
||||
fn from(_value: Infallible) -> Self {
|
||||
unsafe { unreachable_unchecked() }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IsizeEncodeError> for GenericEncodeError {
|
||||
#[inline(always)]
|
||||
fn from(value: IsizeEncodeError) -> Self {
|
||||
Self::LargeIsize(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, E: Into<Self>> From<ItemEncodeError<I, E>> for GenericEncodeError {
|
||||
#[inline(always)]
|
||||
fn from(value: ItemEncodeError<I, E>) -> Self {
|
||||
value.error.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UsizeEncodeError> for GenericEncodeError {
|
||||
#[inline(always)]
|
||||
fn from(value: UsizeEncodeError) -> Self {
|
||||
Self::LargeUsize(value)
|
||||
}
|
||||
}
|
42
librum/src/error/isize_encode_error.rs
Normal file
42
librum/src/error/isize_encode_error.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
|
||||
/// An [`isize`] value could not be decoded.
|
||||
///
|
||||
/// Any `isize` object that can fit in an [`i16`] can be encoded successfully.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub struct IsizeEncodeError(
|
||||
/// The unencodable value.
|
||||
pub isize,
|
||||
);
|
||||
|
||||
impl Display for IsizeEncodeError {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "signed size value ({}) cannot be serialised: must be in the range ({}) to ({})", self.0, i16::MIN, i16::MAX)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for IsizeEncodeError { }
|
66
librum/src/error/item_decode_error.rs
Normal file
66
librum/src/error/item_decode_error.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Debug, Display, Formatter};
|
||||
|
||||
/// A collection's item could not be decoded.
|
||||
///
|
||||
/// See also [`CollectionDecodeError`](crate::error::CollectionDecodeError).
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub struct ItemDecodeError<I, E> {
|
||||
/// The index of the invalid item.
|
||||
pub index: I,
|
||||
|
||||
/// The decoder's error.
|
||||
pub error: E,
|
||||
}
|
||||
|
||||
impl<I, E> Display for ItemDecodeError<I, E>
|
||||
where
|
||||
I: Display,
|
||||
E: Display,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "could not decode item at `{}`: {}", self.index, self.error)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, E> Error for ItemDecodeError<I, E>
|
||||
where
|
||||
Self: Debug + Display,
|
||||
E: Error + 'static,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
Some(&self.error)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, E> From<ItemDecodeError<I, E>> for Infallible {
|
||||
#[inline(always)]
|
||||
fn from(_value: ItemDecodeError<I, E>) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
66
librum/src/error/item_encode_error.rs
Normal file
66
librum/src/error/item_encode_error.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Debug, Display, Formatter};
|
||||
|
||||
/// A collection's item could not be encoded.
|
||||
///
|
||||
/// See also [`CollectionEncodeError`](crate::error::CollectionEncodeError).
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub struct ItemEncodeError<I, E> {
|
||||
/// The index of the invalid item.
|
||||
pub index: I,
|
||||
|
||||
/// The encoder's error.
|
||||
pub error: E,
|
||||
}
|
||||
|
||||
impl<I, E> Display for ItemEncodeError<I, E>
|
||||
where
|
||||
I: Display,
|
||||
E: Display,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "could not encode item at `{}`: {}", self.index, self.error)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, E> Error for ItemEncodeError<I, E>
|
||||
where
|
||||
Self: Debug + Display,
|
||||
E: Error + 'static,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
Some(&self.error)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, E: Into<Self>> From<ItemEncodeError<I, E>> for Infallible {
|
||||
#[inline(always)]
|
||||
fn from(_value: ItemEncodeError<I, E>) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
52
librum/src/error/mod.rs
Normal file
52
librum/src/error/mod.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Error variants.
|
||||
//!
|
||||
//! This module defines the error types used by Librum.
|
||||
//! All of these types define (at least conditionally) the [`Error`](core::error::Error) trait.
|
||||
|
||||
use crate::use_mod;
|
||||
|
||||
use_mod!(pub bool_decode_error);
|
||||
use_mod!(pub char_decode_error);
|
||||
use_mod!(pub collection_decode_error);
|
||||
use_mod!(pub collection_encode_error);
|
||||
use_mod!(pub enum_encode_error);
|
||||
use_mod!(pub enum_decode_error);
|
||||
use_mod!(pub generic_decode_error);
|
||||
use_mod!(pub generic_encode_error);
|
||||
use_mod!(pub isize_encode_error);
|
||||
use_mod!(pub item_decode_error);
|
||||
use_mod!(pub item_encode_error);
|
||||
use_mod!(pub non_zero_decode_error);
|
||||
use_mod!(pub ref_cell_encode_error);
|
||||
use_mod!(pub size_error);
|
||||
use_mod!(pub string_error);
|
||||
use_mod!(pub usize_encode_error);
|
||||
use_mod!(pub utf16_error);
|
||||
use_mod!(pub utf8_error);
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use_mod!(pub c_string_decode_error);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use_mod!(pub system_time_decode_error);
|
38
librum/src/error/non_zero_decode_error.rs
Normal file
38
librum/src/error/non_zero_decode_error.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
|
||||
/// A non-zero integer could not be decoded.
|
||||
///
|
||||
/// The implementations of [`Decode`](crate::Decode) for <code>[NonZero](core::num::NonZero)<T></code> yield this error type if decoding `T` yields zero.
|
||||
#[derive(Debug)]
|
||||
pub struct NonZeroDecodeError;
|
||||
|
||||
impl Display for NonZeroDecodeError {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "expected non-zero integer but found `0`")
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for NonZeroDecodeError { }
|
67
librum/src/error/ref_cell_encode_error.rs
Normal file
67
librum/src/error/ref_cell_encode_error.rs
Normal file
|
@ -0,0 +1,67 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use core::cell::BorrowError;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
|
||||
/// A reference cell could not be encoded.
|
||||
///
|
||||
/// The implementation of <code><[RefCell](core::cell::RefCell)<T> as [Encode](crate::Encode)>::[encode](crate::Encode::encode)</code> will first attempt to call <code>RefCell::[borrow](core::cell::RefCell::borrow)</code>.
|
||||
/// If this call fails, then the returned error is again returned as a [`Borrow`](Self::Borrow) instance.
|
||||
/// If the following call to <code>T::encode</code> fails instead, then the error returned from that call is passed on as a [`Value`](Self::Value) instance.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub enum RefCellEncodeError<E> {
|
||||
/// The reference cell could not be borrowed.
|
||||
Borrow(BorrowError),
|
||||
|
||||
/// The contained value could not be encoded.
|
||||
Value(E),
|
||||
}
|
||||
|
||||
impl<E: Display> Display for RefCellEncodeError<E> {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
use RefCellEncodeError::*;
|
||||
|
||||
let e: &dyn Display = match *self {
|
||||
Borrow(ref e) => e,
|
||||
|
||||
Value(ref e) => e,
|
||||
};
|
||||
|
||||
write!(f, "unable to encode reference cell: {e}")
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Error + 'static> Error for RefCellEncodeError<E> {
|
||||
#[inline(always)]
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
use RefCellEncodeError::*;
|
||||
|
||||
match *self {
|
||||
Borrow(ref e) => Some(e),
|
||||
|
||||
Value(ref e) => Some(e)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,41 +1,48 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
|
||||
/// A fixed-size buffer was too small.
|
||||
///
|
||||
/// Some data types use a statically-sized buffer whilst still allowing for partial usage of this buffer (e.g. [`SizedSlice`](crate::SizedSlice)).
|
||||
///
|
||||
/// Taking `SizedSlice` as an example, it encodes its actual length before encoding each of its elements.
|
||||
/// It is allowed for any smaller-sized `SizedSlice` instance to decode a larger-sized encoding **if** the actual length still fits.
|
||||
/// If not, then this error type is used to denote the error state.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub struct SizeError {
|
||||
/// The required amount of bytes.
|
||||
pub req: usize,
|
||||
|
||||
/// The total capacity of the buffer.
|
||||
pub cap: usize,
|
||||
|
||||
/// The required amount of elements.
|
||||
pub len: usize,
|
||||
}
|
||||
|
||||
impl Display for SizeError {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "collection of size ({}) cannot hold ({}) elements", self.len, self.req)
|
||||
write!(f, "collection of size ({}) cannot hold ({}) elements", self.cap, self.len)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +1,22 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::error::{SizeError, Utf16Error, Utf8Error};
|
||||
|
@ -27,6 +27,7 @@ use core::fmt::{self, Display, Formatter};
|
|||
/// String error variants.
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
#[must_use]
|
||||
pub enum StringError {
|
||||
/// An invalid UTF-16 sequence was encountered.
|
||||
BadUtf16(Utf16Error),
|
||||
|
@ -44,14 +45,14 @@ impl Display for StringError {
|
|||
use StringError::*;
|
||||
|
||||
match *self {
|
||||
BadUtf16(ref source)
|
||||
=> write!(f, "bad utf-16: {source}"),
|
||||
BadUtf16(ref e)
|
||||
=> write!(f, "bad utf-16: {e}"),
|
||||
|
||||
BadUtf8(ref source)
|
||||
=> write!(f, "bad utf-8: {source}"),
|
||||
BadUtf8(ref e)
|
||||
=> write!(f, "bad utf-8: {e}"),
|
||||
|
||||
SmallBuffer(ref source)
|
||||
=> write!(f, "buffer too small: {source}"),
|
||||
SmallBuffer(ref e)
|
||||
=> write!(f, "buffer too small: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -62,14 +63,11 @@ impl Error for StringError {
|
|||
use StringError::*;
|
||||
|
||||
match *self {
|
||||
BadUtf16(ref source)
|
||||
=> Some(source),
|
||||
BadUtf16(ref e) => Some(e),
|
||||
|
||||
BadUtf8(ref source)
|
||||
=> Some(source),
|
||||
BadUtf8(ref e) => Some(e),
|
||||
|
||||
SmallBuffer(ref source)
|
||||
=> Some(source),
|
||||
SmallBuffer(ref e) => Some(e),
|
||||
}
|
||||
}
|
||||
}
|
46
librum/src/error/system_time_decode_error.rs
Normal file
46
librum/src/error/system_time_decode_error.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
|
||||
/// The [`SystemTime`](std::time::SystemTime) type could not represent a UNIX timestamp.
|
||||
///
|
||||
/// Note that a UNIX timestamp is here defined as a signed, 64-bit integer denoting a difference of time to 1 january 1970, as measured in Greenwich using seconds.
|
||||
/// This error should therefore not occur on systems that use the same or a more precise counter.
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub struct SystemTimeDecodeError {
|
||||
/// The unrepresentable timestamp.
|
||||
pub timestamp: i64,
|
||||
}
|
||||
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl Display for SystemTimeDecodeError {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "could not represent `{}` as a system timestamp", self.timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl Error for SystemTimeDecodeError { }
|
42
librum/src/error/usize_encode_error.rs
Normal file
42
librum/src/error/usize_encode_error.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
|
||||
/// A [`usize`] value could not be decoded.
|
||||
///
|
||||
/// Any `usize` object that can fit in an [`u16`] can be encoded successfully.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub struct UsizeEncodeError(
|
||||
/// The unencodable value.
|
||||
pub usize,
|
||||
);
|
||||
|
||||
impl Display for UsizeEncodeError {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "unsigned size value ({}) cannot be serialised: must be at most ({})", self.0, u16::MAX)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for UsizeEncodeError { }
|
|
@ -1,22 +1,22 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use core::error::Error;
|
||||
|
@ -24,6 +24,7 @@ use core::fmt::{self, Display, Formatter};
|
|||
|
||||
/// An invalid UTF-16 sequence was encountered.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
pub struct Utf16Error {
|
||||
/// The invalid UTF-16 hextet.
|
||||
pub value: u16,
|
|
@ -1,22 +1,22 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use core::error::Error;
|
||||
|
@ -24,7 +24,7 @@ use core::fmt::{self, Display, Formatter};
|
|||
|
||||
/// An invalid UTF-8 sequence was encountered.
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
#[must_use]
|
||||
pub struct Utf8Error {
|
||||
/// The invalid UTF-8 octet.
|
||||
pub value: u8,
|
|
@ -1,22 +1,22 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use core::slice;
|
|
@ -1,27 +1,27 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#![doc(html_logo_url = "https://gitlab.com/bjoernager/bzipper/-/raw/master/doc-icon.svg")]
|
||||
#![doc(html_logo_url = "https://gitlab.com/bjoernager/librum/-/raw/master/doc-icon.svg")]
|
||||
|
||||
//! bZipper is a Rust crate for cheaply serialising (encoding) and deserialising (decoding) data structures into binary streams
|
||||
//! Librum is a Rust crate for cheaply serialising (encoding) and deserialising (decoding) data structures into binary streams
|
||||
//!
|
||||
//! What separates this crate from others such as [Bincode](https://crates.io/crates/bincode/) or [Postcard](https://crates.io/crates/postcard/) is that this crate is extensively optimised for *just* binary encodings (whilst the mentioned crates specifically use Serde and build on a more abstract data model).
|
||||
//! The original goal of this project was specifically to guarantee size constraints for encodings on a per-type basis at compile-time.
|
||||
|
@ -34,28 +34,29 @@
|
|||
//!
|
||||
//! # Performance
|
||||
//!
|
||||
//! As bZipper is optimised exclusively for a single, binary format, it may outperform other libraries that are more generic in nature.
|
||||
//! As Librum is optimised exclusively for a single, binary format, it may outperform other libraries that are more generic in nature.
|
||||
//!
|
||||
//! The `bzipper_benchmarks` binary compares multiple scenarios using bZipper and other, similar crates.
|
||||
//! According to my runs on an AMD Ryzen 7 3700X, these benchmarks indicate that bZipper outperform all of the tested crates -- as demonstrated in the following table:
|
||||
//! The `librum-benchmarks` binary compares multiple scenarios using Librum and other, similar crates.
|
||||
//! According to my runs on an AMD Ryzen 7 3700X, these benchmarks indicate that Librum outperform all of the tested crates -- as demonstrated in the following table:
|
||||
//!
|
||||
//! | Benchmark | [Bincode] | [Borsh] | bZipper | [Ciborium] | [Postcard] |
|
||||
//! | :--------------------------------- | --------: | ------: | ------: | ---------: | ---------: |
|
||||
//! | `encode_u8` | 1.234 | 1.096 | 0.881 | 3.076 | 1.223 |
|
||||
//! | `encode_struct_unit` | 0.000 | 0.000 | 0.000 | 0.516 | 0.000 |
|
||||
//! | `encode_struct_unnamed` | 1.367 | 1.154 | 1.009 | 2.051 | 1.191 |
|
||||
//! | `encode_struct_named` | 4.101 | 1.271 | 1.181 | 9.342 | 1.182 |
|
||||
//! | `encode_enum_unit` | 0.306 | 0.008 | 0.000 | 2.304 | 0.004 |
|
||||
//! | **Total time** → | 7.009 | 3.528 | 3.071 | 17.289 | 3.599 |
|
||||
//! | **Total deviation (p.c.)** → | +128 | +15 | ±0 | +463 | +17 |
|
||||
//! | Benchmark | [Bincode] | [Borsh] | Librum | [Postcard] |
|
||||
//! | :--------------------------------- | --------: | ------: | ------: | ---------: |
|
||||
//! | `encode_u8` | 1.306 | 1.315 | 1.150 | 1.304 |
|
||||
//! | `encode_u32` | 1.321 | 1.317 | 1.146 | 3.016 |
|
||||
//! | `encode_u128` | 2.198 | 2.103 | 1.509 | 6.376 |
|
||||
//! | `encode_struct_unit` | 0.000 | 0.000 | 0.000 | 0.000 |
|
||||
//! | `encode_struct_unnamed` | 1.362 | 1.448 | 1.227 | 2.659 |
|
||||
//! | `encode_struct_named` | 3.114 | 1.530 | 0.969 | 3.036 |
|
||||
//! | `encode_enum_unit` | 0.252 | 0.297 | 0.000 | 0.299 |
|
||||
//! | **Total time** → | 9.553 | 8.010 | 6.001 | 16.691 |
|
||||
//! | **Total deviation (p.c.)** → | +59 | +33 | ±0 | +178 |
|
||||
//!
|
||||
//! [Bincode]: https://crates.io/crates/bincode/
|
||||
//! [Borsh]: https://crates.io/crates/borsh/
|
||||
//! [Ciborium]: https://crates.io/crates/ciborium/
|
||||
//! [Postcard]: https://crates.io/crates/postcard/
|
||||
//!
|
||||
//! All quantities are measured in seconds unless otherwise noted.
|
||||
//! Please feel free to conduct your own tests of bZipper.
|
||||
//! Please feel free to conduct your own tests of Librum.
|
||||
//!
|
||||
//! # Data model
|
||||
//!
|
||||
|
@ -64,51 +65,71 @@
|
|||
//!
|
||||
//! See specific types' implementations for notes on their data models.
|
||||
//!
|
||||
//! **Note that the data model is currently not stabilised,** and may not necessarily be in the near future (before [specialisation](https://github.com/rust-lang/rust/issues/31844/)).
|
||||
//! **Note that the data model is currently not stabilised,** and may not necessarily be in the near future (at least before [specialisation](https://github.com/rust-lang/rust/issues/31844/)).
|
||||
//! It may therefore be undesired to store encodings long-term.
|
||||
//!
|
||||
//! # Usage
|
||||
//!
|
||||
//! This crate revolves around the [`Encode`] and [`Decode`] traits which both handle conversions to and from byte streams.
|
||||
//!
|
||||
//! Many standard types come implemented with bZipper, including most primitives as well as some standard library types such as [`Option`] and [`Result`].
|
||||
//! Many standard types come implemented with Librum, including most primitives as well as some standard library types such as [`Option`] and [`Result`].
|
||||
//! Some [features](#feature-flags) enable an extended set of implementations.
|
||||
//!
|
||||
//! It is recommended in most cases to simply derive these two traits for custom types (although this is only supported with enumerations and structures -- not untagged unions).
|
||||
//! Here, each field is *chained* according to declaration order:
|
||||
//!
|
||||
//! ```
|
||||
//! use bzipper::{Buf, Decode, Encode, SizedEncode};
|
||||
//! use librum::{Buf, Decode, Encode};
|
||||
//!
|
||||
//! #[derive(Debug, Decode, PartialEq, SizedEncode)]
|
||||
//! struct IoRegister {
|
||||
//! addr: u32,
|
||||
//! value: u16,
|
||||
//! #[derive(Debug, Decode, Encode, PartialEq)]
|
||||
//! struct Ints {
|
||||
//! value0: u8,
|
||||
//! value1: u16,
|
||||
//! value2: u32,
|
||||
//! value3: u64,
|
||||
//! value4: u128,
|
||||
//! }
|
||||
//!
|
||||
//! let mut buf = Buf::new();
|
||||
//! const VALUE: Ints = Ints {
|
||||
//! value0: 0x00,
|
||||
//! value1: 0x02_01,
|
||||
//! value2: 0x06_05_04_03,
|
||||
//! value3: 0x0E_0D_0C_0B_0A_09_08_07,
|
||||
//! value4: 0x1E_1D_1C_1B_1A_19_18_17_16_15_14_13_12_11_10_0F,
|
||||
//! };
|
||||
//!
|
||||
//! buf.write(IoRegister { addr: 0x04000000, value: 0x0402 }).unwrap();
|
||||
//! let mut buf = Buf::with_capacity(0x100);
|
||||
//!
|
||||
//! assert_eq!(buf.len(), 0x6);
|
||||
//! assert_eq!(buf, [0x04, 0x00, 0x00, 0x00, 0x04, 0x02].as_slice());
|
||||
//! buf.write(VALUE).unwrap();
|
||||
//!
|
||||
//! assert_eq!(buf.read().unwrap(), IoRegister { addr: 0x04000000, value: 0x0402 });
|
||||
//! assert_eq!(buf.len(), 0x1F);
|
||||
//!
|
||||
//! assert_eq!(
|
||||
//! buf,
|
||||
//! [
|
||||
//! 0x00, 0x02, 0x01, 0x06, 0x05, 0x04, 0x03, 0x0E,
|
||||
//! 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x1E,
|
||||
//! 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16,
|
||||
//! 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F,
|
||||
//! ].as_slice(),
|
||||
//! );
|
||||
//!
|
||||
//! assert_eq!(buf.read().unwrap(), VALUE);
|
||||
//! ```
|
||||
//!
|
||||
//! ## Buffer types
|
||||
//!
|
||||
//! The [`Encode`] and [`Decode`] traits both rely on streams for carrying the manipulated byte streams.
|
||||
//! The [`Encode`] and [`Decode`] traits both rely on streams for carrying the manipulated bytes.
|
||||
//!
|
||||
//! These streams are separated into two type: [*O-streams*](OStream) (output streams) and [*i-streams*](IStream) (input streams).
|
||||
//! Often, but not always, the [`Buf`] type is preferred over directly calling the [`encode`](Encode::encode) and [`decode`](Decode::decode) methods.
|
||||
//! The [`Buf`] type can be used to handle these streams.
|
||||
//!
|
||||
//! ## Encoding
|
||||
//!
|
||||
//! To encode an object directly using the [`Encode`] trait, simply allocate a buffer for the encoding and wrap it in an [`OStream`] object:
|
||||
//! To encode an object directly using the `Encode` trait, simply allocate a buffer for the encoding and wrap it in an [`OStream`] object:
|
||||
//!
|
||||
//! ```
|
||||
//! use bzipper::{Encode, OStream, SizedEncode};
|
||||
//! use librum::{Encode, OStream, SizedEncode};
|
||||
//!
|
||||
//! let mut buf = [0x00; char::MAX_ENCODED_SIZE];
|
||||
//! let mut stream = OStream::new(&mut buf);
|
||||
|
@ -121,7 +142,7 @@
|
|||
//! Streams can also be used to chain multiple objects together:
|
||||
//!
|
||||
//! ```
|
||||
//! use bzipper::{Encode, OStream, SizedEncode};
|
||||
//! use librum::{Encode, OStream, SizedEncode};
|
||||
//!
|
||||
//! let mut buf = [0x0; char::MAX_ENCODED_SIZE * 0x5];
|
||||
//! let mut stream = OStream::new(&mut buf);
|
||||
|
@ -154,7 +175,7 @@
|
|||
//! To decode a byte array, simply call the [`decode`](Decode::decode) method with an [`IStream`] object:
|
||||
//!
|
||||
//! ```
|
||||
//! use bzipper::{Decode, IStream};
|
||||
//! use librum::{Decode, IStream};
|
||||
//!
|
||||
//! let data = [0x45, 0x54];
|
||||
//! let mut stream = IStream::new(&data);
|
||||
|
@ -180,13 +201,13 @@
|
|||
//! A UDP server/client for geographic data:
|
||||
//!
|
||||
//! ```
|
||||
//! use bzipper::{Buf, Decode, SizedEncode};
|
||||
//! use librum::{Buf, Encode, Decode, SizedEncode};
|
||||
//! use std::io;
|
||||
//! use std::net::{SocketAddr, ToSocketAddrs, UdpSocket};
|
||||
//! use std::thread::spawn;
|
||||
//!
|
||||
//! // City, region, etc.:
|
||||
//! #[derive(Clone, Copy, Debug, Decode, Eq, PartialEq, SizedEncode)]
|
||||
//! #[derive(Clone, Copy, Debug, Decode, Encode, Eq, PartialEq, SizedEncode)]
|
||||
//! enum Area {
|
||||
//! AlQuds,
|
||||
//! Byzantion,
|
||||
|
@ -196,7 +217,7 @@
|
|||
//! }
|
||||
//!
|
||||
//! // Client-to-server message:
|
||||
//! #[derive(Debug, Decode, PartialEq, SizedEncode)]
|
||||
//! #[derive(Debug, Decode, Encode, PartialEq, SizedEncode)]
|
||||
//! enum Request {
|
||||
//! AtmosphericHumidity { area: Area },
|
||||
//! AtmosphericPressure { area: Area },
|
||||
|
@ -205,7 +226,7 @@
|
|||
//! }
|
||||
//!
|
||||
//! // Server-to-client message:
|
||||
//! #[derive(Debug, Decode, PartialEq, SizedEncode)]
|
||||
//! #[derive(Debug, Decode, Encode, PartialEq, SizedEncode)]
|
||||
//! enum Response {
|
||||
//! AtmosphericHumidity(f64),
|
||||
//! AtmosphericPressure(f64), // Pascal
|
||||
|
@ -281,25 +302,28 @@
|
|||
//!
|
||||
//! # Feature flags
|
||||
//!
|
||||
//! bZipper defines the following features:
|
||||
//! Librum defines the following features:
|
||||
//!
|
||||
//! * `alloc` (default): Enables the [`Buf`] type and implementations for e.g. [`Box`](alloc::boxed::Box) and [`Arc`](alloc::sync::Arc)
|
||||
//! * `std` (default): Enables implementations for types such as [`Mutex`](std::sync::Mutex) and [`RwLock`](std::sync::RwLock)
|
||||
//! * *`alloc`: Enables the [`Buf`] type and implementations for e.g. [`Box`](alloc::boxed::Box) and [`Arc`](alloc::sync::Arc)
|
||||
//! * *`proc-macro`: Pulls the procedural macros from the [`librum_macros`](https://crates.io/crates/librum_macros/) crate
|
||||
//! * *`std`: Enables implementations for types such as [`Mutex`](std::sync::Mutex) and [`RwLock`](std::sync::RwLock)
|
||||
//!
|
||||
//! Features marked with * are enabled by default.
|
||||
//!
|
||||
//! # Documentation
|
||||
//!
|
||||
//! bZipper has its documentation written in-source for use by `rustdoc`.
|
||||
//! See [Docs.rs](https://docs.rs/bzipper/latest/bzipper/) for an on-line, rendered instance.
|
||||
//! Librum has its documentation written in-source for use by `rustdoc`.
|
||||
//! See [Docs.rs](https://docs.rs/librum/latest/librum/) for an on-line, rendered instance.
|
||||
//!
|
||||
//! Currently, these docs make use of some unstable features for the sake of readability.
|
||||
//! The nightly toolchain is therefore required when rendering them.
|
||||
//!
|
||||
//! # Contribution
|
||||
//!
|
||||
//! bZipper does not accept source code contributions at the moment.
|
||||
//! Librum does not accept source code contributions at the moment.
|
||||
//! This is a personal choice by the maintainer and may be undone in the future.
|
||||
//!
|
||||
//! Do however feel free to open up an issue on [`GitLab`](https://gitlab.com/bjoernager/bzipper/issues/) or (preferably) [`GitHub`](https://github.com/bjoernager/bzipper/issues/) if you feel the need to express any concerns over the project.
|
||||
//! Do however feel free to open up an issue on [`GitLab`](https://gitlab.com/bjoernager/librum/issues/) or (preferably) [`GitHub`](https://github.com/bjoernager/librum/issues/) if you feel the need to express any concerns over the project.
|
||||
//!
|
||||
//! # Copyright & Licence
|
||||
//!
|
||||
|
@ -319,7 +343,7 @@
|
|||
#![cfg_attr(doc, feature(doc_cfg, rustdoc_internals))]
|
||||
|
||||
// For use in macros:
|
||||
extern crate self as bzipper;
|
||||
extern crate self as librum;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
|
@ -330,20 +354,30 @@ extern crate std;
|
|||
/// Implements [`Decode`] for the provided type.
|
||||
///
|
||||
/// This macro assumes the same format used by the equivalent [`Encode`](derive@Encode) macro.
|
||||
#[cfg(feature = "proc-macro")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "proc-macro")))]
|
||||
#[doc(inline)]
|
||||
pub use bzipper_macros::Decode;
|
||||
pub use librum_macros::Decode;
|
||||
|
||||
/// Implements [`Encode`] for the provided type.
|
||||
///
|
||||
/// Note that if all fields additionally implement [`SizedEncode`](trait@SizedEncode), then the [`SizedEncode`](derive@SizedEncode) derive macro is usually prefered instead.
|
||||
/// This derive macro assumes that all fields implement <code>Encode<[Error]: [Into]<[GenericEncodeError]>></code>.
|
||||
/// If this is **not** the case, then the trait should be implemented manually instead.
|
||||
///
|
||||
/// [Error]: Encode::Error
|
||||
/// [GenericEncodeError]: crate::error::GenericEncodeError
|
||||
///
|
||||
/// Do also consider deriving [`SizedEncode`](derive@SizedEncode) -- if possible.
|
||||
///
|
||||
/// # Structs
|
||||
///
|
||||
/// For structures, each element is chained in **order of declaration.**
|
||||
/// If the structure is a unit structure (i.e. it has *no* fields) then it is encoded equivalently to the [unit] type.
|
||||
///
|
||||
/// For example, the following struct will encode its field `foo` followed by `bar`:
|
||||
///
|
||||
/// ```
|
||||
/// use bzipper::Encode;
|
||||
/// use librum::Encode;
|
||||
///
|
||||
/// #[derive(Encode)]
|
||||
/// struct FooBar {
|
||||
|
@ -352,9 +386,9 @@ pub use bzipper_macros::Decode;
|
|||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This should be kept in mind when changing the structure's declaration as doing so may invalidate previous encodings.
|
||||
/// This should be kept in mind when changing the structure's declarationm as doing so may invalidate previous encodings.
|
||||
///
|
||||
/// If the structure is a unit structure (i.e. it has *no* fields) then it is encoded equivalently to the [unit] type.
|
||||
/// The [`Error`](Encode::Error) type will in all cases just be `GenericEncodeError`.
|
||||
///
|
||||
/// # Enums
|
||||
///
|
||||
|
@ -365,9 +399,9 @@ pub use bzipper_macros::Decode;
|
|||
/// Unspecified discriminants then increment the previous variant's discriminant:
|
||||
///
|
||||
/// ```
|
||||
/// use bzipper::{Buf, SizedEncode};
|
||||
/// use librum::{Buf, Encode};
|
||||
///
|
||||
/// #[derive(SizedEncode)]
|
||||
/// #[derive(Encode)]
|
||||
/// enum Num {
|
||||
/// Two = 0x2,
|
||||
///
|
||||
|
@ -378,7 +412,7 @@ pub use bzipper_macros::Decode;
|
|||
/// One,
|
||||
/// }
|
||||
///
|
||||
/// let mut buf = Buf::new();
|
||||
/// let mut buf = Buf::with_capacity(size_of::<i16>());
|
||||
///
|
||||
/// buf.write(Num::Zero).unwrap();
|
||||
/// assert_eq!(buf, [0x00, 0x00].as_slice());
|
||||
|
@ -396,25 +430,36 @@ pub use bzipper_macros::Decode;
|
|||
/// Variants with fields are encoded exactly like structures.
|
||||
/// That is, each field is chained in order of declaration.
|
||||
///
|
||||
/// For error handling, the `Error` type is defined as:
|
||||
///
|
||||
/// <code>[EnumEncodeError]<<Repr as Encode>::Error, GenericEncodeError></code>,
|
||||
///
|
||||
/// [EnumEncodeError]: crate::error::GenericEncodeError
|
||||
///
|
||||
/// wherein `Repr` is the enumeration's representation.
|
||||
///
|
||||
/// # Unions
|
||||
///
|
||||
/// Unions cannot derive `Encode` due to the uncertainty of their contents.
|
||||
/// The trait should therefore be implemented manually for such types.
|
||||
#[cfg(feature = "proc-macro")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "proc-macro")))]
|
||||
#[doc(inline)]
|
||||
pub use bzipper_macros::Encode;
|
||||
pub use librum_macros::Encode;
|
||||
|
||||
/// Implements [`Encode`](trait@Encode) and [`SizedEncode`] for the given type.
|
||||
/// Implements [`Encode`](trait@Encode) using the default implementation.
|
||||
///
|
||||
/// See also the [`Encode`](derive@Encode) derive macro for how the resulting encoder is implemented.
|
||||
/// For simple structures, the value of [`MAX_ENCODED_SIZE`](SizedEncode::MAX_ENCODED_SIZE) is set as the combined value of <code>T*n*::MAX_ENCODED_SIZE</code> wherein <code>T*n*</code> is the type of each field.
|
||||
///
|
||||
/// For simple structures, the value of [`MAX_ENCODED_SIZE`](SizedEncode::MAX_ENCODED_SIZE) is set to the combined value of all fields' own definition.
|
||||
///
|
||||
/// For enumerations, each variant has its own `MAX_ENCODED_SIZE` value calculated as if it was an equivalent structure (additionally containing the discriminant).
|
||||
/// The largest of these values is then chosen as the enumeration type's actual `MAX_ENCODED_SIZE` value.
|
||||
/// For enumerations, the value is set such that each variant is treated like a structure (with the discriminant as an extra field) and where the variant that produces the largest `MAX_ENCODED_SIZE` is chosen.
|
||||
///
|
||||
/// As untagged unions cannot derive `Encode`, `SizedEncode` also cannot be derived for them.
|
||||
///
|
||||
/// Do remember that deriving this trait is only recommended
|
||||
#[cfg(feature = "proc-macro")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "proc-macro")))]
|
||||
#[doc(inline)]
|
||||
pub use bzipper_macros::SizedEncode;
|
||||
pub use librum_macros::SizedEncode;
|
||||
|
||||
macro_rules! use_mod {
|
||||
($vis:vis $name:ident$(,)?) => {
|
||||
|
@ -425,9 +470,11 @@ macro_rules! use_mod {
|
|||
pub(crate) use use_mod;
|
||||
|
||||
use_mod!(pub decode);
|
||||
use_mod!(pub decode_borrowed);
|
||||
use_mod!(pub encode);
|
||||
use_mod!(pub i_stream);
|
||||
use_mod!(pub o_stream);
|
||||
use_mod!(pub primitive_discriminant);
|
||||
use_mod!(pub sized_encode);
|
||||
use_mod!(pub sized_iter);
|
||||
use_mod!(pub sized_slice);
|
|
@ -1,22 +1,22 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use core::ptr::copy_nonoverlapping;
|
||||
|
@ -65,7 +65,6 @@ impl<'a> OStream<'a> {
|
|||
/// Closes the stream.
|
||||
///
|
||||
/// The total ammount of bytes written is returned.
|
||||
#[expect(clippy::must_use_candidate)]
|
||||
#[inline(always)]
|
||||
pub const fn close(self) -> usize {
|
||||
let Self { pos, .. } = self;
|
73
librum/src/primitive_discriminant/mod.rs
Normal file
73
librum/src/primitive_discriminant/mod.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
mod sealed {
|
||||
/// Denotes a primitive, integral discriminant type.
|
||||
///
|
||||
/// See the public [`PrimitiveDiscriminant`](crate::PrimitiveDiscriminant) trait for more information.
|
||||
pub trait PrimitiveDiscriminant {
|
||||
/// Interprets the discriminant value as `u128`.
|
||||
///
|
||||
/// The returned value has exactly the same representation as the original value except that it is zero-extended to fit.
|
||||
#[must_use]
|
||||
fn to_u128(self) -> u128;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use sealed::PrimitiveDiscriminant as SealedPrimitiveDiscriminant;
|
||||
|
||||
/// Denotes a primitive, integral discriminant type.
|
||||
///
|
||||
/// This type is specifically defined as a type which may be used as a representation in the `repr` attribute, i.e. [`u8`], [`i8`], [`u16`], [`i16`], [`u32`], [`i32`], [`u64`], [`i64`], [`usize`], and [`isize`].
|
||||
///
|
||||
/// On nightly, this additionally includes [`u128`] and [`i128`] (see [`repr128`](https://github.com/rust-lang/rust/issues/56071/)).
|
||||
/// Note that this trait is implemented for these two types regardless.
|
||||
///
|
||||
/// Internally -- specifically in the [`GenericDecodeError`](crate::error::GenericDecodeError) enumeration -- this trait guarantees representability in the `u128` type.
|
||||
pub trait PrimitiveDiscriminant: SealedPrimitiveDiscriminant + Sized { }
|
||||
|
||||
macro_rules! impl_primitive_discriminant {
|
||||
($ty:ty) => {
|
||||
impl ::librum::SealedPrimitiveDiscriminant for $ty {
|
||||
#[allow(clippy::cast_lossless)]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn to_u128(self) -> u128 {
|
||||
self as u128
|
||||
}
|
||||
}
|
||||
|
||||
impl ::librum::PrimitiveDiscriminant for $ty { }
|
||||
};
|
||||
}
|
||||
|
||||
impl_primitive_discriminant!(u8);
|
||||
impl_primitive_discriminant!(i8);
|
||||
impl_primitive_discriminant!(u16);
|
||||
impl_primitive_discriminant!(i16);
|
||||
impl_primitive_discriminant!(u32);
|
||||
impl_primitive_discriminant!(i32);
|
||||
impl_primitive_discriminant!(u64);
|
||||
impl_primitive_discriminant!(i64);
|
||||
impl_primitive_discriminant!(u128);
|
||||
impl_primitive_discriminant!(i128);
|
||||
impl_primitive_discriminant!(usize);
|
||||
impl_primitive_discriminant!(isize);
|
|
@ -1,26 +1,26 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
mod tests;
|
||||
|
||||
use crate::Encode;
|
||||
|
||||
|
@ -56,7 +56,7 @@ use alloc::boxed::Box;
|
|||
#[cfg(feature = "alloc")]
|
||||
use alloc::rc::Rc;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
|
||||
use alloc::sync::Arc;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
|
@ -70,157 +70,159 @@ use std::time::SystemTime;
|
|||
/// When using [`Encode`], the size of the resulting encoding cannot always be known beforehand.
|
||||
/// This trait defines an upper bound for these sizes.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Users of the `Encode` and [`Decode`](crate::Decode) traits may assume that the [`MAX_ENCODED_SIZE`](Self::MAX_ENCODED_SIZE) constant is properly defined and that no encoding will be larger than this value.
|
||||
/// Implementors must therefore guarantee that **no** call to [`encode`](Encode::encode) or [`decode`](bzipper::Decode::decode) consumes more bytes than specified by this constant.
|
||||
pub unsafe trait SizedEncode: Encode + Sized {
|
||||
/// Note that -- in practice -- this trait is **not** strictly enforceable.
|
||||
/// Users of the `Encode` and [`Decode`](crate::Decode) traits may assume that this trait is properly defined, but should still leave room for the possibility that it isn't.
|
||||
pub trait SizedEncode: Encode + Sized {
|
||||
/// The maximum guaranteed amount of bytes that can result from an encoding.
|
||||
///
|
||||
/// Implementors of this trait should make sure that no encoding (or decoding) uses more than the amount specified by this constant.
|
||||
/// Implementors of this trait should make sure that no encoding (or decoding) consumes more than the amount specified by this constant.
|
||||
const MAX_ENCODED_SIZE: usize;
|
||||
}
|
||||
|
||||
unsafe impl<T: SizedEncode> SizedEncode for &T {
|
||||
impl<T: SizedEncode> SizedEncode for &T {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
unsafe impl<T: SizedEncode> SizedEncode for &mut T {
|
||||
impl<T: SizedEncode> SizedEncode for &mut T {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
/// Implemented for tuples with up to twelve members.
|
||||
#[cfg_attr(doc, doc(fake_variadic))]
|
||||
unsafe impl<T: SizedEncode> SizedEncode for (T, ) {
|
||||
impl<T: SizedEncode> SizedEncode for (T, ) {
|
||||
#[doc(hidden)]
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
unsafe impl<T: SizedEncode, const N: usize> SizedEncode for [T; N] {
|
||||
impl<T: SizedEncode, const N: usize> SizedEncode for [T; N] {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE * N;
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
unsafe impl<T: SizedEncode> SizedEncode for Arc<T> {
|
||||
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
|
||||
#[cfg_attr(doc, doc(cfg(all(feature = "alloc", target_has_atomic = "ptr"))))]
|
||||
impl<T: SizedEncode> SizedEncode for Arc<T> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
unsafe impl SizedEncode for bool {
|
||||
impl SizedEncode for bool {
|
||||
const MAX_ENCODED_SIZE: usize = u8::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
unsafe impl<T: SizedEncode> SizedEncode for Bound<T> {
|
||||
impl<T: SizedEncode> SizedEncode for Bound<T> {
|
||||
const MAX_ENCODED_SIZE: usize = 0x0;
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
unsafe impl<T: SizedEncode> SizedEncode for Box<T> {
|
||||
impl<T: SizedEncode> SizedEncode for Box<T> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
unsafe impl<T: Copy + SizedEncode> SizedEncode for Cell<T> {
|
||||
impl<T: Copy + SizedEncode> SizedEncode for Cell<T> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
unsafe impl SizedEncode for char {
|
||||
impl SizedEncode for char {
|
||||
const MAX_ENCODED_SIZE: usize = u32::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
unsafe impl<T: SizedEncode + ToOwned> SizedEncode for Cow<'_, T> {
|
||||
impl<T: SizedEncode + ToOwned> SizedEncode for Cow<'_, T> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
unsafe impl SizedEncode for Duration {
|
||||
impl SizedEncode for Duration {
|
||||
const MAX_ENCODED_SIZE: usize =
|
||||
u64::MAX_ENCODED_SIZE
|
||||
+ u32::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
unsafe impl SizedEncode for Infallible {
|
||||
impl SizedEncode for Infallible {
|
||||
const MAX_ENCODED_SIZE: usize = 0x0;
|
||||
}
|
||||
|
||||
unsafe impl SizedEncode for IpAddr {
|
||||
impl SizedEncode for IpAddr {
|
||||
const MAX_ENCODED_SIZE: usize = u8::MAX_ENCODED_SIZE + Ipv6Addr::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
unsafe impl SizedEncode for Ipv4Addr {
|
||||
impl SizedEncode for Ipv4Addr {
|
||||
const MAX_ENCODED_SIZE: usize = u32::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
unsafe impl SizedEncode for Ipv6Addr {
|
||||
impl SizedEncode for Ipv6Addr {
|
||||
const MAX_ENCODED_SIZE: usize = u128::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
unsafe impl SizedEncode for isize {
|
||||
impl SizedEncode for isize {
|
||||
const MAX_ENCODED_SIZE: usize = i16::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
unsafe impl<T: SizedEncode> SizedEncode for LazyCell<T> {
|
||||
impl<T: SizedEncode> SizedEncode for LazyCell<T> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
unsafe impl<T: SizedEncode> SizedEncode for LazyLock<T> {
|
||||
impl<T: SizedEncode> SizedEncode for LazyLock<T> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
unsafe impl<T: SizedEncode> SizedEncode for Mutex<T> {
|
||||
impl<T: SizedEncode> SizedEncode for Mutex<T> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
unsafe impl<T: SizedEncode> SizedEncode for Option<T> {
|
||||
impl<T: SizedEncode> SizedEncode for Option<T> {
|
||||
const MAX_ENCODED_SIZE: usize =
|
||||
bool::MAX_ENCODED_SIZE
|
||||
+ T::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
unsafe impl<T> SizedEncode for PhantomData<T> {
|
||||
impl<T> SizedEncode for PhantomData<T> {
|
||||
const MAX_ENCODED_SIZE: usize = 0x0;
|
||||
}
|
||||
|
||||
unsafe impl<T: SizedEncode> SizedEncode for Range<T> {
|
||||
impl<T: SizedEncode> SizedEncode for Range<T> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE * 0x2;
|
||||
}
|
||||
|
||||
unsafe impl<T: SizedEncode> SizedEncode for RangeFrom<T> {
|
||||
impl<T: SizedEncode> SizedEncode for RangeFrom<T> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
unsafe impl SizedEncode for RangeFull {
|
||||
impl SizedEncode for RangeFull {
|
||||
const MAX_ENCODED_SIZE: usize = 0x0;
|
||||
}
|
||||
|
||||
unsafe impl<T: SizedEncode> SizedEncode for RangeInclusive<T> {
|
||||
impl<T: SizedEncode> SizedEncode for RangeInclusive<T> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE * 0x2;
|
||||
}
|
||||
|
||||
unsafe impl<T: SizedEncode> SizedEncode for RangeTo<T> {
|
||||
impl<T: SizedEncode> SizedEncode for RangeTo<T> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
unsafe impl<T: SizedEncode> SizedEncode for RangeToInclusive<T> {
|
||||
impl<T: SizedEncode> SizedEncode for RangeToInclusive<T> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
unsafe impl<T: SizedEncode> SizedEncode for Rc<T> {
|
||||
impl<T: SizedEncode> SizedEncode for Rc<T> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
unsafe impl<T: SizedEncode> SizedEncode for RefCell<T> {
|
||||
impl<T: SizedEncode> SizedEncode for RefCell<T> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
unsafe impl<T: SizedEncode, E: SizedEncode> SizedEncode for core::result::Result<T, E> {
|
||||
impl<T, E, Err> SizedEncode for core::result::Result<T, E>
|
||||
where
|
||||
T: SizedEncode + Encode<Error = Err>,
|
||||
E: SizedEncode + Encode<Error = Err>,
|
||||
{
|
||||
const MAX_ENCODED_SIZE: usize =
|
||||
bool::MAX_ENCODED_SIZE
|
||||
+ if size_of::<T>() > size_of::<E>() { size_of::<T>() } else { size_of::<E>() };
|
||||
|
@ -228,24 +230,24 @@ unsafe impl<T: SizedEncode, E: SizedEncode> SizedEncode for core::result::Result
|
|||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
unsafe impl<T: SizedEncode> SizedEncode for RwLock<T> {
|
||||
impl<T: SizedEncode> SizedEncode for RwLock<T> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
unsafe impl<T: SizedEncode> SizedEncode for Saturating<T> {
|
||||
impl<T: SizedEncode> SizedEncode for Saturating<T> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
unsafe impl SizedEncode for SocketAddr {
|
||||
impl SizedEncode for SocketAddr {
|
||||
const MAX_ENCODED_SIZE: usize = u8::MAX_ENCODED_SIZE + SocketAddrV6::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
unsafe impl SizedEncode for SocketAddrV4 {
|
||||
impl SizedEncode for SocketAddrV4 {
|
||||
const MAX_ENCODED_SIZE: usize = Ipv4Addr::MAX_ENCODED_SIZE + u16::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
/// This implementation encodes the address's bits followed by the port number, all of which in big-endian.
|
||||
unsafe impl SizedEncode for SocketAddrV6 {
|
||||
impl SizedEncode for SocketAddrV6 {
|
||||
const MAX_ENCODED_SIZE: usize =
|
||||
Ipv6Addr::MAX_ENCODED_SIZE
|
||||
+ u16::MAX_ENCODED_SIZE
|
||||
|
@ -255,25 +257,25 @@ unsafe impl SizedEncode for SocketAddrV6 {
|
|||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
unsafe impl SizedEncode for SystemTime {
|
||||
impl SizedEncode for SystemTime {
|
||||
const MAX_ENCODED_SIZE: usize = i64::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
unsafe impl SizedEncode for () {
|
||||
impl SizedEncode for () {
|
||||
const MAX_ENCODED_SIZE: usize = 0x0;
|
||||
}
|
||||
|
||||
unsafe impl SizedEncode for usize {
|
||||
impl SizedEncode for usize {
|
||||
const MAX_ENCODED_SIZE: Self = u16::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
unsafe impl<T: SizedEncode> SizedEncode for Wrapping<T> {
|
||||
impl<T: SizedEncode> SizedEncode for Wrapping<T> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
macro_rules! impl_numeric {
|
||||
($ty:ty$(,)?) => {
|
||||
unsafe impl ::bzipper::SizedEncode for $ty {
|
||||
impl ::librum::SizedEncode for $ty {
|
||||
const MAX_ENCODED_SIZE: usize = size_of::<$ty>();
|
||||
}
|
||||
};
|
||||
|
@ -284,16 +286,18 @@ macro_rules! impl_tuple {
|
|||
$($tys:ident),+$(,)?
|
||||
} => {
|
||||
#[doc(hidden)]
|
||||
unsafe impl<$($tys: ::bzipper::SizedEncode, )*> ::bzipper::SizedEncode for ($($tys, )*) {
|
||||
const MAX_ENCODED_SIZE: usize = 0x0 $(+ <$tys as ::bzipper::SizedEncode>::MAX_ENCODED_SIZE)*;
|
||||
impl<$($tys, )* E> ::librum::SizedEncode for ($($tys, )*)
|
||||
where
|
||||
$($tys: SizedEncode + Encode<Error = E>, )* {
|
||||
const MAX_ENCODED_SIZE: usize = 0x0 $(+ <$tys as ::librum::SizedEncode>::MAX_ENCODED_SIZE)*;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_non_zero {
|
||||
($ty:ty$(,)?) => {
|
||||
unsafe impl ::bzipper::SizedEncode for ::core::num::NonZero<$ty> {
|
||||
const MAX_ENCODED_SIZE: usize = <$ty as ::bzipper::SizedEncode>::MAX_ENCODED_SIZE;
|
||||
impl ::librum::SizedEncode for ::core::num::NonZero<$ty> {
|
||||
const MAX_ENCODED_SIZE: usize = <$ty as ::librum::SizedEncode>::MAX_ENCODED_SIZE;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -306,8 +310,8 @@ macro_rules! impl_atomic {
|
|||
} => {
|
||||
#[cfg(target_has_atomic = $width)]
|
||||
#[cfg_attr(doc, doc(cfg(target_has_atomic = $width)))]
|
||||
unsafe impl ::bzipper::SizedEncode for $atomic_ty {
|
||||
const MAX_ENCODED_SIZE: usize = <$ty as ::bzipper::SizedEncode>::MAX_ENCODED_SIZE;
|
||||
impl ::librum::SizedEncode for $atomic_ty {
|
||||
const MAX_ENCODED_SIZE: usize = <$ty as ::librum::SizedEncode>::MAX_ENCODED_SIZE;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,24 +1,25 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bzipper.
|
||||
//test!(you can redistribut => []);
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bzipper is distributed in the hope that it will
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bzipper. If
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use bzipper::{SizedStr, SizedEncode};
|
||||
use librum::{Encode, SizedStr, SizedEncode};
|
||||
use std::convert::Infallible;
|
||||
use std::marker::PhantomData;
|
||||
use std::net::{
|
||||
|
@ -31,28 +32,14 @@ use std::net::{
|
|||
};
|
||||
use std::num::NonZero;
|
||||
|
||||
#[derive(SizedEncode)]
|
||||
struct Foo(char);
|
||||
|
||||
#[derive(SizedEncode)]
|
||||
#[expect(dead_code)]
|
||||
#[repr(u8)] // Not honoured.
|
||||
enum Bar {
|
||||
Unit = 0x45,
|
||||
|
||||
Pretty(bool) = 127,
|
||||
|
||||
Teacher { initials: [char; 0x3] },
|
||||
macro_rules! assert_encoded_size {
|
||||
($ty:ty, $value:expr$(,)?) => {{
|
||||
assert_eq!(<$ty as ::librum::SizedEncode>::MAX_ENCODED_SIZE, $value);
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sized_encode() {
|
||||
macro_rules! assert_encoded_size {
|
||||
($ty:ty, $value:expr$(,)?) => {{
|
||||
assert_eq!(<$ty as ::bzipper::SizedEncode>::MAX_ENCODED_SIZE, $value);
|
||||
}};
|
||||
}
|
||||
|
||||
assert_encoded_size!(bool, 0x1);
|
||||
assert_encoded_size!(char, 0x4);
|
||||
assert_encoded_size!(f32, 0x4);
|
||||
|
@ -63,7 +50,7 @@ fn test_sized_encode() {
|
|||
assert_encoded_size!(i64, 0x8);
|
||||
assert_encoded_size!(i8, 0x1);
|
||||
assert_encoded_size!(isize, 0x2);
|
||||
assert_encoded_size!(SizedStr::<0x45>, 0x47);
|
||||
assert_encoded_size!(SizedStr::<0x45>, 0x47);
|
||||
assert_encoded_size!(Infallible, 0x0);
|
||||
assert_encoded_size!(IpAddr, 0x11);
|
||||
assert_encoded_size!(Ipv4Addr, 0x4);
|
||||
|
@ -91,7 +78,24 @@ fn test_sized_encode() {
|
|||
assert_encoded_size!(u8, 0x1);
|
||||
assert_encoded_size!(usize, 0x2);
|
||||
assert_encoded_size!((), 0x0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sized_encode_derive() {
|
||||
#[derive(Encode, SizedEncode)]
|
||||
struct Foo(char);
|
||||
|
||||
#[derive(Encode, SizedEncode)]
|
||||
#[expect(dead_code)]
|
||||
#[repr(i64)]
|
||||
enum Bar {
|
||||
Unit = 0x45,
|
||||
|
||||
Pretty(bool) = 127,
|
||||
|
||||
Teacher { initials: [char; 0x3] },
|
||||
}
|
||||
|
||||
assert_encoded_size!(Foo, 0x4);
|
||||
assert_encoded_size!(Bar, 0xE);
|
||||
assert_encoded_size!(Bar, 0x14);
|
||||
}
|
|
@ -1,26 +1,26 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
mod tests;
|
||||
|
||||
use core::iter::{DoubleEndedIterator, ExactSizeIterator, FusedIterator};
|
||||
use core::mem::MaybeUninit;
|
||||
|
@ -87,7 +87,6 @@ impl<T, const N: usize> AsRef<[T]> for SizedIter<T, N> {
|
|||
}
|
||||
|
||||
impl<T: Clone, const N: usize> Clone for SizedIter<T, N> {
|
||||
#[expect(clippy::borrow_deref_ref)] // Clippy is gaslighting me into believing pointers and references are identical???
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
let mut buf: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
|
|
@ -1,25 +1,25 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use bzipper::{SizedSlice, SizedStr};
|
||||
use librum::{SizedSlice, SizedStr};
|
||||
|
||||
#[test]
|
||||
fn test_sized_iter_clone() {
|
96
librum/src/sized_slice/cmp.rs
Normal file
96
librum/src/sized_slice/cmp.rs
Normal file
|
@ -0,0 +1,96 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::SizedSlice;
|
||||
|
||||
use core::cmp::Ordering;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::vec::Vec;
|
||||
|
||||
impl<T: Eq, const N: usize> Eq for SizedSlice<T, N> { }
|
||||
|
||||
impl<T: Ord, const N: usize> Ord for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.as_slice().cmp(other.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq<U>, U: PartialEq<T>, const N: usize, const M: usize> PartialEq<SizedSlice<U, M>> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &SizedSlice<U, M>) -> bool {
|
||||
self.as_slice() == other.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq<U>, U: PartialEq<T>, const N: usize, const M: usize> PartialEq<[U; M]> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &[U; M]) -> bool {
|
||||
self.as_slice() == other.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq<U>, U: PartialEq<T>, const N: usize> PartialEq<&[U]> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &&[U]) -> bool {
|
||||
self.as_slice() == *other
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: PartialEq<U>, U: PartialEq<T>, const N: usize> PartialEq<Vec<U>> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Vec<U>) -> bool {
|
||||
self.as_slice() == other.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialOrd, const N: usize, const M: usize> PartialOrd<SizedSlice<T, M>> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &SizedSlice<T, M>) -> Option<Ordering> {
|
||||
self.as_slice().partial_cmp(other.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialOrd, const N: usize, const M: usize> PartialOrd<[T; M]> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &[T; M]) -> Option<Ordering> {
|
||||
self.as_slice().partial_cmp(other.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialOrd, const N: usize> PartialOrd<&[T]> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &&[T]) -> Option<Ordering> {
|
||||
self.as_slice().partial_cmp(*other)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: PartialOrd, const N: usize> PartialOrd<Vec<T>> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &Vec<T>) -> Option<Ordering> {
|
||||
self.as_slice().partial_cmp(other.as_slice())
|
||||
}
|
||||
}
|
230
librum/src/sized_slice/conv.rs
Normal file
230
librum/src/sized_slice/conv.rs
Normal file
|
@ -0,0 +1,230 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::SizedSlice;
|
||||
use crate::error::SizeError;
|
||||
|
||||
use core::borrow::{Borrow, BorrowMut};
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::ptr::copy_nonoverlapping;
|
||||
use core::slice;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::alloc::{alloc, Layout};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::boxed::Box;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::vec::Vec;
|
||||
|
||||
impl<T, const N: usize> SizedSlice<T, N> {
|
||||
/// Constructs a fixed-size vector from raw parts.
|
||||
///
|
||||
/// The provided parts are not tested in any way.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The value of `len` may not exceed that of `N`.
|
||||
/// Additionally, all elements of `buf` in the range specified by `len` must be initialised.
|
||||
///
|
||||
/// If any of these requirements are violated, behaviour is undefined.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const unsafe fn from_raw_parts(buf: [MaybeUninit<T>; N], len: usize) -> Self {
|
||||
debug_assert!(len <= N, "cannot construct vector longer than its capacity");
|
||||
|
||||
Self { buf, len }
|
||||
}
|
||||
|
||||
/// Gets a pointer to the first element.
|
||||
///
|
||||
/// The pointed-to element may not necessarily be initialised.
|
||||
/// See [`len`](Self::len) for more information.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn as_ptr(&self) -> *const T {
|
||||
self.buf.as_ptr().cast()
|
||||
}
|
||||
|
||||
/// Gets a mutable pointer to the first element.
|
||||
///
|
||||
/// The pointed-to element may not necessarily be initialised.
|
||||
/// See [`len`](Self::len) for more information.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn as_mut_ptr(&mut self) -> *mut T {
|
||||
self.buf.as_mut_ptr().cast()
|
||||
}
|
||||
|
||||
/// Borrows the vector as a slice.
|
||||
///
|
||||
/// The range of the returned slice only includes the elements specified by [`len`](Self::len).
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn as_slice(&self) -> &[T] {
|
||||
let ptr = self.as_ptr();
|
||||
let len = self.len();
|
||||
|
||||
unsafe { slice::from_raw_parts(ptr, len) }
|
||||
}
|
||||
|
||||
/// Borrows the vector as a mutable slice.
|
||||
///
|
||||
/// The range of the returned slice only includes the elements specified by [`len`](Self::len).
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn as_mut_slice(&mut self) -> &mut [T] {
|
||||
let ptr = self.as_mut_ptr();
|
||||
let len = self.len();
|
||||
|
||||
unsafe { slice::from_raw_parts_mut(ptr, len) }
|
||||
}
|
||||
|
||||
/// Destructs the vector into its raw parts.
|
||||
///
|
||||
/// The returned values are valid to pass on to [`from_raw_parts`](Self::from_raw_parts).
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn into_raw_parts(self) -> ([MaybeUninit<T>; N], usize) {
|
||||
let Self { buf, len } = self;
|
||||
(buf, len)
|
||||
}
|
||||
|
||||
/// Converts the vector into a boxed slice.
|
||||
///
|
||||
/// The vector is reallocated using the global allocator.
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
#[must_use]
|
||||
pub fn into_boxed_slice(self) -> Box<[T]> {
|
||||
let (buf, len) = self.into_raw_parts();
|
||||
|
||||
unsafe {
|
||||
let layout = Layout::array::<T>(len).unwrap();
|
||||
let ptr = alloc(layout).cast::<T>();
|
||||
|
||||
assert!(!ptr.is_null(), "allocation failed");
|
||||
|
||||
copy_nonoverlapping(buf.as_ptr().cast(), ptr, len);
|
||||
|
||||
let slice = core::ptr::slice_from_raw_parts_mut(ptr, len);
|
||||
Box::from_raw(slice)
|
||||
|
||||
// `self.buf` is dropped without destructors being
|
||||
// run.
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the vector into a dynamic vector.
|
||||
///
|
||||
/// The vector is reallocated using the global allocator.
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn into_vec(self) -> Vec<T> {
|
||||
self.into_boxed_slice().into_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> AsMut<[T]> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.as_mut_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> AsRef<[T]> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Borrow<[T]> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &[T] {
|
||||
self.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> BorrowMut<[T]> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn borrow_mut(&mut self) -> &mut [T] {
|
||||
self.as_mut_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Deref for SizedSlice<T, N> {
|
||||
type Target = [T];
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> DerefMut for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.as_mut_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> From<[T; N]> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn from(value: [T; N]) -> Self {
|
||||
unsafe {
|
||||
let buf = value.as_ptr().cast::<[MaybeUninit<T>; N]>().read();
|
||||
|
||||
Self { buf, len: N }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, const N: usize> TryFrom<&[T]> for SizedSlice<T, N> {
|
||||
type Error = SizeError;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(value: &[T]) -> Result<Self, Self::Error> {
|
||||
Self::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T, const N: usize> From<SizedSlice<T, N>> for Box<[T]> {
|
||||
#[inline(always)]
|
||||
fn from(value: SizedSlice<T, N>) -> Self {
|
||||
value.into_boxed_slice()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T, const N: usize> From<SizedSlice<T, N>> for Vec<T> {
|
||||
#[inline(always)]
|
||||
fn from(value: SizedSlice<T, N>) -> Self {
|
||||
value.into_vec()
|
||||
}
|
||||
}
|
79
librum/src/sized_slice/iter.rs
Normal file
79
librum/src/sized_slice/iter.rs
Normal file
|
@ -0,0 +1,79 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{SizedIter, SizedSlice};
|
||||
|
||||
use core::mem::MaybeUninit;
|
||||
use core::slice;
|
||||
|
||||
impl<T, const N: usize> FromIterator<T> for SizedSlice<T, N> {
|
||||
#[inline]
|
||||
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
|
||||
let mut iter = iter.into_iter();
|
||||
|
||||
let mut buf: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
|
||||
let mut len = 0x0;
|
||||
|
||||
for item in &mut buf {
|
||||
let Some(value) = iter.next() else { break };
|
||||
item.write(value);
|
||||
|
||||
len += 0x1;
|
||||
}
|
||||
|
||||
Self { buf, len }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> IntoIterator for SizedSlice<T, N> {
|
||||
type Item = T;
|
||||
|
||||
type IntoIter = SizedIter<T, N>;
|
||||
|
||||
#[inline(always)]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
let Self { buf, len } = self;
|
||||
|
||||
unsafe { SizedIter::new(buf, len) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, const N: usize> IntoIterator for &'a SizedSlice<T, N> {
|
||||
type Item = &'a T;
|
||||
|
||||
type IntoIter = slice::Iter<'a, T>;
|
||||
|
||||
#[inline(always)]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, const N: usize> IntoIterator for &'a mut SizedSlice<T, N> {
|
||||
type Item = &'a mut T;
|
||||
|
||||
type IntoIter = slice::IterMut<'a, T>;
|
||||
|
||||
#[inline(always)]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter_mut()
|
||||
}
|
||||
}
|
277
librum/src/sized_slice/mod.rs
Normal file
277
librum/src/sized_slice/mod.rs
Normal file
|
@ -0,0 +1,277 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use crate::error::SizeError;
|
||||
|
||||
use core::fmt::{self, Debug, Formatter};
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ops::{Index, IndexMut};
|
||||
use core::ptr::{copy_nonoverlapping, null, null_mut};
|
||||
use core::slice::SliceIndex;
|
||||
|
||||
// Conversion facilities:
|
||||
mod conv;
|
||||
|
||||
// Comparison facilities:
|
||||
mod cmp;
|
||||
|
||||
// Iterator facilities:
|
||||
mod iter;
|
||||
|
||||
// Encode/decode facilities:
|
||||
mod serdes;
|
||||
|
||||
/// Stack-allocated vector with maximum length.
|
||||
///
|
||||
/// This type is intended as a [sized-encodable](crate::SizedEncode) alternative to [`Vec`](alloc::vec::Vec) -- for cases where [arrays](array) may not be wanted -- as well as a [decodable](crate::Decode) alternative to normal [slices](slice).
|
||||
///
|
||||
/// Note that this type is immutable in the sense that it does **not** define methods like `push` and `pop`, unlike `Vec`.
|
||||
///
|
||||
/// See [`SizedStr`](crate::SizedStr) for an equivalent alternative to [`String`](alloc::string::String).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// All instances of this type with the same `T` and `N` also have the exact same layout:
|
||||
///
|
||||
/// ```
|
||||
/// use librum::SizedSlice;
|
||||
///
|
||||
/// let vec0 = SizedSlice::<u8, 0x4>::try_from([0x3].as_slice()).unwrap();
|
||||
/// let vec1 = SizedSlice::<u8, 0x4>::try_from([0x3, 0x2].as_slice()).unwrap();
|
||||
/// let vec2 = SizedSlice::<u8, 0x4>::try_from([0x3, 0x2, 0x4].as_slice()).unwrap();
|
||||
/// let vec3 = SizedSlice::<u8, 0x4>::try_from([0x3, 0x2, 0x4, 0x3].as_slice()).unwrap();
|
||||
///
|
||||
/// assert_eq!(size_of_val(&vec0), size_of_val(&vec1));
|
||||
/// assert_eq!(size_of_val(&vec0), size_of_val(&vec2));
|
||||
/// assert_eq!(size_of_val(&vec0), size_of_val(&vec3));
|
||||
/// assert_eq!(size_of_val(&vec1), size_of_val(&vec2));
|
||||
/// assert_eq!(size_of_val(&vec1), size_of_val(&vec3));
|
||||
/// assert_eq!(size_of_val(&vec2), size_of_val(&vec3));
|
||||
/// ```
|
||||
pub struct SizedSlice<T, const N: usize> {
|
||||
buf: [MaybeUninit<T>; N],
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl<T, const N: usize> SizedSlice<T, N> {
|
||||
/// Copies elements from a slice.
|
||||
#[inline]
|
||||
pub const fn copy_from_slice(&mut self, data: &[T])
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
unsafe {
|
||||
let src = data.as_ptr();
|
||||
let dst = self.buf.as_mut_ptr().cast();
|
||||
let count = data.len();
|
||||
|
||||
// SAFETY: `T` implements `Copy`.
|
||||
copy_nonoverlapping(src, dst, count);
|
||||
|
||||
self.set_len(count);
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a sized slice referencing the elements of `self`.
|
||||
#[inline]
|
||||
pub const fn each_ref(&self) -> SizedSlice<&T, N> {
|
||||
let mut buf = [null::<T>(); N];
|
||||
let len = self.len;
|
||||
|
||||
let mut i = 0x0;
|
||||
while i < len {
|
||||
unsafe {
|
||||
let item = buf.as_mut_ptr().add(i);
|
||||
|
||||
let value = self.as_ptr().add(i).cast();
|
||||
item.write(value);
|
||||
}
|
||||
|
||||
i += 0x1;
|
||||
}
|
||||
|
||||
// SAFETY: `*const T` has the same layout as
|
||||
// `MaybeUninit<&T>`, and every relavent pointer
|
||||
// has been initialised as a valid reference.
|
||||
let buf = unsafe { (&raw const buf).cast::<[MaybeUninit<&T>; N]>().read() };
|
||||
|
||||
unsafe { SizedSlice::from_raw_parts(buf, len) }
|
||||
}
|
||||
|
||||
/// Generates a sized slice mutably referencing the elements of `self`.
|
||||
#[inline]
|
||||
pub const fn each_mut(&mut self) -> SizedSlice<&mut T, N> {
|
||||
let mut buf = [null_mut::<T>(); N];
|
||||
let len = self.len;
|
||||
|
||||
let mut i = 0x0;
|
||||
while i < len {
|
||||
unsafe {
|
||||
let item = buf.as_mut_ptr().add(i);
|
||||
|
||||
let value = self.as_mut_ptr().add(i).cast();
|
||||
item.write(value);
|
||||
}
|
||||
|
||||
i += 0x1;
|
||||
}
|
||||
|
||||
// SAFETY: `*mut T` has the same layout as
|
||||
// `MaybeUninit<&mut T>`, and every relavent point-
|
||||
// er has been initialised as a valid reference.
|
||||
let buf = unsafe { (&raw const buf).cast::<[MaybeUninit<&mut T>; N]>().read() };
|
||||
|
||||
unsafe { SizedSlice::from_raw_parts(buf, len) }
|
||||
}
|
||||
|
||||
/// Sets the length of the vector.
|
||||
///
|
||||
/// The provided length is not tested in any way.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The new length `len` may not be larger than `N`.
|
||||
///
|
||||
/// It is only valid to enlarge vectors if `T` supports being in a purely uninitialised state.
|
||||
/// Such is permitted with e.g. [`MaybeUninit`].
|
||||
#[inline(always)]
|
||||
pub const unsafe fn set_len(&mut self, len: usize) {
|
||||
debug_assert!(len <= N, "cannot set length past bounds");
|
||||
|
||||
self.len = len
|
||||
}
|
||||
|
||||
/// Returns the total capacity of the vector.
|
||||
///
|
||||
/// By definition, this is always exactly equal to the value of `N`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn capacity(&self) -> usize {
|
||||
N
|
||||
}
|
||||
|
||||
/// Returns the length of the vector.
|
||||
///
|
||||
/// This value may necessarily be smaller than `N`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
|
||||
/// Checks if the vector is empty, i.e. no elements are recorded.
|
||||
///
|
||||
/// Note that the internal buffer may still contain objects that have been "shadowed" by setting a smaller length with [`len`](Self::len).
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn is_empty(&self) -> bool {
|
||||
self.len() == 0x0
|
||||
}
|
||||
|
||||
/// Checks if the vector is full, i.e. it cannot hold any more elements.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn is_full(&self) -> bool {
|
||||
self.len() == self.capacity()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, const N: usize> SizedSlice<T, N> {
|
||||
/// Constructs an empty, fixed-size vector.
|
||||
#[inline]
|
||||
pub fn new(data: &[T]) -> Result<Self, SizeError> {
|
||||
let mut buf: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
|
||||
|
||||
let len = data.len();
|
||||
if len > N { return Err(SizeError { cap: N, len }) };
|
||||
|
||||
for (item, value) in buf.iter_mut().zip(data.iter()) {
|
||||
item.write(value.clone());
|
||||
}
|
||||
|
||||
Ok(Self { buf, len })
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, const N: usize> Clone for SizedSlice<T, N> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let mut buf: [MaybeUninit<T>; N] = MaybeUninit::uninit().assume_init();
|
||||
|
||||
for i in 0x0..self.len() {
|
||||
let value = self.get_unchecked(i).clone();
|
||||
buf.get_unchecked_mut(i).write(value);
|
||||
}
|
||||
|
||||
Self { buf, len: self.len }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug, const N: usize> Debug for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
Debug::fmt(self.as_slice(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Default for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
unsafe {
|
||||
// SAFETY: Always safe.
|
||||
let buf = MaybeUninit::uninit().assume_init();
|
||||
|
||||
// SAFETY: The resulting slice is zero lengthed.
|
||||
Self::from_raw_parts(buf, 0x0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash, const N: usize> Hash for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
for v in self {
|
||||
v.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I: SliceIndex<[T]>, const N: usize> Index<I> for SizedSlice<T, N> {
|
||||
type Output = I::Output;
|
||||
|
||||
#[inline(always)]
|
||||
fn index(&self, index: I) -> &Self::Output {
|
||||
self.get(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I: SliceIndex<[T]>, const N: usize> IndexMut<I> for SizedSlice<T, N> {
|
||||
#[inline(always)]
|
||||
fn index_mut(&mut self, index: I) -> &mut Self::Output {
|
||||
self.get_mut(index).unwrap()
|
||||
}
|
||||
}
|
69
librum/src/sized_slice/serdes.rs
Normal file
69
librum/src/sized_slice/serdes.rs
Normal file
|
@ -0,0 +1,69 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{
|
||||
Decode,
|
||||
DecodeBorrowed,
|
||||
Encode,
|
||||
IStream,
|
||||
OStream,
|
||||
SizedEncode,
|
||||
SizedSlice
|
||||
};
|
||||
use crate::error::{CollectionDecodeError, ItemDecodeError, SizeError};
|
||||
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
impl<T: Decode, const N: usize> Decode for SizedSlice<T, N> {
|
||||
type Error = CollectionDecodeError<SizeError, ItemDecodeError<usize, T::Error>>;
|
||||
|
||||
#[inline]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let len = Decode::decode(stream).unwrap();
|
||||
if len > N { return Err(CollectionDecodeError::Length(SizeError { cap: N, len })) };
|
||||
|
||||
let mut buf: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
|
||||
|
||||
for (i, slot) in buf.iter_mut().enumerate() {
|
||||
let v = Decode::decode(stream)
|
||||
.map_err(|e| CollectionDecodeError::Item(ItemDecodeError { index: i, error: e }))?;
|
||||
|
||||
slot.write(v);
|
||||
}
|
||||
|
||||
Ok(Self { buf, len })
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decode, const N: usize> DecodeBorrowed<[T]> for SizedSlice<T, N> { }
|
||||
|
||||
impl<T: Encode, const N: usize> Encode for SizedSlice<T, N> {
|
||||
type Error = <[T] as Encode>::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
self.as_slice().encode(stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SizedEncode, const N: usize> SizedEncode for SizedSlice<T, N> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE * N;
|
||||
}
|
|
@ -1,26 +1,26 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use bzipper::SizedSlice;
|
||||
use librum::SizedSlice;
|
||||
use std::vec::Vec;
|
||||
|
||||
#[test]
|
||||
fn test_sized_slice_from_iter() {
|
80
librum/src/sized_str/cmp.rs
Normal file
80
librum/src/sized_str/cmp.rs
Normal file
|
@ -0,0 +1,80 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::SizedStr;
|
||||
|
||||
use core::cmp::Ordering;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::string::String;
|
||||
|
||||
impl<const N: usize> Ord for SizedStr<N> {
|
||||
#[inline(always)]
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.as_str().cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize, const M: usize> PartialEq<SizedStr<M>> for SizedStr<N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &SizedStr<M>) -> bool {
|
||||
self.as_str() == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> PartialEq<&str> for SizedStr<N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &&str) -> bool {
|
||||
self.as_str() == *other
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<const N: usize> PartialEq<String> for SizedStr<N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &String) -> bool {
|
||||
self.as_str() == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize, const M: usize> PartialOrd<SizedStr<M>> for SizedStr<N> {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &SizedStr<M>) -> Option<Ordering> {
|
||||
self.as_str().partial_cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> PartialOrd<&str> for SizedStr<N> {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &&str) -> Option<Ordering> {
|
||||
self.as_str().partial_cmp(*other)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<const N: usize> PartialOrd<String> for SizedStr<N> {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &String) -> Option<Ordering> {
|
||||
self.as_str().partial_cmp(other.as_str())
|
||||
}
|
||||
}
|
|
@ -1,54 +1,33 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
use crate::{
|
||||
Decode,
|
||||
Encode,
|
||||
IStream,
|
||||
OStream,
|
||||
SizedEncode,
|
||||
SizedSlice,
|
||||
};
|
||||
use crate::error::{
|
||||
DecodeError,
|
||||
EncodeError,
|
||||
SizeError,
|
||||
StringError,
|
||||
Utf8Error,
|
||||
};
|
||||
use crate::{SizedSlice, SizedStr};
|
||||
use crate::error::{SizeError, StringError, Utf8Error};
|
||||
|
||||
use core::borrow::{Borrow, BorrowMut};
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt::{self, Debug, Display, Formatter};
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::mem::{ManuallyDrop, MaybeUninit};
|
||||
use core::ops::{Deref, DerefMut, Index, IndexMut};
|
||||
use core::ptr::{addr_of, copy_nonoverlapping};
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::ptr::copy_nonoverlapping;
|
||||
use core::slice;
|
||||
use core::slice::SliceIndex;
|
||||
use core::str;
|
||||
use core::str::{Chars, CharIndices, FromStr};
|
||||
use core::str::{self, FromStr};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::boxed::Box;
|
||||
|
@ -65,62 +44,7 @@ use std::net::ToSocketAddrs;
|
|||
#[cfg(feature = "std")]
|
||||
use std::path::Path;
|
||||
|
||||
/// Stack-allocated string with maximum length.
|
||||
///
|
||||
/// This is in contrast to [`String`] -- which has no size limit in practice -- and [`prim@str`], which is unsized.
|
||||
///
|
||||
/// The string itself is encoded in UTF-8 for interoperability wtih Rust's standard string facilities, and partly due to memory concerns.
|
||||
///
|
||||
/// Keep in mind that the size limit specified by `N` denotes *bytes* (octets) and **not** *characters* -- i.e. a value of `8` may translate to between two and eight characters due to variable-length encoding.
|
||||
///
|
||||
/// See [`SizedSlice`] for an equivalent alternative to [`Vec`](alloc::vec::Vec).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// All instances of this type have the same size if the value of `N` is also the same.
|
||||
/// Therefore, the following four strings have -- despite their different contents -- the same total size.
|
||||
///
|
||||
/// ```
|
||||
/// use bzipper::SizedStr;
|
||||
/// use std::str::FromStr;
|
||||
///
|
||||
/// let str0 = SizedStr::<0x40>::default(); // Empty string.
|
||||
/// let str1 = SizedStr::<0x40>::from_str("Hello there!").unwrap();
|
||||
/// let str2 = SizedStr::<0x40>::from_str("أنا من أوروپا").unwrap();
|
||||
/// let str3 = SizedStr::<0x40>::from_str("COGITO ERGO SUM").unwrap();
|
||||
///
|
||||
/// assert_eq!(size_of_val(&str0), size_of_val(&str1));
|
||||
/// assert_eq!(size_of_val(&str0), size_of_val(&str2));
|
||||
/// assert_eq!(size_of_val(&str0), size_of_val(&str3));
|
||||
/// assert_eq!(size_of_val(&str1), size_of_val(&str2));
|
||||
/// assert_eq!(size_of_val(&str1), size_of_val(&str3));
|
||||
/// assert_eq!(size_of_val(&str2), size_of_val(&str3));
|
||||
/// ```
|
||||
///
|
||||
/// These three strings can -- by extend in theory -- also interchange their contents between each other.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct SizedStr<const N: usize>(SizedSlice<u8, N>);
|
||||
|
||||
impl<const N: usize> SizedStr<N> {
|
||||
/// Constructs an empty, fixed-size string.
|
||||
///
|
||||
/// Note that string is not required to completely fill out its size-constraint.
|
||||
///
|
||||
/// The constructed string will have a null length.
|
||||
///
|
||||
/// For constructing a string with an already defined buffer, see [`from_raw_parts`](Self::from_raw_parts) and [`from_str`](Self::from_str).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If the internal buffer cannot contain the entirety of `s`, then an error is returned.
|
||||
#[inline(always)]
|
||||
pub const fn new(s: &str) -> Result<Self, StringError> {
|
||||
if s.len() > N { return Err(StringError::SmallBuffer(SizeError { req: s.len(), len: N })) };
|
||||
|
||||
let this = unsafe { Self::from_utf8_unchecked(s.as_bytes()) };
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
/// Constructs a fixed-size string from UTF-8 octets.
|
||||
///
|
||||
/// The passed slice is checked for its validity.
|
||||
|
@ -131,7 +55,7 @@ impl<const N: usize> SizedStr<N> {
|
|||
/// Each byte value must be a valid UTF-8 code point.
|
||||
#[inline]
|
||||
pub const fn from_utf8(data: &[u8]) -> Result<Self, StringError> {
|
||||
if data.len() > N { return Err(StringError::SmallBuffer(SizeError { req: data.len(), len: N })) };
|
||||
if data.len() > N { return Err(StringError::SmallBuffer(SizeError { cap: N, len: data.len() })) };
|
||||
|
||||
let s = match str::from_utf8(data) {
|
||||
Ok(s) => s,
|
||||
|
@ -144,8 +68,8 @@ impl<const N: usize> SizedStr<N> {
|
|||
}
|
||||
};
|
||||
|
||||
// SAFETY: `s` is guaranteed to only contain valid
|
||||
// octets.
|
||||
// SAFETY: `s` has been tested to only contain
|
||||
// valid octets.
|
||||
let this = unsafe { Self::from_utf8_unchecked(s.as_bytes()) };
|
||||
Ok(this)
|
||||
}
|
||||
|
@ -159,7 +83,6 @@ impl<const N: usize> SizedStr<N> {
|
|||
#[inline]
|
||||
#[must_use]
|
||||
pub const unsafe fn from_utf8_unchecked(s: &[u8]) -> Self {
|
||||
// Should we assert the length?
|
||||
debug_assert!(s.len() <= N, "cannot construct string from utf-8 sequence that is longer");
|
||||
|
||||
let mut buf = [0x00; N];
|
||||
|
@ -186,8 +109,7 @@ impl<const N: usize> SizedStr<N> {
|
|||
pub const unsafe fn from_raw_parts(buf: [u8; N], len: usize) -> Self {
|
||||
debug_assert!(len <= N, "cannot construct string that is longer than its capacity");
|
||||
|
||||
let init_buf = ManuallyDrop::new(buf);
|
||||
let buf = unsafe { addr_of!(init_buf).cast::<[MaybeUninit<u8>; N]>().read() };
|
||||
let buf = unsafe { buf.as_ptr().cast::<[MaybeUninit<u8>; N]>().read() };
|
||||
|
||||
Self(SizedSlice::from_raw_parts(buf, len))
|
||||
}
|
||||
|
@ -231,6 +153,8 @@ impl<const N: usize> SizedStr<N> {
|
|||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn as_str(&self) -> &str {
|
||||
// SAFETY: We guarantee that all octets are valid
|
||||
// UTF-8 code points.
|
||||
unsafe { core::str::from_utf8_unchecked(self.as_bytes()) }
|
||||
}
|
||||
|
||||
|
@ -244,55 +168,11 @@ impl<const N: usize> SizedStr<N> {
|
|||
let ptr = self.as_mut_ptr();
|
||||
let len = self.len();
|
||||
|
||||
let bytes = slice::from_raw_parts_mut(ptr, len);
|
||||
core::str::from_utf8_unchecked_mut(bytes)
|
||||
let data = slice::from_raw_parts_mut(ptr, len);
|
||||
core::str::from_utf8_unchecked_mut(data)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the total capacity of the string.
|
||||
///
|
||||
/// This is defined as being exactly the value of `N`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn capacity(&self) -> usize {
|
||||
self.0.capacity()
|
||||
}
|
||||
|
||||
/// Returns the length of the string.
|
||||
///
|
||||
/// This does not necessarily equate to the value of `N`, as the internal buffer may be used but partially.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
/// Checks if the string is empty, i.e. no characters are contained.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
/// Checks if the string is full, i.e. it cannot hold any more characters.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn is_full(&self) -> bool {
|
||||
self.0.is_full()
|
||||
}
|
||||
|
||||
/// Returns an iterator of the string's characters.
|
||||
#[inline(always)]
|
||||
pub fn chars(&self) -> Chars {
|
||||
self.as_str().chars()
|
||||
}
|
||||
|
||||
/// Returns an iterator of the string's characters along with their positions.
|
||||
#[inline(always)]
|
||||
pub fn char_indices(&self) -> CharIndices {
|
||||
self.as_str().char_indices()
|
||||
}
|
||||
|
||||
/// Destructs the provided string into its raw parts.
|
||||
///
|
||||
/// The returned values are valid to pass on to [`from_raw_parts`](Self::from_raw_parts).
|
||||
|
@ -306,7 +186,7 @@ impl<const N: usize> SizedStr<N> {
|
|||
let (buf, len) = vec.into_raw_parts();
|
||||
|
||||
let init_buf = ManuallyDrop::new(buf);
|
||||
let buf = unsafe { addr_of!(init_buf).cast::<[u8; N]>().read() };
|
||||
let buf = unsafe { (&raw const init_buf).cast::<[u8; N]>().read() };
|
||||
|
||||
(buf, len)
|
||||
}
|
||||
|
@ -394,13 +274,6 @@ impl<const N: usize> BorrowMut<str> for SizedStr<N> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Debug for SizedStr<N> {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
Debug::fmt(self.as_str(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Deref for SizedStr<N> {
|
||||
type Target = str;
|
||||
|
||||
|
@ -417,162 +290,18 @@ impl<const N: usize> DerefMut for SizedStr<N> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Decode for SizedStr<N> {
|
||||
#[inline]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||
let len = Decode::decode(stream)?;
|
||||
|
||||
let data = stream.read(len);
|
||||
|
||||
Self::from_utf8(data)
|
||||
.map_err(|e| match e {
|
||||
StringError::BadUtf8(e) => DecodeError::BadString(e),
|
||||
|
||||
StringError::SmallBuffer(e) => DecodeError::SmallBuffer(e),
|
||||
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Display for SizedStr<N> {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
Display::fmt(self.as_str(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Encode for SizedStr<N> {
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||
// Optimised encode. Don't just rely on `SizedSlice`.
|
||||
|
||||
self.as_str().encode(stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Eq for SizedStr<N> { }
|
||||
|
||||
impl<const N: usize> FromIterator<char> for SizedStr<N> {
|
||||
#[inline]
|
||||
fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> Self {
|
||||
let mut buf = [0x00; N];
|
||||
let mut len = 0x0;
|
||||
|
||||
for c in iter {
|
||||
let rem = N - len;
|
||||
let req = c.len_utf8();
|
||||
|
||||
if rem < req { break }
|
||||
|
||||
let start = len;
|
||||
let stop = start + req;
|
||||
|
||||
c.encode_utf8(&mut buf[start..stop]);
|
||||
|
||||
len += req;
|
||||
}
|
||||
|
||||
// SAFETY: All octets are initialised and come from
|
||||
// `char::encode_utf8`.
|
||||
unsafe { Self::from_raw_parts(buf, len) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> FromStr for SizedStr<N> {
|
||||
type Err = StringError;
|
||||
|
||||
#[inline]
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if s.len() > N { return Err(StringError::SmallBuffer(SizeError { req: s.len(), len: N })) };
|
||||
if s.len() > N { return Err(StringError::SmallBuffer(SizeError { cap: N, len: s.len() })) };
|
||||
|
||||
let this = unsafe { Self::from_utf8_unchecked(s.as_bytes()) };
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Hash for SizedStr<N> {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.as_str().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: SliceIndex<str>, const N: usize> Index<I> for SizedStr<N> {
|
||||
type Output = I::Output;
|
||||
|
||||
#[inline(always)]
|
||||
fn index(&self, index: I) -> &Self::Output {
|
||||
self.get(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: SliceIndex<str>, const N: usize> IndexMut<I> for SizedStr<N> {
|
||||
#[inline(always)]
|
||||
fn index_mut(&mut self, index: I) -> &mut Self::Output {
|
||||
self.get_mut(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Ord for SizedStr<N> {
|
||||
#[inline(always)]
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.as_str().cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize, const M: usize> PartialEq<SizedStr<M>> for SizedStr<N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &SizedStr<M>) -> bool {
|
||||
self.as_str() == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> PartialEq<&str> for SizedStr<N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &&str) -> bool {
|
||||
self.as_str() == *other
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<const N: usize> PartialEq<String> for SizedStr<N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &String) -> bool {
|
||||
self.as_str() == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize, const M: usize> PartialOrd<SizedStr<M>> for SizedStr<N> {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &SizedStr<M>) -> Option<Ordering> {
|
||||
self.as_str().partial_cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> PartialOrd<&str> for SizedStr<N> {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &&str) -> Option<Ordering> {
|
||||
self.as_str().partial_cmp(*other)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<const N: usize> PartialOrd<String> for SizedStr<N> {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &String) -> Option<Ordering> {
|
||||
self.as_str().partial_cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<const N: usize> SizedEncode for SizedStr<N> {
|
||||
const MAX_ENCODED_SIZE: usize =
|
||||
usize::MAX_ENCODED_SIZE
|
||||
+ SizedSlice::<u8, N>::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl<const N: usize> ToSocketAddrs for SizedStr<N> {
|
204
librum/src/sized_str/mod.rs
Normal file
204
librum/src/sized_str/mod.rs
Normal file
|
@ -0,0 +1,204 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use crate::SizedSlice;
|
||||
use crate::error::{SizeError, StringError};
|
||||
|
||||
use core::fmt::{self, Debug, Display, Formatter};
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::ops::{Index, IndexMut};
|
||||
use core::slice::SliceIndex;
|
||||
use core::str::{Chars, CharIndices};
|
||||
|
||||
// Comparison facilities:
|
||||
mod cmp;
|
||||
|
||||
// Conversion facilities:
|
||||
mod conv;
|
||||
|
||||
// Encode/decode facilities:
|
||||
mod serdes;
|
||||
|
||||
/// Stack-allocated string with maximum length.
|
||||
///
|
||||
/// This is in contrast to [`String`](alloc::string::String) -- which has no size limit in practice -- and [`prim@str`], which is unsized.
|
||||
///
|
||||
/// The string itself is encoded in UTF-8 for interoperability wtih Rust's standard string facilities, and partly due to memory concerns.
|
||||
///
|
||||
/// Keep in mind that the size limit specified by `N` denotes *bytes* (octets) and **not** *characters* -- i.e. a value of `8` may translate to between two and eight characters due to variable-length encoding.
|
||||
///
|
||||
/// See [`SizedSlice`] for an equivalent alternative to [`Vec`](alloc::vec::Vec).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// All instances of this type have the same size if the value of `N` is also the same.
|
||||
/// Therefore, the following four strings have -- despite their different contents -- the same total size.
|
||||
///
|
||||
/// ```
|
||||
/// use librum::SizedStr;
|
||||
/// use std::str::FromStr;
|
||||
///
|
||||
/// let str0 = SizedStr::<0x40>::default(); // Empty string.
|
||||
/// let str1 = SizedStr::<0x40>::from_str("Hello there!").unwrap();
|
||||
/// let str2 = SizedStr::<0x40>::from_str("أنا من أوروپا").unwrap();
|
||||
/// let str3 = SizedStr::<0x40>::from_str("COGITO ERGO SUM").unwrap();
|
||||
///
|
||||
/// assert_eq!(size_of_val(&str0), size_of_val(&str1));
|
||||
/// assert_eq!(size_of_val(&str0), size_of_val(&str2));
|
||||
/// assert_eq!(size_of_val(&str0), size_of_val(&str3));
|
||||
/// assert_eq!(size_of_val(&str1), size_of_val(&str2));
|
||||
/// assert_eq!(size_of_val(&str1), size_of_val(&str3));
|
||||
/// assert_eq!(size_of_val(&str2), size_of_val(&str3));
|
||||
/// ```
|
||||
///
|
||||
/// These three strings can -- by extend in theory -- also interchange their contents between each other.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct SizedStr<const N: usize>(SizedSlice<u8, N>);
|
||||
|
||||
impl<const N: usize> SizedStr<N> {
|
||||
/// Constructs a new, fixed-size string.
|
||||
///
|
||||
/// Note that string is not required to completely fill out its size-constraint.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If the internal buffer cannot contain the entirety of `s`, then an error is returned.
|
||||
#[inline(always)]
|
||||
pub const fn new(s: &str) -> Result<Self, StringError> {
|
||||
if s.len() > N { return Err(StringError::SmallBuffer(SizeError { cap: N, len: s.len() })) };
|
||||
|
||||
let this = unsafe { Self::from_utf8_unchecked(s.as_bytes()) };
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
/// Returns the total capacity of the string.
|
||||
///
|
||||
/// This is defined as being exactly the value of `N`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn capacity(&self) -> usize {
|
||||
self.0.capacity()
|
||||
}
|
||||
|
||||
/// Returns the length of the string.
|
||||
///
|
||||
/// This does not necessarily equate to the value of `N`, as the internal buffer may be used but partially.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
/// Checks if the string is empty, i.e. no characters are contained.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
/// Checks if the string is full, i.e. it cannot hold any more characters.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn is_full(&self) -> bool {
|
||||
self.0.is_full()
|
||||
}
|
||||
|
||||
/// Returns an iterator of the string's characters.
|
||||
#[inline(always)]
|
||||
pub fn chars(&self) -> Chars {
|
||||
self.as_str().chars()
|
||||
}
|
||||
|
||||
/// Returns an iterator of the string's characters along with their positions.
|
||||
#[inline(always)]
|
||||
pub fn char_indices(&self) -> CharIndices {
|
||||
self.as_str().char_indices()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Debug for SizedStr<N> {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
Debug::fmt(self.as_str(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Display for SizedStr<N> {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
Display::fmt(self.as_str(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Eq for SizedStr<N> { }
|
||||
|
||||
impl<const N: usize> FromIterator<char> for SizedStr<N> {
|
||||
#[inline]
|
||||
fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> Self {
|
||||
let mut buf = [0x00; N];
|
||||
let mut len = 0x0;
|
||||
|
||||
for c in iter {
|
||||
let rem = N - len;
|
||||
let req = c.len_utf8();
|
||||
|
||||
if rem < req { break }
|
||||
|
||||
let start = len;
|
||||
let stop = start + req;
|
||||
|
||||
c.encode_utf8(&mut buf[start..stop]);
|
||||
|
||||
len += req;
|
||||
}
|
||||
|
||||
// SAFETY: All octets are initialised and come from
|
||||
// `char::encode_utf8`.
|
||||
unsafe { Self::from_raw_parts(buf, len) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Hash for SizedStr<N> {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.as_str().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: SliceIndex<str>, const N: usize> Index<I> for SizedStr<N> {
|
||||
type Output = I::Output;
|
||||
|
||||
#[inline(always)]
|
||||
fn index(&self, index: I) -> &Self::Output {
|
||||
self.get(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: SliceIndex<str>, const N: usize> IndexMut<I> for SizedStr<N> {
|
||||
#[inline(always)]
|
||||
fn index_mut(&mut self, index: I) -> &mut Self::Output {
|
||||
self.get_mut(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
68
librum/src/sized_str/serdes.rs
Normal file
68
librum/src/sized_str/serdes.rs
Normal file
|
@ -0,0 +1,68 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{
|
||||
Decode,
|
||||
DecodeBorrowed,
|
||||
Encode,
|
||||
IStream,
|
||||
OStream,
|
||||
SizedEncode,
|
||||
SizedStr
|
||||
};
|
||||
use crate::error::{CollectionDecodeError, SizeError, StringError, Utf8Error};
|
||||
|
||||
impl<const N: usize> Decode for SizedStr<N> {
|
||||
type Error = CollectionDecodeError<SizeError, Utf8Error>;
|
||||
|
||||
#[inline]
|
||||
fn decode(stream: &mut IStream) -> Result<Self, Self::Error> {
|
||||
let len = Decode::decode(stream).unwrap();
|
||||
|
||||
let data = stream.read(len);
|
||||
|
||||
Self::from_utf8(data)
|
||||
.map_err(|e| match e {
|
||||
StringError::BadUtf8(e) => CollectionDecodeError::Item(e),
|
||||
|
||||
StringError::SmallBuffer(e) => CollectionDecodeError::Length(e),
|
||||
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> DecodeBorrowed<str> for SizedStr<N> { }
|
||||
|
||||
impl<const N: usize> Encode for SizedStr<N> {
|
||||
type Error = <str as Encode>::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, stream: &mut OStream) -> Result<(), Self::Error> {
|
||||
self.as_str().encode(stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> SizedEncode for SizedStr<N> {
|
||||
const MAX_ENCODED_SIZE: usize =
|
||||
usize::MAX_ENCODED_SIZE
|
||||
+ u8::MAX_ENCODED_SIZE * N;
|
||||
}
|
|
@ -1,27 +1,27 @@
|
|||
// Copyright 2024 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This file is part of bZipper.
|
||||
// This file is part of Librum.
|
||||
//
|
||||
// bZipper is free software: you can redistribute
|
||||
// it and/or modify it under the terms of the GNU
|
||||
// Librum is free software: you can redistribute it
|
||||
// and/or modify it under the terms of the GNU
|
||||
// Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3
|
||||
// of the License, or (at your option) any later
|
||||
// version.
|
||||
//
|
||||
// bZipper is distributed in the hope that it will
|
||||
// Librum is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without
|
||||
// even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Less-
|
||||
// er General Public License along with bZipper. If
|
||||
// er General Public License along with Librum. If
|
||||
// not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use bzipper::SizedStr;
|
||||
use bzipper::error::{StringError, Utf8Error};
|
||||
use core::cmp::Ordering;
|
||||
use librum::SizedStr;
|
||||
use librum::error::{StringError, Utf8Error};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[test]
|
||||
fn test_fixed_str_from_iter() {
|
Loading…
Add table
Add a link
Reference in a new issue