Reimplement 'Decode' for 'alloc::vec::Vec', 'alloc::string::String', 'CString', 'LinkedList', 'HashMap', and 'HashSet'; Reimplement 'DecodeBorrowed' for 'alloc::vec::Vec', 'alloc::string::String', and 'CString'; Update and add tests; Update readme; Clean up code; Implement 'From<Infallible>' for all error types; Update docs; Rework 'EnumEncodeError' and 'EnumDecodeError'; Add 'encode_char' benchmark; Implement 'Encode' and 'Decode' for 'OsStr', 'OsString', 'c_void', and 'BinaryHeap'; Remove 'never-type' feature flag; Rework 'PrimDiscriminant' as 'PrimRepr'; Add 'PrimDiscriminant' enumeration; Implement 'From<T: PrimRepr>' for 'PrimDiscriminant'; Implement 'PrimRepr' for 'u8', 'u16', 'u32', 'u64', 'u128', 'usize', 'i8', 'i16', 'i32', 'i64', 'i128', and 'isize'; Implement 'Debug', 'Display', 'Binary', 'Octal', 'LowerHex', 'UpperHex', 'LowerExp', and 'UpperExp' for 'PrimDiscriminant'; Implement 'Clone', 'Copy', 'Eq', 'PartialEq', and 'Hash' for 'PrimDiscriminant'; Implement 'Eq' and 'PartialEq' for **all** error types; Add 'as_slice' and 'as_ptr' methods to 'Input'; Implement 'AsRef<[u8]>' and 'Borrow<[u8]>' for 'Input'; Implement 'SizedEncode' for 'c_void'; Fix '<LinkedList as Encode>::Error'; Add 'new_unchecked' constructor to 'String' and 'Vec'; Rework 'from_utf8' and 'from_utf8_unchecked' in 'String'; Remove 'StringError'; Rework 'String' to make it trivially-destructable; Actually mark 'String::as_mut_str' with 'const'; Unimplement 'PartialOrd<{&str, alloc::string::String}>' for 'String'; Implement 'PartialEq<str>' for 'String'; Unimplement 'PartialOrd<{[T; M], &[T], alloc::vec::Vec<T>}>' for 'Vec<T, N>'; Remove 'is_full' method from 'String' and 'Vec'; Implement 'Copy' for 'String'; Implement 'PartialEq<{Self, [u8], &[u8]}>', 'Eq', and 'Debug' for 'Input'; Implement 'PartialEq<[U]>' for 'Vec<T, ..>'; Implement 'PartialEq<Vec<U, ..>>' for 'alloc::vec::Vec<T>'; Implement 'PartialEq<String>' for 'alloc::string::String'; Add 'is_char_boundary' and 'as_mut_bytes' methods to 'String'; Add doc aliases; Update lints; Fix atomics being imported from 'std';
This commit is contained in:
parent
d43ed46b08
commit
12975eabf5
71 changed files with 2594 additions and 1822 deletions
43
CHANGELOG.md
43
CHANGELOG.md
|
@ -3,6 +3,49 @@
|
|||
This is the changelog of [Oct](https://crates.io/crates/oct/).
|
||||
See `README.md` for more information.
|
||||
|
||||
## 0.16.0
|
||||
|
||||
* Reimplement `Decode` for `alloc::vec::Vec`, `alloc::string::String`, `CString`, `LinkedList`, `HashMap`, and `HashSet`
|
||||
* Reimplement `DecodeBorrowed` for `alloc::vec::Vec`, `alloc::string::String`, and `CString`
|
||||
* Update and add tests
|
||||
* Update readme
|
||||
* Clean up code
|
||||
* Implement `From<Infallible>` for all error types
|
||||
* Update docs
|
||||
* Rework `EnumEncodeError` and `EnumDecodeError`
|
||||
* Add `encode_char` benchmark
|
||||
* Implement `Encode` and `Decode` for `OsStr`, `OsString`, `c_void`, and `BinaryHeap`
|
||||
* Remove `never-type` feature flag
|
||||
* Rework `PrimDiscriminant` as `PrimRepr`
|
||||
* Add `PrimDiscriminant` enumeration
|
||||
* Implement `From<T: PrimRepr>` for `PrimDiscriminant`
|
||||
* Implement `PrimRepr` for `u8`, `u16`, `u32`, `u64`, `u128`, `usize`, `i8`, `i16`, `i32`, `i64`, `i128`, and `isize`
|
||||
* Implement `Debug`, `Display`, `Binary`, `Octal`, `LowerHex`, `UpperHex`, `LowerExp`, and `UpperExp` for `PrimDiscriminant`
|
||||
* Implement `Clone`, `Copy`, `Eq`, `PartialEq`, and `Hash` for `PrimDiscriminant`
|
||||
* Implement `Eq` and `PartialEq` for **all** error types
|
||||
* Add `as_slice` and `as_ptr` methods to `Input`
|
||||
* Implement `AsRef<[u8]>` and `Borrow<[u8]>` for `Input`
|
||||
* Implement `SizedEncode` for `c_void`
|
||||
* Fix `<LinkedList as Encode>::Error`
|
||||
* Add `new_unchecked` constructor to `String` and `Vec`
|
||||
* Rework `from_utf8` and `from_utf8_unchecked` in `String`
|
||||
* Remove `StringError`
|
||||
* Rework `String` to make it trivially-destructable
|
||||
* Actually mark `String::as_mut_str` with `const`
|
||||
* Unimplement `PartialOrd<{&str, alloc::string::String}>` for `String`
|
||||
* Implement `PartialEq<str>` for `String`
|
||||
* Unimplement `PartialOrd<{[T; M], &[T], alloc::vec::Vec<T>}>` for `Vec<T, N>`
|
||||
* Remove `is_full` method from `String` and `Vec`
|
||||
* Implement `Copy` for `String`
|
||||
* Implement `PartialEq<{Self, [u8], &[u8]}>`, `Eq`, and `Debug` for `Input`
|
||||
* Implement `PartialEq<[U]>` for `Vec<T, ..>`
|
||||
* Implement `PartialEq<Vec<U, ..>>` for `alloc::vec::Vec<T>`
|
||||
* Implement `PartialEq<String>` for `alloc::string::String`
|
||||
* Add `is_char_boundary` and `as_mut_bytes` methods to `String`
|
||||
* Add doc aliases
|
||||
* Update lints
|
||||
* Fix atomics being imported from `std`
|
||||
|
||||
## 0.15.3
|
||||
|
||||
* Update readme
|
||||
|
|
|
@ -131,6 +131,8 @@ same_name_method = "forbid"
|
|||
self_named_module_files = "forbid"
|
||||
separated_literal_suffix = "warn"
|
||||
single_char_pattern = "warn"
|
||||
std_instead_of_alloc = "forbid"
|
||||
std_instead_of_core = "forbid"
|
||||
str_split_at_newline = "warn"
|
||||
string_lit_as_bytes = "forbid"
|
||||
string_lit_chars_any = "warn"
|
||||
|
|
96
README.md
96
README.md
|
@ -20,18 +20,19 @@ According to my runs on an AMD Ryzen 7 3700X with default settings, these benchm
|
|||
|
||||
| Benchmark | [Bincode] | [Borsh] | Oct | [Postcard] |
|
||||
| :--------------------------------- | --------: | ------: | -----: | ---------: |
|
||||
| `encode_u8` | 0.977 | 0.871 | 0.754 | 0.916 |
|
||||
| `encode_u32` | 0.967 | 0.983 | 0.730 | 2.727 |
|
||||
| `encode_u128` | 2.178 | 2.175 | 1.481 | 6.002 |
|
||||
| `encode_u8` | 0.927 | 0.939 | 0.742 | 0.896 |
|
||||
| `encode_u32` | 1.069 | 1.007 | 0.738 | 2.732 |
|
||||
| `encode_u128` | 2.180 | 2.204 | 1.522 | 6.412 |
|
||||
| `encode_char` | 2.474 | 1.261 | 0.817 | 2.480 |
|
||||
| `encode_struct_unit` | 0.000 | 0.000 | 0.000 | 0.000 |
|
||||
| `encode_struct_unnamed` | 1.206 | 1.168 | 0.805 | 2.356 |
|
||||
| `encode_struct_named` | 3.021 | 1.532 | 0.952 | 3.013 |
|
||||
| `encode_enum_unit` | 0.245 | 0.294 | 0.000 | 0.294 |
|
||||
| `decode_u8` | 0.952 | 0.895 | 0.885 | 0.894 |
|
||||
| `decode_non_zero_u8` | 1.215 | 1.250 | 1.229 | 1.232 |
|
||||
| `decode_bool` | 1.204 | 1.224 | 1.126 | 1.176 |
|
||||
| **Total time** → | 11.964 | 1 0.392 | 7.963 | 18.609 |
|
||||
| **Total deviation (p.c.)** → | +50 | +31 | ±0 | +134 |
|
||||
| `encode_struct_unnamed` | 1.245 | 1.146 | 0.834 | 2.378 |
|
||||
| `encode_struct_named` | 3.037 | 1.541 | 0.961 | 3.014 |
|
||||
| `encode_enum_unit` | 0.250 | 0.297 | 0.000 | 0.296 |
|
||||
| `decode_u8` | 0.992 | 0.926 | 0.915 | 0.981 |
|
||||
| `decode_non_zero_u8` | 1.218 | 1.215 | 1.225 | 1.238 |
|
||||
| `decode_bool` | 1.064 | 1.088 | 1.046 | 1.080 |
|
||||
| **Total time** → | 14.456 | 11.624 | 8.800 | 21.509 |
|
||||
| **Total deviation (p.c.)** → | +64 | +32 | ±0 | +144 |
|
||||
|
||||
[Bincode]: https://crates.io/crates/bincode/
|
||||
[Borsh]: https://crates.io/crates/borsh/
|
||||
|
@ -45,11 +46,11 @@ Do feel free to conduct your own tests of Oct.
|
|||
|
||||
## 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).
|
||||
Primitives encode losslessly by default, although [`usize`] and [`isize`] are the exception to this.
|
||||
Due to their machine-dependent representation, these are truncated to the smallest subset of values guaranteed by Rust, with this equating to a cast to [`u16`] or [`i16`], respectively.
|
||||
|
||||
Numerical primitives in general encode as little endian (and **not** ["network order"](https://en.wikipedia.org/wiki/Endianness#Networking)).
|
||||
It is recommended for implementors to follow this convention as well.
|
||||
Numerical types in general (including `char`) are encoded as little endian (and **not** ["network order"](https://en.wikipedia.org/wiki/Endianness#Networking) as is the norm in TCP/UDP/IP).
|
||||
It is recommended for implementors of custom types to adhere to this convention as well.
|
||||
|
||||
See specific types' implementations for notes on their data models.
|
||||
|
||||
|
@ -60,54 +61,10 @@ It may therefore be undesired to store encodings long-term.
|
|||
|
||||
This crate revolves around the `Encode` and `Decode` traits, both of which handle conversions to and from byte streams.
|
||||
|
||||
Many standard types come implemented with Oct, 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.
|
||||
These traits are already implemented by Oct for a large set of the standard types, such as [`Option`] and [`Mutex`](std::sync::Mutex).
|
||||
Some [features](#feature-flags) enable an extended set of implementations that are locked behind unstable feature gates or other crates.
|
||||
|
||||
It is recommended in most cases to simply derive these two traits for user-defined types, although this is only supported for enumerations and structures – not untagged unions.
|
||||
When deriving, each field is *chained* according to declaration order:
|
||||
|
||||
```rust
|
||||
use oct::decode::Decode;
|
||||
use oct::encode::Encode;
|
||||
use oct::slot::Slot;
|
||||
|
||||
#[derive(Debug, Decode, Encode, PartialEq)]
|
||||
struct Ints {
|
||||
value0: u8,
|
||||
value1: u16,
|
||||
value2: u32,
|
||||
value3: u64,
|
||||
value4: u128,
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
let mut buf = Slot::with_capacity(0x100);
|
||||
|
||||
buf.write(VALUE).unwrap();
|
||||
|
||||
assert_eq!(buf.len(), 0x1F);
|
||||
|
||||
assert_eq!(
|
||||
buf,
|
||||
[
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E
|
||||
].as_slice(),
|
||||
);
|
||||
|
||||
assert_eq!(buf.read().unwrap(), VALUE);
|
||||
```
|
||||
|
||||
The following is a more complete example of a UDP server/client for geographic data:
|
||||
The following is an example of a UDP server/client for geographic data:
|
||||
|
||||
```rust
|
||||
use oct::decode::Decode;
|
||||
|
@ -118,8 +75,8 @@ use std::net::{SocketAddr, ToSocketAddrs, UdpSocket};
|
|||
use std::thread::spawn;
|
||||
|
||||
// City, region, etc.:
|
||||
#[derive(Clone, Copy, Debug, Decode, Encode, Eq, PartialEq, SizedEncode)]
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Copy, Debug, Decode, Encode, Eq, PartialEq, SizedEncode)]
|
||||
enum Area {
|
||||
AlQuds,
|
||||
Byzantion,
|
||||
|
@ -129,8 +86,8 @@ enum Area {
|
|||
}
|
||||
|
||||
// Client-to-server message:
|
||||
#[derive(Debug, Decode, Encode, PartialEq, SizedEncode)]
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Decode, Encode, PartialEq, SizedEncode)]
|
||||
enum Request {
|
||||
AtmosphericHumidity { area: Area },
|
||||
AtmosphericPressure { area: Area },
|
||||
|
@ -151,8 +108,8 @@ enum Response {
|
|||
struct Party {
|
||||
pub socket: UdpSocket,
|
||||
|
||||
pub request_buf: Slot::<Request>,
|
||||
pub response_buf: Slot::<Response>,
|
||||
pub request_buf: Slot<Request>,
|
||||
pub response_buf: Slot<Response>,
|
||||
}
|
||||
|
||||
impl Party {
|
||||
|
@ -222,13 +179,18 @@ Oct defines the following, default features:
|
|||
* `proc-macro`: Pulls procedural macros from the [`oct-macros`](https://crates.io/crates/oct-macros/) crate
|
||||
* `std`: Enables implementations for types `std`, e.g. `Mutex` and `RwLock`
|
||||
|
||||
The following features can additionally be enabled for support with nightly-only constructs:
|
||||
|
||||
* `f128`: Enable implementations for the [`f128`] type
|
||||
* `f16`: Enable implementations for the [`f16`] type
|
||||
|
||||
## Documentation
|
||||
|
||||
Oct has its documentation written alongside its source code for use by `rustdoc`.
|
||||
See [Docs.rs](https://docs.rs/oct/latest/oct/) 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.
|
||||
The nightly toolchain is therefore always required when rendering them or or running tests herein.
|
||||
|
||||
## Contribution
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
[package]
|
||||
name = "oct-benchmarks"
|
||||
version = "0.15.3"
|
||||
version = "0.16.0"
|
||||
edition = "2021"
|
||||
description = "Oct benchmarks."
|
||||
license = "MIT"
|
||||
|
@ -32,7 +32,7 @@ readme.workspace = true
|
|||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
oct = { path = "../oct", version = "0.15.0", features = ["proc-macro"]}
|
||||
oct = { path = "../oct", version = "0.16.0", features = ["proc-macro"]}
|
||||
|
||||
bincode = "1.3.0"
|
||||
rand = "0.8.0"
|
||||
|
|
|
@ -20,10 +20,10 @@
|
|||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
use core::array;
|
||||
use core::num::NonZero;
|
||||
use rand::random;
|
||||
use rand::distributions::{Distribution, Standard};
|
||||
use std::array;
|
||||
use std::num::NonZero;
|
||||
use std::time::Instant;
|
||||
use zerocopy::{Immutable, IntoBytes};
|
||||
|
||||
|
@ -43,7 +43,8 @@ macro_rules! benchmark {
|
|||
postcard: $postcard_op:block$(,)?
|
||||
}$(,)?)+
|
||||
} => {{
|
||||
use ::std::{concat, eprint, eprintln, stringify};
|
||||
use ::core::{concat, stringify};
|
||||
use ::std::{eprint, eprintln};
|
||||
|
||||
macro_rules! time {
|
||||
{ $op: block } => {{
|
||||
|
@ -371,6 +372,53 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
encode_char: {
|
||||
bincode: {
|
||||
use bincode::serialize_into;
|
||||
|
||||
const ITEM_SIZE: usize = size_of::<char>();
|
||||
|
||||
let mut buf = vec![0x00; ITEM_SIZE * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
serialize_into(&mut buf, &random::<char>()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
borsh: {
|
||||
const ITEM_SIZE: usize = size_of::<char>();
|
||||
|
||||
let mut buf = vec![0x00; ITEM_SIZE * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
borsh::to_writer::<u32, _>(&mut buf, &random::<char>().into()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
oct: {
|
||||
use oct::encode::{Encode, Output, SizedEncode};
|
||||
|
||||
const ITEM_SIZE: usize = char::MAX_ENCODED_SIZE;
|
||||
|
||||
let mut buf = vec![0x00; ITEM_SIZE * VALUE_COUNT].into_boxed_slice();
|
||||
let mut stream = Output::new(&mut buf);
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
random::<char>().encode(&mut stream).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
postcard: {
|
||||
const ITEM_SIZE: usize = size_of::<char>();
|
||||
|
||||
let mut buf = vec![0x00; ITEM_SIZE * VALUE_COUNT];
|
||||
|
||||
for _ in 0x0..VALUE_COUNT {
|
||||
postcard::to_io(&random::<char>(), &mut buf).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
encode_struct_unit: {
|
||||
bincode: {
|
||||
use bincode::serialize_into;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
[package]
|
||||
name = "oct-macros"
|
||||
version = "0.15.3"
|
||||
version = "0.16.0"
|
||||
edition = "2021"
|
||||
description = "Octonary transcoder. Procedural macros."
|
||||
documentation = "https://docs.rs/oct-macros/"
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use core::borrow::Borrow;
|
||||
use proc_macro2::Span;
|
||||
use syn::{Expr, Lit, LitInt, Variant};
|
||||
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use core::fmt;
|
||||
use core::fmt::{Debug, Formatter};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use std::fmt;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use syn::{
|
||||
GenericParam,
|
||||
Generics,
|
||||
|
|
|
@ -20,11 +20,11 @@ use syn::{
|
|||
};
|
||||
|
||||
pub fn impl_derive_macro<S, E>(
|
||||
input: DeriveInput,
|
||||
trait_path: Path,
|
||||
r#unsafe_token: Option<Token![unsafe]>,
|
||||
struct_body: S,
|
||||
enum_body: E,
|
||||
input: DeriveInput,
|
||||
trait_path: Path,
|
||||
unsafe_token: Option<Token![unsafe]>,
|
||||
struct_body: S,
|
||||
enum_body: E,
|
||||
) -> TokenStream
|
||||
where
|
||||
S: FnOnce(DataStruct) -> TokenStream,
|
||||
|
@ -47,7 +47,7 @@ where
|
|||
enum_body(data, repr)
|
||||
}
|
||||
|
||||
Data::Union(..) => panic!("unions cannot derive `{trait_name:?}`"),
|
||||
Data::Union(..) => panic!("unions cannot derive `{trait_name}`"),
|
||||
};
|
||||
|
||||
let generic_params = &input.generics.params;
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
use crate::{Discriminants, Repr};
|
||||
|
||||
use core::iter;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use std::iter;
|
||||
use syn::{DataEnum, Fields};
|
||||
|
||||
#[must_use]
|
||||
|
@ -49,7 +49,7 @@ pub fn decode_enum(data: DataEnum, repr: Repr) -> TokenStream {
|
|||
});
|
||||
|
||||
quote! {
|
||||
type Error = ::oct::error::EnumDecodeError<#repr, ::oct::error::GenericDecodeError>;
|
||||
type Error = ::oct::error::EnumDecodeError<#repr, <#repr as ::oct::decode::Decode>::Error, ::oct::error::GenericDecodeError>;
|
||||
|
||||
#[inline]
|
||||
fn decode(stream: &mut ::oct::decode::Input) -> ::core::result::Result<Self, Self::Error> {
|
||||
|
@ -62,10 +62,10 @@ pub fn decode_enum(data: DataEnum, repr: Repr) -> TokenStream {
|
|||
let this = match discriminant {
|
||||
#(#discriminants => #values,)*
|
||||
|
||||
value => return Result::Err(::oct::error::EnumDecodeError::UnassignedDiscriminant { value }),
|
||||
value => return ::core::result::Result::Err(::oct::error::EnumDecodeError::UnassignedDiscriminant(value)),
|
||||
};
|
||||
|
||||
Result::Ok(this)
|
||||
::core::result::Result::Ok(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use core::iter;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{DataStruct, Fields};
|
||||
use std::iter;
|
||||
|
||||
#[must_use]
|
||||
pub fn decode_struct(data: DataStruct) -> TokenStream {
|
||||
|
|
|
@ -53,7 +53,7 @@ pub fn encode_enum(data: DataEnum, repr: Repr) -> TokenStream {
|
|||
});
|
||||
|
||||
quote! {
|
||||
type Error = ::oct::error::EnumEncodeError<#repr, ::oct::error::GenericEncodeError>;
|
||||
type Error = ::oct::error::EnumEncodeError<<#repr as ::oct::encode::Encode>::Error, ::oct::error::GenericEncodeError>;
|
||||
|
||||
#[allow(unreachable_patterns)]
|
||||
#[inline]
|
||||
|
|
|
@ -39,7 +39,7 @@ pub fn encode_struct(data: DataStruct) -> TokenStream {
|
|||
|
||||
#[inline]
|
||||
fn encode(&self, stream: &mut ::oct::encode::Output) -> ::core::result::Result<(), Self::Error> {
|
||||
let #pattern = self;
|
||||
let #pattern = *self;
|
||||
|
||||
#(
|
||||
::oct::encode::Encode::encode(#captures, stream)
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
use crate::Repr;
|
||||
|
||||
use core::iter;
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::quote;
|
||||
use std::iter;
|
||||
use syn::DataEnum;
|
||||
|
||||
#[must_use]
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use core::iter;
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::ToTokens;
|
||||
use std::iter;
|
||||
use syn::{
|
||||
Attribute,
|
||||
Ident,
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
[package]
|
||||
name = "oct"
|
||||
version = "0.15.3"
|
||||
version = "0.16.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.83"
|
||||
description = "Octonary transcoder."
|
||||
|
@ -32,12 +32,11 @@ alloc = []
|
|||
proc-macro = ["dep:oct-macros"]
|
||||
std = ["alloc"]
|
||||
|
||||
f128 = []
|
||||
f16 = []
|
||||
never-type = []
|
||||
f128 = []
|
||||
f16 = []
|
||||
|
||||
[dependencies]
|
||||
oct-macros = { path = "../oct-macros", version = "0.15.0", optional = true}
|
||||
oct-macros = { path = "../oct-macros", version = "0.16.0", optional = true}
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod test;
|
||||
|
||||
use crate::decode::{DecodeBorrowed, Input};
|
||||
use crate::error::{
|
||||
|
@ -20,6 +20,7 @@ use crate::error::{
|
|||
|
||||
use core::cell::{Cell, RefCell, UnsafeCell};
|
||||
use core::convert::Infallible;
|
||||
use core::ffi::c_void;
|
||||
use core::marker::{PhantomData, PhantomPinned};
|
||||
use core::mem::MaybeUninit;
|
||||
use core::net::{
|
||||
|
@ -44,14 +45,22 @@ use core::time::Duration;
|
|||
|
||||
#[cfg(feature = "alloc")]
|
||||
use {
|
||||
crate::error::Utf8Error,
|
||||
|
||||
alloc::borrow::{Cow, ToOwned},
|
||||
alloc::boxed::Box,
|
||||
alloc::collections::{BinaryHeap, LinkedList},
|
||||
alloc::ffi::CString,
|
||||
alloc::rc::Rc,
|
||||
alloc::sync::Arc,
|
||||
};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use {
|
||||
core::hash::{BuildHasher, Hash},
|
||||
|
||||
std::collections::{HashMap, HashSet},
|
||||
std::ffi::OsString,
|
||||
std::sync::{Mutex, RwLock},
|
||||
std::time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
@ -59,6 +68,7 @@ use {
|
|||
// Should we require `Encode` for `Decode`?
|
||||
|
||||
/// Denotes a type capable of being decoded.
|
||||
#[doc(alias("Deserialise", "Deserialize"))]
|
||||
pub trait Decode: Sized {
|
||||
/// The type returned in case of error.
|
||||
type Error;
|
||||
|
@ -109,7 +119,7 @@ impl<T: Decode, const N: usize> Decode for [T; N] {
|
|||
// dropped automatically, so we can just forget
|
||||
// about it from this point on. `transmute` cannot
|
||||
// be used here, and `transmute_unchecked` is re-
|
||||
// served for the greedy rustc devs. :(
|
||||
// served for the greedy rustc devs. >:(
|
||||
let this = unsafe { buf.as_ptr().cast::<[T; N]>().read() };
|
||||
Ok(this)
|
||||
}
|
||||
|
@ -129,6 +139,20 @@ impl<T: Decode> Decode for Arc<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: Decode + Ord> Decode for BinaryHeap<T> {
|
||||
type Error = <alloc::vec::Vec<T> as Decode>::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(input: &mut Input) -> Result<Self, Self::Error> {
|
||||
let v = alloc::vec::Vec::decode(input)?;
|
||||
|
||||
let this = v.into();
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for bool {
|
||||
type Error = Infallible;
|
||||
|
||||
|
@ -145,7 +169,7 @@ impl Decode for bool {
|
|||
}
|
||||
|
||||
impl<T: Decode> Decode for Bound<T> {
|
||||
type Error = EnumDecodeError<u8, T::Error>;
|
||||
type Error = EnumDecodeError<u8, <u8 as Decode>::Error, T::Error>;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(input: &mut Input) -> Result<Self, Self::Error> {
|
||||
|
@ -168,7 +192,7 @@ impl<T: Decode> Decode for Bound<T> {
|
|||
|
||||
0x2 => Self::Unbounded,
|
||||
|
||||
value => return Err(EnumDecodeError::UnassignedDiscriminant { value }),
|
||||
value => return Err(EnumDecodeError::UnassignedDiscriminant(value)),
|
||||
};
|
||||
|
||||
Ok(this)
|
||||
|
@ -189,6 +213,30 @@ impl<T: Decode> Decode for Box<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl Decode for CString {
|
||||
type Error = <alloc::vec::Vec<u8> as Decode>::Error;
|
||||
|
||||
/// Decodes a byte slice from the input.
|
||||
///
|
||||
/// This implementation will always allocate one more byte than specified by the slice for the null terminator.
|
||||
/// Note that any null value already in the data will truncate the final string.
|
||||
#[inline(always)]
|
||||
fn decode(input: &mut Input) -> Result<Self, Self::Error> {
|
||||
let Ok(len) = usize::decode(input);
|
||||
|
||||
let mut v = alloc::vec![0x00; len + 0x1];
|
||||
input.read_into(&mut v[..len]).unwrap();
|
||||
|
||||
// SAFETY: We have guaranteed that there is at
|
||||
// least one null value. We also don't care if the
|
||||
// string gets truncated.
|
||||
let this = unsafe { Self::from_vec_with_nul_unchecked(v) };
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decode> Decode for Cell<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
|
@ -201,6 +249,15 @@ impl<T: Decode> Decode for Cell<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Decode for c_void {
|
||||
type Error = Infallible;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(_input: &mut Input) -> Result<Self, Self::Error> {
|
||||
panic!("cannot deserialise `c_void` as it cannot be constructed to begin with")
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for char {
|
||||
type Error = CharDecodeError;
|
||||
|
||||
|
@ -280,28 +337,84 @@ impl Decode for f16 {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl<K, V, S, E> Decode for HashMap<K, V, S>
|
||||
where
|
||||
K: Decode<Error = E> + Eq + Hash,
|
||||
V: Decode<Error = E>,
|
||||
S: BuildHasher + Default,
|
||||
{
|
||||
type Error = CollectionDecodeError<Infallible, ItemDecodeError<usize, E>>;
|
||||
|
||||
#[inline]
|
||||
fn decode(input: &mut Input) -> Result<Self, Self::Error> {
|
||||
let Ok(len) = Decode::decode(input);
|
||||
|
||||
let mut this = Self::with_capacity_and_hasher(len, Default::default());
|
||||
|
||||
for i in 0x0..len {
|
||||
let key= Decode::decode(input)
|
||||
.map_err(|e| CollectionDecodeError::BadItem(ItemDecodeError { index: i, error: e }))?;
|
||||
|
||||
let value = Decode::decode(input)
|
||||
.map_err(|e| CollectionDecodeError::BadItem(ItemDecodeError { index: i, error: e }))?;
|
||||
|
||||
this.insert(key, value);
|
||||
}
|
||||
|
||||
Result::Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl<K, S> Decode for HashSet<K, S>
|
||||
where
|
||||
K: Decode + Eq + Hash,
|
||||
S: BuildHasher + Default,
|
||||
{
|
||||
type Error = CollectionDecodeError<Infallible, ItemDecodeError<usize, K::Error>>;
|
||||
|
||||
#[inline]
|
||||
fn decode(input: &mut Input) -> Result<Self, Self::Error> {
|
||||
let Ok(len) = Decode::decode(input);
|
||||
|
||||
let mut this = Self::with_capacity_and_hasher(len, Default::default());
|
||||
|
||||
for i in 0x0..len {
|
||||
let key = Decode::decode(input)
|
||||
.map_err(|e| CollectionDecodeError::BadItem(ItemDecodeError { index: i, error: e }) )?;
|
||||
|
||||
this.insert(key);
|
||||
}
|
||||
|
||||
Result::Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for Infallible {
|
||||
type Error = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(_input: &mut Input) -> Result<Self, Self::Error> {
|
||||
panic!("cannot deserialise `Infallible` as it cannot be serialised to begin with")
|
||||
panic!("cannot deserialise `Infallible` as it cannot be constructed to begin with")
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for IpAddr {
|
||||
type Error = EnumDecodeError<u8, Infallible>;
|
||||
type Error = EnumDecodeError<u8, <u8 as Decode>::Error, Infallible>;
|
||||
|
||||
#[inline]
|
||||
fn decode(input: &mut Input) -> Result<Self, Self::Error> {
|
||||
let discriminant = u8::decode(input)
|
||||
.map_err(EnumDecodeError::InvalidDiscriminant)?;
|
||||
let Ok(discriminant) = u8::decode(input);
|
||||
|
||||
let this = match discriminant {
|
||||
0x4 => Self::V4(Decode::decode(input).unwrap()),
|
||||
0x6 => Self::V6(Decode::decode(input).unwrap()),
|
||||
|
||||
value => return Err(EnumDecodeError::UnassignedDiscriminant { value })
|
||||
value => return Err(EnumDecodeError::UnassignedDiscriminant(value))
|
||||
};
|
||||
|
||||
Ok(this)
|
||||
|
@ -343,6 +456,28 @@ 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(input: &mut Input) -> Result<Self, Self::Error> {
|
||||
let Ok(len) = usize::decode(input);
|
||||
|
||||
let mut this = Self::new();
|
||||
|
||||
for i in 0x0..len {
|
||||
let value = T::decode(input)
|
||||
.map_err(|e| CollectionDecodeError::BadItem(ItemDecodeError { index: i, error: e }))?;
|
||||
|
||||
this.push_back(value);
|
||||
}
|
||||
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl<T: Decode> Decode for Mutex<T> {
|
||||
|
@ -357,25 +492,13 @@ impl<T: Decode> Decode for Mutex<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "never-type")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "never-type")))]
|
||||
impl Decode for ! {
|
||||
type Error = Infallible;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(_input: &mut Input) -> Result<Self, Self::Error> {
|
||||
panic!("cannot deserialise `!` as it cannot be serialised to begin with")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decode> Decode for Option<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
#[expect(clippy::if_then_some_else_none)] // ???
|
||||
#[inline]
|
||||
fn decode(input: &mut Input) -> Result<Self, Self::Error> {
|
||||
let sign = bool::decode(input)
|
||||
.map_err::<T::Error, _>(|_e| unreachable!())?;
|
||||
let Ok(sign) = bool::decode(input);
|
||||
|
||||
let this = if sign {
|
||||
Some(Decode::decode(input)?)
|
||||
|
@ -387,6 +510,20 @@ impl<T: Decode> Decode for Option<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl Decode for OsString {
|
||||
type Error = <alloc::string::String as Decode>::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn decode(input: &mut Input) -> Result<Self, Self::Error> {
|
||||
let s: alloc::string::String = Decode::decode(input)?;
|
||||
|
||||
let this = s.into();
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Decode for PhantomData<T> {
|
||||
type Error = Infallible;
|
||||
|
||||
|
@ -499,7 +636,7 @@ where
|
|||
T: Decode<Error = Err>,
|
||||
E: Decode<Error: Into<Err>>,
|
||||
{
|
||||
type Error = EnumDecodeError<bool, Err>;
|
||||
type Error = EnumDecodeError<bool, <bool as Decode>::Error, Err>;
|
||||
|
||||
#[inline]
|
||||
fn decode(input: &mut Input) -> Result<Self, Self::Error> {
|
||||
|
@ -550,7 +687,7 @@ impl<T: Decode> Decode for Saturating<T> {
|
|||
}
|
||||
|
||||
impl Decode for SocketAddr {
|
||||
type Error = EnumDecodeError<u8, Infallible>;
|
||||
type Error = EnumDecodeError<u8, <u8 as Decode>::Error, Infallible>;
|
||||
|
||||
#[inline]
|
||||
fn decode(input: &mut Input) -> Result<Self, Self::Error> {
|
||||
|
@ -560,7 +697,7 @@ impl Decode for SocketAddr {
|
|||
0x4 => Ok(Self::V4(Decode::decode(input).unwrap())),
|
||||
0x6 => Ok(Self::V6(Decode::decode(input).unwrap())),
|
||||
|
||||
value => Err(EnumDecodeError::UnassignedDiscriminant { value }),
|
||||
value => Err(EnumDecodeError::UnassignedDiscriminant(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -593,6 +730,33 @@ impl Decode for SocketAddrV6 {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl Decode for alloc::string::String {
|
||||
type Error = CollectionDecodeError<Infallible, Utf8Error>;
|
||||
|
||||
#[inline]
|
||||
fn decode(input: &mut Input) -> Result<Self, Self::Error> {
|
||||
let Ok(len) = Decode::decode(input);
|
||||
|
||||
let mut v = alloc::vec![0x00; len];
|
||||
input.read_into(&mut v).unwrap();
|
||||
|
||||
match Self::from_utf8(v) {
|
||||
Ok(s) => Ok(s),
|
||||
|
||||
Err(e) => {
|
||||
let i = e.utf8_error().valid_up_to();
|
||||
let c = e.as_bytes()[i];
|
||||
|
||||
Err(CollectionDecodeError::BadItem(
|
||||
Utf8Error { value: c, index: i },
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl Decode for SystemTime {
|
||||
|
@ -647,6 +811,34 @@ impl Decode for usize {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: Decode> Decode for alloc::vec::Vec<T> {
|
||||
type Error = CollectionDecodeError<Infallible, ItemDecodeError<usize, T::Error>>;
|
||||
|
||||
#[inline]
|
||||
fn decode(input: &mut Input) -> Result<Self, Self::Error> {
|
||||
let Ok(len) = Decode::decode(input);
|
||||
|
||||
let mut this = Self::with_capacity(len);
|
||||
|
||||
let buf = this.as_mut_ptr();
|
||||
for i in 0x0..len {
|
||||
let value = Decode::decode(input)
|
||||
.map_err(|e| CollectionDecodeError::BadItem(ItemDecodeError { index: i, error: e }))?;
|
||||
|
||||
// SAFETY: Each index is within bounds (i.e. capac-
|
||||
// ity).
|
||||
unsafe { buf.add(i).write(value) };
|
||||
}
|
||||
|
||||
// SAFETY: We have initialised the buffer.
|
||||
unsafe { this.set_len(len); }
|
||||
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decode> Decode for Wrapping<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
|
@ -892,65 +1084,65 @@ impl_non_zero!(usize);
|
|||
impl_atomic! {
|
||||
width: "8",
|
||||
ty: bool,
|
||||
atomic_ty: std::sync::atomic::AtomicBool,
|
||||
atomic_ty: core::sync::atomic::AtomicBool,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "16",
|
||||
ty: i16,
|
||||
atomic_ty: std::sync::atomic::AtomicI16,
|
||||
atomic_ty: core::sync::atomic::AtomicI16,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "32",
|
||||
ty: i32,
|
||||
atomic_ty: std::sync::atomic::AtomicI32,
|
||||
atomic_ty: core::sync::atomic::AtomicI32,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "64",
|
||||
ty: i64,
|
||||
atomic_ty: std::sync::atomic::AtomicI64,
|
||||
atomic_ty: core::sync::atomic::AtomicI64,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "8",
|
||||
ty: i8,
|
||||
atomic_ty: std::sync::atomic::AtomicI8,
|
||||
atomic_ty: core::sync::atomic::AtomicI8,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "ptr",
|
||||
ty: isize,
|
||||
atomic_ty: std::sync::atomic::AtomicIsize,
|
||||
atomic_ty: core::sync::atomic::AtomicIsize,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "16",
|
||||
ty: u16,
|
||||
atomic_ty: std::sync::atomic::AtomicU16,
|
||||
atomic_ty: core::sync::atomic::AtomicU16,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "32",
|
||||
ty: u32,
|
||||
atomic_ty: std::sync::atomic::AtomicU32,
|
||||
atomic_ty: core::sync::atomic::AtomicU32,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "64",
|
||||
ty: u64,
|
||||
atomic_ty: std::sync::atomic::AtomicU64,
|
||||
atomic_ty: core::sync::atomic::AtomicU64,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "8",
|
||||
ty: u8,
|
||||
atomic_ty: std::sync::atomic::AtomicU8,
|
||||
atomic_ty: core::sync::atomic::AtomicU8,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "ptr",
|
||||
ty: usize,
|
||||
atomic_ty: std::sync::atomic::AtomicUsize,
|
||||
atomic_ty: core::sync::atomic::AtomicUsize,
|
||||
}
|
||||
|
|
186
oct/src/decode/decode/test.rs
Normal file
186
oct/src/decode/decode/test.rs
Normal file
|
@ -0,0 +1,186 @@
|
|||
// Copyright 2024-2025 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of
|
||||
// the Mozilla Public License, v. 2.0. If a copy of
|
||||
// the MPL was not distributed with this file, you
|
||||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use core::char;
|
||||
use oct::decode::{Decode, Input};
|
||||
use oct::encode::{Encode, SizedEncode};
|
||||
use oct::error::EnumDecodeError;
|
||||
use oct::string::String;
|
||||
use oct::vec::Vec;
|
||||
|
||||
macro_rules! test {
|
||||
{
|
||||
$(
|
||||
$ty:ty {
|
||||
$($data:expr => $value:expr),+$(,)?
|
||||
}$(,)?
|
||||
)*
|
||||
} => {{
|
||||
$($({
|
||||
let data: &[u8] = &$data;
|
||||
|
||||
let mut input = ::oct::decode::Input::new(data);
|
||||
|
||||
let left: ::core::result::Result<$ty, <$ty as ::oct::decode::Decode>::Error> = ::oct::decode::Decode::decode(&mut input);
|
||||
let right: ::core::result::Result<$ty, <$ty as ::oct::decode::Decode>::Error> = $value;
|
||||
|
||||
::std::assert_eq!(left, right);
|
||||
})*)*
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode() {
|
||||
test! {
|
||||
i8 {
|
||||
[0x00] => Ok( 0x00),
|
||||
[0x7F] => Ok( 0x7F),
|
||||
[0x80] => Ok(-0x80),
|
||||
[0xFF] => Ok(-0x01),
|
||||
}
|
||||
|
||||
i16 {
|
||||
[0x00, 0x00] => Ok( 0x0000),
|
||||
[0xFF, 0x7F] => Ok( 0x7FFF),
|
||||
[0x00, 0x80] => Ok(-0x8000),
|
||||
[0xFF, 0xFF] => Ok(-0x0001),
|
||||
}
|
||||
|
||||
i32 {
|
||||
[0x00, 0x00, 0x00, 0x00] => Ok( 0x00000000),
|
||||
[0xFF, 0xFF, 0xFF, 0x7F] => Ok( 0x7FFFFFFF),
|
||||
[0x00, 0x00, 0x00, 0x80] => Ok(-0x80000000),
|
||||
[0xFF, 0xFF, 0xFF, 0xFF] => Ok(-0x00000001),
|
||||
}
|
||||
|
||||
i64 {
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] => Ok( 0x0000000000000000),
|
||||
[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F] => Ok( 0x7FFFFFFFFFFFFFFF),
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80] => Ok(-0x8000000000000000),
|
||||
[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] => Ok(-0x0000000000000001),
|
||||
}
|
||||
|
||||
u128 {
|
||||
[
|
||||
0x7F, 0x8F, 0x6F, 0x9F, 0x5F, 0xAF, 0x4F, 0xBF,
|
||||
0x3F, 0xCF, 0x2F, 0xDF, 0x1F, 0xEF, 0x0F, 0xFF,
|
||||
] => Ok(0xFF_0F_EF_1F_DF_2F_CF_3F_BF_4F_AF_5F_9F_6F_8F_7F),
|
||||
}
|
||||
|
||||
char {
|
||||
[0xFD, 0xFF, 0x00, 0x00] => Ok(char::REPLACEMENT_CHARACTER),
|
||||
}
|
||||
|
||||
[char; 0x5] {
|
||||
[
|
||||
0xBB, 0x03, 0x00, 0x00, 0x91, 0x03, 0x00, 0x00,
|
||||
0xBC, 0x03, 0x00, 0x00, 0x94, 0x03, 0x00, 0x00,
|
||||
0xB1, 0x03, 0x00, 0x00,
|
||||
] => Ok(['\u{03BB}', '\u{0391}', '\u{03BC}', '\u{0394}', '\u{03B1}']),
|
||||
}
|
||||
|
||||
Option<()> {
|
||||
[0x00] => Ok(None),
|
||||
[0x01] => Ok(Some(())),
|
||||
}
|
||||
|
||||
Result<(), i8> {
|
||||
[0x00, 0x00] => Ok(Ok(())),
|
||||
[0x01, 0x7F] => Ok(Err(i8::MAX)),
|
||||
}
|
||||
|
||||
Vec<u16, 0x6> {
|
||||
[0x02, 0x00, 0xBB, 0xAA, 0xDD, 0xCC] => Ok(Vec::new(&[0xAA_BB, 0xCC_DD]).unwrap()),
|
||||
}
|
||||
|
||||
String<0x6> {
|
||||
[0x06, 0x00, 0xE6, 0x97, 0xA5, 0xE6, 0x9C, 0xAC] => Ok(String::new("\u{65E5}\u{672C}").unwrap()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_decode_alloc_vec_long_len() {
|
||||
let data = [
|
||||
0xFF, 0xFF,
|
||||
];
|
||||
|
||||
let mut stream = Input::new(&data);
|
||||
|
||||
let _ = <alloc::vec::Vec<u32> as Decode>::decode(&mut stream).unwrap();
|
||||
}
|
||||
|
||||
#[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 {
|
||||
[
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0xE1, 0x0B, 0x5E,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
] => Ok(ProcExit { exit_code: 0x1, timestmap: 1577836800 }),
|
||||
}
|
||||
|
||||
NewByte {
|
||||
[0x80] => Ok(NewByte(0x80)),
|
||||
}
|
||||
|
||||
Unit {
|
||||
[] => Ok(Unit),
|
||||
}
|
||||
|
||||
UnitOrFields {
|
||||
[
|
||||
0x00, 0x00,
|
||||
] => Ok(UnitOrFields::Unit),
|
||||
|
||||
[
|
||||
0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
] => Ok(UnitOrFields::Unnamed(-0x1)),
|
||||
|
||||
[
|
||||
0x02, 0x00, 0x4C, 0xC8, 0xC5, 0x66, 0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
] => Ok(UnitOrFields::Named { timestamp: 1724237900 }),
|
||||
|
||||
[
|
||||
0x03, 0x00,
|
||||
] => Err(EnumDecodeError::UnassignedDiscriminant(0x3)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_oct_vec_long_len() {
|
||||
let data = [
|
||||
0xFF, 0xFF,
|
||||
];
|
||||
|
||||
let mut stream = Input::new(&data);
|
||||
|
||||
let _ = <oct::vec::Vec<u32, 0x0> as Decode>::decode(&mut stream).unwrap_err();
|
||||
}
|
|
@ -1,158 +0,0 @@
|
|||
// Copyright 2024-2025 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of
|
||||
// the Mozilla Public License, v. 2.0. If a copy of
|
||||
// the MPL was not distributed with this file, you
|
||||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use oct::decode::Decode;
|
||||
use oct::encode::{Encode, SizedEncode};
|
||||
use std::char;
|
||||
|
||||
macro_rules! test {
|
||||
{
|
||||
$(
|
||||
$ty:ty {
|
||||
$($data:expr => $value:expr),+$(,)?
|
||||
}$(,)?
|
||||
)*
|
||||
} => {{
|
||||
$($({
|
||||
let data: &[u8] = &$data;
|
||||
|
||||
let mut input = ::oct::decode::Input::new(data);
|
||||
|
||||
let left: $ty = ::oct::decode::Decode::decode(&mut input).unwrap();
|
||||
let right: $ty = $value;
|
||||
|
||||
::std::assert_eq!(left, right);
|
||||
})*)*
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode() {
|
||||
test! {
|
||||
i8 {
|
||||
[0x00] => 0x00,
|
||||
[0x7F] => 0x7F,
|
||||
[0x80] => -0x80,
|
||||
[0xFF] => -0x01,
|
||||
}
|
||||
|
||||
i16 {
|
||||
[0x00, 0x00] => 0x0000,
|
||||
[0xFF, 0x7F] => 0x7FFF,
|
||||
[0x00, 0x80] => -0x8000,
|
||||
[0xFF, 0xFF] => -0x0001,
|
||||
}
|
||||
|
||||
i32 {
|
||||
[0x00, 0x00, 0x00, 0x00] => 0x00000000,
|
||||
[0xFF, 0xFF, 0xFF, 0x7F] => 0x7FFFFFFF,
|
||||
[0x00, 0x00, 0x00, 0x80] => -0x80000000,
|
||||
[0xFF, 0xFF, 0xFF, 0xFF] => -0x00000001,
|
||||
}
|
||||
|
||||
i64 {
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] => 0x0000000000000000,
|
||||
[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F] => 0x7FFFFFFFFFFFFFFF,
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80] => -0x8000000000000000,
|
||||
[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] => -0x0000000000000001,
|
||||
}
|
||||
|
||||
u128 {
|
||||
[
|
||||
0x7F, 0x8F, 0x6F, 0x9F, 0x5F, 0xAF, 0x4F, 0xBF,
|
||||
0x3F, 0xCF, 0x2F, 0xDF, 0x1F, 0xEF, 0x0F, 0xFF,
|
||||
] => 0xFF_0F_EF_1F_DF_2F_CF_3F_BF_4F_AF_5F_9F_6F_8F_7F,
|
||||
}
|
||||
|
||||
char {
|
||||
[0xFD, 0xFF, 0x00, 0x00] => char::REPLACEMENT_CHARACTER,
|
||||
}
|
||||
|
||||
[char; 0x5] {
|
||||
[
|
||||
0xBB, 0x03, 0x00, 0x00, 0x91, 0x03, 0x00, 0x00,
|
||||
0xBC, 0x03, 0x00, 0x00, 0x94, 0x03, 0x00, 0x00,
|
||||
0xB1, 0x03, 0x00, 0x00,
|
||||
] => ['\u{03BB}', '\u{0391}', '\u{03BC}', '\u{0394}', '\u{03B1}'],
|
||||
}
|
||||
|
||||
Option<()> {
|
||||
[0x00] => None,
|
||||
[0x01] => Some(()),
|
||||
}
|
||||
|
||||
Result<(), i8> {
|
||||
[0x00, 0x00] => Ok(()),
|
||||
[0x01, 0x7F] => Err(i8::MAX),
|
||||
}
|
||||
|
||||
// Lid<Vec<u16>, 0x6> {
|
||||
// [0x02, 0x00, 0xBB, 0xAA, 0xDD, 0xCC] => Lid([0xAA_BB, 0xCC_DD].into()),
|
||||
// }
|
||||
|
||||
// Lid<String, 0x6> {
|
||||
// [0x06, 0x00, 0xE6, 0x97, 0xA5, 0xE6, 0x9C, 0xAC] => Lid("\u{65E5}\u{672C}".into()),
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
#[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 {
|
||||
[
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0xE1, 0x0B, 0x5E,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
] => ProcExit { exit_code: 0x1, timestmap: 1577836800 },
|
||||
}
|
||||
|
||||
NewByte {
|
||||
[0x80] => NewByte(0x80),
|
||||
}
|
||||
|
||||
Unit {
|
||||
[] => Unit,
|
||||
}
|
||||
|
||||
UnitOrFields {
|
||||
[
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
] => UnitOrFields::Unit,
|
||||
|
||||
[
|
||||
0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
] => UnitOrFields::Unnamed(-0x1),
|
||||
|
||||
[
|
||||
0x02, 0x00, 0x4C, 0xC8, 0xC5, 0x66, 0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
] => UnitOrFields::Named { timestamp: 1724237900 },
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,10 +13,15 @@ use core::borrow::Borrow;
|
|||
#[cfg(feature = "alloc")]
|
||||
use {
|
||||
alloc::boxed::Box,
|
||||
alloc::ffi::CString,
|
||||
alloc::rc::Rc,
|
||||
alloc::sync::Arc,
|
||||
core::ffi::CStr,
|
||||
};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::ffi::{OsStr, OsString};
|
||||
|
||||
/// Indicates a scheme relationship between borrowed and owned types.
|
||||
///
|
||||
/// Implementing this trait is specifically a promise that <code><Self as [Decode]>::[decode](Decode::decode)</code> can handle any encoding of `B`.
|
||||
|
@ -31,6 +36,7 @@ use {
|
|||
/// [\[T\]]: slice
|
||||
///
|
||||
/// An alternative to using arrays would be to use the [`Vec`](crate::vec::Vec) type, which *does* use the same scheme.
|
||||
#[doc(alias("DeserialiseBorrowed", "DeserializeBorrowed"))]
|
||||
pub trait DecodeBorrowed<B: ?Sized>: Borrow<B> + Decode { }
|
||||
|
||||
impl<T: Decode> DecodeBorrowed<T> for T { }
|
||||
|
@ -43,6 +49,22 @@ impl<T: Decode> DecodeBorrowed<T> for Arc<T> { }
|
|||
#[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<CStr> for CString { }
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl DecodeBorrowed<OsStr> for OsString { }
|
||||
|
||||
#[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 alloc::string::String { }
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: Decode> DecodeBorrowed<[T]> for alloc::vec::Vec<T> { }
|
||||
|
|
|
@ -6,8 +6,13 @@
|
|||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
use crate::error::InputError;
|
||||
|
||||
use core::borrow::Borrow;
|
||||
use core::fmt::{self, Debug, Formatter};
|
||||
use core::ptr::copy_nonoverlapping;
|
||||
use core::slice;
|
||||
|
||||
|
@ -109,4 +114,67 @@ impl<'a> Input<'a> {
|
|||
pub const fn position(&self) -> usize {
|
||||
self.pos
|
||||
}
|
||||
|
||||
/// Gets a pointer to the next byte of the input stream.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn as_ptr(&self) -> *const u8 {
|
||||
unsafe { self.buf.as_ptr().add(self.position()) }
|
||||
}
|
||||
|
||||
/// Gets a slice of the remaining bytes.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn as_slice(&self) -> &[u8] {
|
||||
unsafe {
|
||||
let ptr = self.as_ptr();
|
||||
let len = self.remaining();
|
||||
|
||||
slice::from_raw_parts(ptr, len)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for Input<'_> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<[u8]> for Input<'_> {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &[u8] {
|
||||
self.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Input<'_> {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
Debug::fmt(self.as_slice(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Input<'_> { }
|
||||
|
||||
impl PartialEq for Input<'_> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.as_slice() == other.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<[u8]> for Input<'_> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &[u8]) -> bool {
|
||||
self.as_slice() == other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<&[u8]> for Input<'_> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &&[u8]) -> bool {
|
||||
self.as_slice() == *other
|
||||
}
|
||||
}
|
||||
|
|
54
oct/src/decode/input/test.rs
Normal file
54
oct/src/decode/input/test.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2024-2025 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of
|
||||
// the Mozilla Public License, v. 2.0. If a copy of
|
||||
// the MPL was not distributed with this file, you
|
||||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use oct::decode::Input;
|
||||
use oct::error::InputError;
|
||||
|
||||
#[test]
|
||||
fn test_decode_input() {
|
||||
let buf = [0xFF, 0xFE, 0xFD, 0xFC];
|
||||
|
||||
let mut input = Input::new(&buf);
|
||||
|
||||
assert_eq!(input.capacity(), 0x4);
|
||||
assert_eq!(input.position(), 0x0);
|
||||
assert_eq!(input.remaining(), 0x4);
|
||||
|
||||
assert_eq!(input, [0xFF, 0xFE, 0xFD, 0xFC].as_slice());
|
||||
assert_eq!(input.read(0x1), Ok([0xFF].as_slice()));
|
||||
|
||||
assert_eq!(input.capacity(), 0x4);
|
||||
assert_eq!(input.position(), 0x1);
|
||||
assert_eq!(input.remaining(), 0x3);
|
||||
|
||||
assert_eq!(input, [0xFE, 0xFD, 0xFC].as_slice());
|
||||
assert_eq!(input.read(0x1), Ok([0xFE].as_slice()));
|
||||
|
||||
assert_eq!(input.capacity(), 0x4);
|
||||
assert_eq!(input.position(), 0x2);
|
||||
assert_eq!(input.remaining(), 0x2);
|
||||
|
||||
assert_eq!(input, [0xFD, 0xFC].as_slice());
|
||||
assert_eq!(input.read(0x1), Ok([0xFD].as_slice()));
|
||||
|
||||
assert_eq!(input.capacity(), 0x4);
|
||||
assert_eq!(input.position(), 0x3);
|
||||
assert_eq!(input.remaining(), 0x1);
|
||||
|
||||
assert_eq!(input, [0xFC].as_slice());
|
||||
assert_eq!(input.read(0x1), Ok([0xFC].as_slice()));
|
||||
|
||||
assert_eq!(input.capacity(), 0x4);
|
||||
assert_eq!(input.position(), 0x4);
|
||||
assert_eq!(input.remaining(), 0x0);
|
||||
|
||||
assert_eq!(input, [].as_slice());
|
||||
assert_eq!(input.read(0x1), Err(InputError { capacity: 0x4, position: 0x4, count: 0x1 }));
|
||||
|
||||
assert_eq!(input.read(0x0), Ok([].as_slice()));
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod test;
|
||||
|
||||
use crate::encode::Output;
|
||||
use crate::error::{
|
||||
|
@ -17,11 +17,12 @@ use crate::error::{
|
|||
ItemEncodeError,
|
||||
RefCellEncodeError,
|
||||
UsizeEncodeError,
|
||||
Utf8Error,
|
||||
};
|
||||
|
||||
use core::cell::{Cell, LazyCell, RefCell, UnsafeCell};
|
||||
use core::convert::Infallible;
|
||||
use core::ffi::CStr;
|
||||
use core::ffi::{CStr, c_void};
|
||||
use core::hash::BuildHasher;
|
||||
use core::hint::unreachable_unchecked;
|
||||
use core::marker::{PhantomData, PhantomPinned};
|
||||
|
@ -49,7 +50,7 @@ use core::time::Duration;
|
|||
use {
|
||||
alloc::borrow::{Cow, ToOwned},
|
||||
alloc::boxed::Box,
|
||||
alloc::collections::LinkedList,
|
||||
alloc::collections::{BinaryHeap, LinkedList},
|
||||
alloc::ffi::CString,
|
||||
alloc::string::String,
|
||||
alloc::vec::Vec,
|
||||
|
@ -61,15 +62,18 @@ use alloc::sync::Arc;
|
|||
|
||||
#[cfg(feature = "std")]
|
||||
use {
|
||||
core::str,
|
||||
|
||||
std::collections::{HashMap, HashSet},
|
||||
std::ffi::{OsStr, OsString},
|
||||
std::sync::{LazyLock, Mutex, RwLock},
|
||||
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, of course, also just be manually implemented.
|
||||
/// It is recommended to simply derive this trait for custom types (see the [`Encode`](derive@crate::encode::Encode) macro).
|
||||
/// The trait can, of course, also just be manually implemented.
|
||||
///
|
||||
/// If all possible encodings have a known, maximum size, then the [`SizedEncode`](crate::encode::SizedEncode) trait should be considered as well.
|
||||
///
|
||||
|
@ -105,6 +109,7 @@ use {
|
|||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[doc(alias("Serialise", "Serialize"))]
|
||||
pub trait Encode {
|
||||
/// The type returned in case of error.
|
||||
type Error;
|
||||
|
@ -202,6 +207,17 @@ impl<T: Encode + ?Sized> Encode for Arc<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: Encode> Encode for BinaryHeap<T> {
|
||||
type Error = <[T] as Encode>::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, output: &mut Output) -> Result<(), Self::Error> {
|
||||
self.as_slice().encode(output)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for bool {
|
||||
type Error = <u8 as Encode>::Error;
|
||||
|
||||
|
@ -248,6 +264,40 @@ impl<T: Encode + ?Sized> Encode for Box<T> {
|
|||
}
|
||||
}
|
||||
|
||||
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, output: &mut Output) -> Result<(), Self::Error> {
|
||||
self.to_bytes().encode(output)
|
||||
}
|
||||
}
|
||||
|
||||
#[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, output: &mut Output) -> Result<(), Self::Error> {
|
||||
self.as_c_str().encode(output)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for c_void {
|
||||
type Error = Infallible;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, _output: &mut Output) -> Result<(), Self::Error> {
|
||||
// NOTE: Contrary to `Infallible` and/or `!`,
|
||||
// `c_void` *can* actually be constructed (although
|
||||
// only by the the standard library).
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Encode> Encode for Cell<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
|
@ -277,28 +327,6 @@ impl<T: Encode + ToOwned + ?Sized> Encode for Cow<'_, T> {
|
|||
}
|
||||
}
|
||||
|
||||
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, output: &mut Output) -> Result<(), Self::Error> {
|
||||
self.to_bytes().encode(output)
|
||||
}
|
||||
}
|
||||
|
||||
#[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, output: &mut Output) -> Result<(), Self::Error> {
|
||||
self.as_c_str().encode(output)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for Duration {
|
||||
type Error = Infallible;
|
||||
|
||||
|
@ -395,7 +423,7 @@ impl Encode for Infallible {
|
|||
}
|
||||
|
||||
impl Encode for IpAddr {
|
||||
type Error = EnumEncodeError<u8, Infallible>;
|
||||
type Error = EnumEncodeError<<u8 as Encode>::Error, 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).
|
||||
///
|
||||
|
@ -479,7 +507,7 @@ impl<T: Encode> Encode for LazyLock<T> {
|
|||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: Encode> Encode for LinkedList<T> {
|
||||
type Error = CollectionEncodeError<UsizeEncodeError, (usize, T::Error)>;
|
||||
type Error = CollectionEncodeError<UsizeEncodeError, ItemEncodeError<usize, T::Error>>;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, output: &mut Output) -> Result<(), Self::Error> {
|
||||
|
@ -491,7 +519,7 @@ impl<T: Encode> Encode for LinkedList<T> {
|
|||
for (i, v) in self.iter().enumerate() {
|
||||
v
|
||||
.encode(output)
|
||||
.map_err(|e| CollectionEncodeError::BadItem((i, e)))?;
|
||||
.map_err(|e| CollectionEncodeError::BadItem(ItemEncodeError { index: i, error: e }))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -512,20 +540,6 @@ impl<T: Encode + ?Sized> Encode for Mutex<T> {
|
|||
}
|
||||
}
|
||||
|
||||
// Especially useful for `Result<T, !>`.
|
||||
// **If** that is even needed, of course.
|
||||
#[cfg(feature = "never-type")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "never-type")))]
|
||||
impl Encode for ! {
|
||||
type Error = Infallible;
|
||||
|
||||
#[inline]
|
||||
fn encode(&self, _output: &mut Output) -> Result<(), Self::Error> {
|
||||
// SAFETY: `!` objects can never be constructed.
|
||||
unsafe { unreachable_unchecked() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Encode> Encode for Option<T> {
|
||||
type Error = T::Error;
|
||||
|
||||
|
@ -554,6 +568,59 @@ impl<T: Encode> Encode for Option<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl Encode for OsStr {
|
||||
type Error = CollectionEncodeError<UsizeEncodeError, Utf8Error>;
|
||||
|
||||
/// Encodes the OS-specific string as a normal string.
|
||||
///
|
||||
/// `OsStr` is losely defined by Rust as being superset of the standard, UTF-8 `str`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This implementation will yield an error if the string `self` contains any non-UTF-8 octets.
|
||||
#[inline(always)]
|
||||
fn encode(&self, output: &mut Output) -> Result<(), Self::Error> {
|
||||
let data = self.as_encoded_bytes();
|
||||
|
||||
let s = match str::from_utf8(data) {
|
||||
Ok(s) => s,
|
||||
|
||||
Err(e) => {
|
||||
let i = e.valid_up_to();
|
||||
let c = data[i];
|
||||
|
||||
return Err(
|
||||
CollectionEncodeError::BadItem(
|
||||
Utf8Error { value: c, index: i },
|
||||
),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(CollectionEncodeError::BadLength(e)) = s.encode(output) {
|
||||
Err(CollectionEncodeError::BadLength(e))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl Encode for OsString {
|
||||
type Error = <OsStr as Encode>::Error;
|
||||
|
||||
/// Encodes the OS-specific string as a normal string.
|
||||
///
|
||||
/// See [`OsStr`]'s implementation of `Encode` for more information.
|
||||
#[inline(always)]
|
||||
fn encode(&self, output: &mut Output) -> Result<(), Self::Error> {
|
||||
self.as_os_str().encode(output)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Encode for PhantomData<T> {
|
||||
type Error = Infallible;
|
||||
|
||||
|
@ -784,7 +851,7 @@ impl Encode for str {
|
|||
impl Encode for String {
|
||||
type Error = <str as Encode>::Error;
|
||||
|
||||
/// See [`str`].
|
||||
/// See [`prim@str`].
|
||||
#[inline(always)]
|
||||
fn encode(&self, output: &mut Output) -> Result<(), Self::Error> {
|
||||
self.as_str().encode(output)
|
||||
|
@ -857,7 +924,7 @@ impl Encode for usize {
|
|||
#[inline]
|
||||
fn encode(&self, output: &mut Output) -> Result<(), Self::Error> {
|
||||
let value = u16::try_from(*self)
|
||||
.map_err(|_e| UsizeEncodeError(*self))?;
|
||||
.map_err(|_| UsizeEncodeError(*self))?;
|
||||
|
||||
let Ok(_) = value.encode(output);
|
||||
Ok(())
|
||||
|
@ -952,7 +1019,7 @@ macro_rules! impl_atomic {
|
|||
/// The atomic object itself is read with the [`Relaxed`](core::sync::atomic::Ordering) ordering scheme.
|
||||
#[inline(always)]
|
||||
fn encode(&self, output: &mut ::oct::encode::Output) -> ::core::result::Result<(), Self::Error> {
|
||||
use ::std::sync::atomic::Ordering;
|
||||
use ::core::sync::atomic::Ordering;
|
||||
|
||||
self.load(Ordering::Relaxed).encode(output)
|
||||
}
|
||||
|
@ -1101,65 +1168,65 @@ impl_non_zero!(usize);
|
|||
impl_atomic! {
|
||||
width: "8",
|
||||
ty: bool,
|
||||
atomic_ty: std::sync::atomic::AtomicBool,
|
||||
atomic_ty: core::sync::atomic::AtomicBool,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "16",
|
||||
ty: i16,
|
||||
atomic_ty: std::sync::atomic::AtomicI16,
|
||||
atomic_ty: core::sync::atomic::AtomicI16,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "32",
|
||||
ty: i32,
|
||||
atomic_ty: std::sync::atomic::AtomicI32,
|
||||
atomic_ty: core::sync::atomic::AtomicI32,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "64",
|
||||
ty: i64,
|
||||
atomic_ty: std::sync::atomic::AtomicI64,
|
||||
atomic_ty: core::sync::atomic::AtomicI64,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "8",
|
||||
ty: i8,
|
||||
atomic_ty: std::sync::atomic::AtomicI8,
|
||||
atomic_ty: core::sync::atomic::AtomicI8,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "ptr",
|
||||
ty: isize,
|
||||
atomic_ty: std::sync::atomic::AtomicIsize,
|
||||
atomic_ty: core::sync::atomic::AtomicIsize,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "16",
|
||||
ty: u16,
|
||||
atomic_ty: std::sync::atomic::AtomicU16,
|
||||
atomic_ty: core::sync::atomic::AtomicU16,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "32",
|
||||
ty: u32,
|
||||
atomic_ty: std::sync::atomic::AtomicU32,
|
||||
atomic_ty: core::sync::atomic::AtomicU32,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "64",
|
||||
ty: u64,
|
||||
atomic_ty: std::sync::atomic::AtomicU64,
|
||||
atomic_ty: core::sync::atomic::AtomicU64,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "8",
|
||||
ty: u8,
|
||||
atomic_ty: std::sync::atomic::AtomicU8,
|
||||
atomic_ty: core::sync::atomic::AtomicU8,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "ptr",
|
||||
ty: usize,
|
||||
atomic_ty: std::sync::atomic::AtomicUsize,
|
||||
atomic_ty: core::sync::atomic::AtomicUsize,
|
||||
}
|
||||
|
|
143
oct/src/encode/encode/test.rs
Normal file
143
oct/src/encode/encode/test.rs
Normal file
|
@ -0,0 +1,143 @@
|
|||
// Copyright 2024-2025 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of
|
||||
// the Mozilla Public License, v. 2.0. If a copy of
|
||||
// the MPL was not distributed with this file, you
|
||||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use core::time::Duration;
|
||||
use oct::encode::{Encode, SizedEncode};
|
||||
use oct::error::UsizeEncodeError;
|
||||
use oct::string::String as OctString;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
macro_rules! test {
|
||||
{
|
||||
$(
|
||||
$ty:ty {
|
||||
$($value:expr => $data:expr),+$(,)?
|
||||
}$(,)?
|
||||
)*
|
||||
} => {{
|
||||
$($({
|
||||
let mut buf = ::std::vec![0x00; <$ty as ::oct::encode::SizedEncode>::MAX_ENCODED_SIZE];
|
||||
|
||||
let mut output = ::oct::encode::Output::new(&mut buf);
|
||||
|
||||
let lhs: ::core::result::Result<&[u8], <$ty as ::oct::encode::Encode>::Error> = <$ty as ::oct::encode::Encode>::encode(&$value, &mut output).map(|_| ::core::borrow::Borrow::borrow(&output));
|
||||
let rhs: ::core::result::Result<&[u8], <$ty as ::oct::encode::Encode>::Error> = $data;
|
||||
|
||||
::std::assert_eq!(lhs, rhs);
|
||||
})*)*
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode() {
|
||||
test! {
|
||||
u8 {
|
||||
0x00 => Ok(&[0x00]),
|
||||
0xFF => Ok(&[0xFF]),
|
||||
0x7F => Ok(&[0x7F]),
|
||||
}
|
||||
|
||||
u16 {
|
||||
0x0F_7E => Ok(&[0x7E, 0x0F]),
|
||||
}
|
||||
|
||||
u32 {
|
||||
0x00_2F_87_E7 => Ok(&[0xE7, 0x87, 0x2F, 0x00]),
|
||||
}
|
||||
|
||||
u64 {
|
||||
0xF3_37_CF_8B_DB_03_2B_39 => Ok(&[0x39, 0x2B, 0x03, 0xDB, 0x8B, 0xCF, 0x37, 0xF3]),
|
||||
}
|
||||
|
||||
u128 {
|
||||
0x45_A0_15_6A_36_77_17_8A_83_2E_3C_2C_84_10_58_1A => Ok(&[
|
||||
0x1A, 0x58, 0x10, 0x84, 0x2C, 0x3C, 0x2E, 0x83,
|
||||
0x8A, 0x17, 0x77, 0x36, 0x6A, 0x15, 0xA0, 0x45,
|
||||
]),
|
||||
}
|
||||
|
||||
usize {
|
||||
0x1A4 => Ok(&[0xA4, 0x01]),
|
||||
|
||||
0x10000 => Err(UsizeEncodeError(0x10000)),
|
||||
}
|
||||
|
||||
[char; 0x5] {
|
||||
['\u{03B4}', '\u{0190}', '\u{03BB}', '\u{03A4}', '\u{03B1}'] => Ok(&[
|
||||
0xB4, 0x03, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00,
|
||||
0xBB, 0x03, 0x00, 0x00, 0xA4, 0x03, 0x00, 0x00,
|
||||
0xB1, 0x03, 0x00, 0x00,
|
||||
]),
|
||||
}
|
||||
|
||||
OctString<0x1> {
|
||||
OctString::new("A").unwrap() => Ok(&[0x01, 0x00, 0x41]),
|
||||
}
|
||||
|
||||
OctString<0xA> {
|
||||
OctString::new("l\u{00F8}gma\u{00F0}ur").unwrap() => Ok(&[
|
||||
0x0A, 0x00, 0x6C, 0xC3, 0xB8, 0x67, 0x6D, 0x61,
|
||||
0xC3, 0xB0, 0x75, 0x72,
|
||||
])
|
||||
}
|
||||
|
||||
Result<u16, char> {
|
||||
Ok(0x4545) => Ok(&[0x00, 0x45, 0x45]),
|
||||
|
||||
Err(char::REPLACEMENT_CHARACTER) => Ok(&[0x01, 0xFD, 0xFF, 0x00, 0x00]),
|
||||
}
|
||||
|
||||
Option<()> {
|
||||
None => Ok(&[0x00]),
|
||||
|
||||
Some(()) => Ok(&[0x01]),
|
||||
}
|
||||
|
||||
SystemTime {
|
||||
UNIX_EPOCH => Ok(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
|
||||
|
||||
UNIX_EPOCH - Duration::from_secs(0x1) => Ok(&[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]),
|
||||
|
||||
UNIX_EPOCH + Duration::from_secs(0x1) => Ok(&[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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}') => Ok(&[0xF2, 0xFD, 0x00, 0x00]),
|
||||
}
|
||||
|
||||
Bar {
|
||||
Bar::Unit => Ok(&[0x45]),
|
||||
|
||||
Bar::Pretty(true) => Ok(&[0x7F, 0x01]),
|
||||
|
||||
Bar::Teacher { initials: ['T', 'L', '\0'] } => Ok(&[
|
||||
0x80, 0x54, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
]),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
// Copyright 2024-2025 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of
|
||||
// the Mozilla Public License, v. 2.0. If a copy of
|
||||
// the MPL was not distributed with this file, you
|
||||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use oct::encode::{Encode, SizedEncode};
|
||||
use std::time::Duration;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
macro_rules! test {
|
||||
{
|
||||
$(
|
||||
$ty:ty {
|
||||
$($value:expr => $data:expr),+$(,)?
|
||||
}$(,)?
|
||||
)*
|
||||
} => {{
|
||||
$($({
|
||||
let right: &[u8] = &$data;
|
||||
let mut left = ::std::vec![0x00; right.len()];
|
||||
|
||||
let mut output = ::oct::encode::Output::new(&mut left);
|
||||
<$ty as ::oct::encode::Encode>::encode(&$value, &mut output).unwrap();
|
||||
|
||||
::std::assert_eq!(left, right);
|
||||
})*)*
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode() {
|
||||
test! {
|
||||
u8 {
|
||||
0x00 => [0x00],
|
||||
0xFF => [0xFF],
|
||||
0x7F => [0x7F],
|
||||
}
|
||||
|
||||
u16 {
|
||||
0x0F_7E => [0x7E, 0x0F],
|
||||
}
|
||||
|
||||
u32 {
|
||||
0x00_2F_87_E7 => [0xE7, 0x87, 0x2F, 0x00],
|
||||
}
|
||||
|
||||
u64 {
|
||||
0xF3_37_CF_8B_DB_03_2B_39 => [0x39, 0x2B, 0x03, 0xDB, 0x8B, 0xCF, 0x37, 0xF3],
|
||||
}
|
||||
|
||||
u128 {
|
||||
0x45_A0_15_6A_36_77_17_8A_83_2E_3C_2C_84_10_58_1A => [
|
||||
0x1A, 0x58, 0x10, 0x84, 0x2C, 0x3C, 0x2E, 0x83,
|
||||
0x8A, 0x17, 0x77, 0x36, 0x6A, 0x15, 0xA0, 0x45,
|
||||
],
|
||||
}
|
||||
|
||||
[char; 0x5] {
|
||||
['\u{03B4}', '\u{0190}', '\u{03BB}', '\u{03A4}', '\u{03B1}'] => [
|
||||
0xB4, 0x03, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00,
|
||||
0xBB, 0x03, 0x00, 0x00, 0xA4, 0x03, 0x00, 0x00,
|
||||
0xB1, 0x03, 0x00, 0x00,
|
||||
],
|
||||
}
|
||||
|
||||
Result<u16, char> {
|
||||
Ok(0x4545) => [0x00, 0x45, 0x45],
|
||||
|
||||
Err(char::REPLACEMENT_CHARACTER) => [0x01, 0xFD, 0xFF, 0x00, 0x00],
|
||||
}
|
||||
|
||||
Option<()> {
|
||||
None => [0x00],
|
||||
|
||||
Some(()) => [0x01],
|
||||
}
|
||||
|
||||
SystemTime {
|
||||
UNIX_EPOCH => [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
|
||||
UNIX_EPOCH - Duration::from_secs(0x1) => [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
|
||||
|
||||
UNIX_EPOCH + Duration::from_secs(0x1) => [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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}') => [0xF2, 0xFD, 0x00, 0x00],
|
||||
}
|
||||
|
||||
Bar {
|
||||
Bar::Unit => [0x45],
|
||||
|
||||
Bar::Pretty(true) => [0x7F, 0x01],
|
||||
|
||||
Bar::Teacher { initials: ['T', 'L', '\0'] } => [
|
||||
0x80, 0x54, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
|
@ -85,6 +85,49 @@ use_mod!(pub sized_encode);
|
|||
///
|
||||
/// The [`Error`](Encode::Error) type will in all cases just be `GenericEncodeError`.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use oct::decode::Decode;
|
||||
/// use oct::encode::Encode;
|
||||
/// use oct::slot::Slot;
|
||||
///
|
||||
/// #[derive(Debug, Decode, Encode, PartialEq)]
|
||||
/// struct Ints {
|
||||
/// value0: u8,
|
||||
/// value1: u16,
|
||||
/// value2: u32,
|
||||
/// value3: u64,
|
||||
/// value4: u128,
|
||||
/// }
|
||||
///
|
||||
/// 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,
|
||||
/// };
|
||||
///
|
||||
/// let mut buf = Slot::with_capacity(0x100);
|
||||
///
|
||||
/// buf.write(VALUE).unwrap();
|
||||
///
|
||||
/// assert_eq!(buf.len(), 0x1F);
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// buf,
|
||||
/// [
|
||||
/// 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
/// 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
/// 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
/// 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E
|
||||
/// ].as_slice(),
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(buf.read().unwrap(), VALUE);
|
||||
/// ```
|
||||
///
|
||||
/// # Enums
|
||||
///
|
||||
/// Enumerations encode like structures except that each variant additionally encodes a unique discriminant.
|
||||
|
@ -93,6 +136,8 @@ use_mod!(pub sized_encode);
|
|||
/// A custom discriminant may be set instead by assigning the variant an integer constant.
|
||||
/// Unspecified discriminants then increment the previous variant's discriminant:
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use oct::encode::Encode;
|
||||
/// use oct::slot::Slot;
|
||||
|
@ -138,6 +183,17 @@ use_mod!(pub sized_encode);
|
|||
///
|
||||
/// Unions cannot derive `Encode` due to the uncertainty of their contents.
|
||||
/// The trait should therefore be implemented manually for such types.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust compile_fail
|
||||
/// use oct::encode::Encode;
|
||||
///
|
||||
/// #[derive(Encode)]
|
||||
/// union MyUnion {
|
||||
/// my_field: u32,
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(feature = "proc-macro")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "proc-macro")))]
|
||||
#[doc(inline)]
|
||||
|
|
|
@ -6,14 +6,17 @@
|
|||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
use crate::error::OutputError;
|
||||
|
||||
use core::borrow::Borrow;
|
||||
use core::fmt::{self, Debug, Formatter};
|
||||
use core::ptr::copy_nonoverlapping;
|
||||
use core::slice;
|
||||
|
||||
/// Byte stream suitable for writing.
|
||||
#[derive(Debug, Eq)]
|
||||
pub struct Output<'a> {
|
||||
buf: &'a mut [u8],
|
||||
pos: usize,
|
||||
|
@ -57,25 +60,6 @@ impl<'a> Output<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets a pointer to the first byte of the output stream.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn as_ptr(&self) -> *const u8 {
|
||||
self.buf.as_ptr()
|
||||
}
|
||||
|
||||
/// Gets a slice of the written bytes in the output stream.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn as_slice(&self) -> &[u8] {
|
||||
unsafe {
|
||||
let ptr = self.as_ptr();
|
||||
let len = self.position();
|
||||
|
||||
slice::from_raw_parts(ptr, len)
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves the maximum capacity of the output stream.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
|
@ -98,6 +82,25 @@ impl<'a> Output<'a> {
|
|||
pub const fn position(&self) -> usize {
|
||||
self.pos
|
||||
}
|
||||
|
||||
/// Gets a pointer to the first byte of the output stream.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn as_ptr(&self) -> *const u8 {
|
||||
self.buf.as_ptr()
|
||||
}
|
||||
|
||||
/// Gets a slice of the written bytes.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn as_slice(&self) -> &[u8] {
|
||||
unsafe {
|
||||
let ptr = self.as_ptr();
|
||||
let len = self.position();
|
||||
|
||||
slice::from_raw_parts(ptr, len)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for Output<'_> {
|
||||
|
@ -114,6 +117,15 @@ impl Borrow<[u8]> for Output<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Debug for Output<'_> {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
Debug::fmt(self.as_slice(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Output<'_> { }
|
||||
|
||||
impl PartialEq for Output<'_> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
|
@ -134,10 +146,3 @@ impl PartialEq<&[u8]> for Output<'_> {
|
|||
self.as_slice() == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<&mut [u8]> for Output<'_> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &&mut [u8]) -> bool {
|
||||
self.as_slice() == *other
|
||||
}
|
||||
}
|
||||
|
|
54
oct/src/encode/output/test.rs
Normal file
54
oct/src/encode/output/test.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2024-2025 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of
|
||||
// the Mozilla Public License, v. 2.0. If a copy of
|
||||
// the MPL was not distributed with this file, you
|
||||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use oct::encode::Output;
|
||||
use oct::error::OutputError;
|
||||
|
||||
#[test]
|
||||
fn test_encode_output() {
|
||||
let mut buf = [0x00; 0x4];
|
||||
|
||||
let mut output = Output::new(&mut buf);
|
||||
|
||||
assert_eq!(output.capacity(), 0x4);
|
||||
assert_eq!(output.position(), 0x0);
|
||||
assert_eq!(output.remaining(), 0x4);
|
||||
|
||||
assert_eq!(output, [].as_slice());
|
||||
assert_eq!(output.write(&[0x04]), Ok(()));
|
||||
|
||||
assert_eq!(output.capacity(), 0x4);
|
||||
assert_eq!(output.position(), 0x1);
|
||||
assert_eq!(output.remaining(), 0x3);
|
||||
|
||||
assert_eq!(output, [0x04].as_slice());
|
||||
assert_eq!(output.write(&[0x03]), Ok(()));
|
||||
|
||||
assert_eq!(output.capacity(), 0x4);
|
||||
assert_eq!(output.position(), 0x2);
|
||||
assert_eq!(output.remaining(), 0x2);
|
||||
|
||||
assert_eq!(output, [0x04, 0x03].as_slice());
|
||||
assert_eq!(output.write(&[0x02]), Ok(()));
|
||||
|
||||
assert_eq!(output.capacity(), 0x4);
|
||||
assert_eq!(output.position(), 0x3);
|
||||
assert_eq!(output.remaining(), 0x1);
|
||||
|
||||
assert_eq!(output, [0x04, 0x03, 0x02].as_slice());
|
||||
assert_eq!(output.write(&[0x01]), Ok(()));
|
||||
|
||||
assert_eq!(output.capacity(), 0x4);
|
||||
assert_eq!(output.position(), 0x4);
|
||||
assert_eq!(output.remaining(), 0x0);
|
||||
|
||||
assert_eq!(output, [0x04, 0x03, 0x02, 0x01].as_slice());
|
||||
assert_eq!(output.write(&[0x00]), Err(OutputError { capacity: 0x4, position: 0x4, count: 0x1 }));
|
||||
|
||||
assert_eq!(output.write(&[]), Ok(()));
|
||||
}
|
|
@ -7,12 +7,13 @@
|
|||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod test;
|
||||
|
||||
use crate::encode::Encode;
|
||||
|
||||
use core::cell::{Cell, LazyCell, RefCell, UnsafeCell};
|
||||
use core::convert::Infallible;
|
||||
use core::ffi::c_void;
|
||||
use core::marker::{PhantomData, PhantomPinned};
|
||||
use core::net::{
|
||||
IpAddr,
|
||||
|
@ -60,6 +61,7 @@ use {
|
|||
///
|
||||
/// Also note that -- in practice -- this trait is **not** strictly enforceable.
|
||||
/// Users of this trait should assume that it is mostly properly defined, but still with the possibility of it not being such.
|
||||
#[doc(alias("SizedSerialise", "SizedSerialize"))]
|
||||
pub trait SizedEncode: Encode {
|
||||
/// The maximum, guaranteed amount of bytes that can result from an encoding.
|
||||
///
|
||||
|
@ -108,6 +110,10 @@ impl<T: SizedEncode + ?Sized> SizedEncode for Box<T> {
|
|||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
impl SizedEncode for c_void {
|
||||
const MAX_ENCODED_SIZE: usize = 0x0;
|
||||
}
|
||||
|
||||
impl<T: Copy + SizedEncode> SizedEncode for Cell<T> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
@ -178,12 +184,6 @@ impl<T: SizedEncode + ?Sized> SizedEncode for Mutex<T> {
|
|||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
||||
}
|
||||
|
||||
#[cfg(feature = "never-type")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "never-type")))]
|
||||
impl SizedEncode for ! {
|
||||
const MAX_ENCODED_SIZE: usize = 0x0;
|
||||
}
|
||||
|
||||
impl<T: SizedEncode> SizedEncode for Option<T> {
|
||||
const MAX_ENCODED_SIZE: usize =
|
||||
bool::MAX_ENCODED_SIZE
|
||||
|
@ -478,65 +478,65 @@ impl_non_zero!(usize);
|
|||
impl_atomic! {
|
||||
width: "8",
|
||||
ty: bool,
|
||||
atomic_ty: std::sync::atomic::AtomicBool,
|
||||
atomic_ty: core::sync::atomic::AtomicBool,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "16",
|
||||
ty: i16,
|
||||
atomic_ty: std::sync::atomic::AtomicI16,
|
||||
atomic_ty: core::sync::atomic::AtomicI16,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "32",
|
||||
ty: i32,
|
||||
atomic_ty: std::sync::atomic::AtomicI32,
|
||||
atomic_ty: core::sync::atomic::AtomicI32,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "64",
|
||||
ty: i64,
|
||||
atomic_ty: std::sync::atomic::AtomicI64,
|
||||
atomic_ty: core::sync::atomic::AtomicI64,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "8",
|
||||
ty: i8,
|
||||
atomic_ty: std::sync::atomic::AtomicI8,
|
||||
atomic_ty: core::sync::atomic::AtomicI8,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "ptr",
|
||||
ty: isize,
|
||||
atomic_ty: std::sync::atomic::AtomicIsize,
|
||||
atomic_ty: core::sync::atomic::AtomicIsize,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "16",
|
||||
ty: u16,
|
||||
atomic_ty: std::sync::atomic::AtomicU16,
|
||||
atomic_ty: core::sync::atomic::AtomicU16,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "32",
|
||||
ty: u32,
|
||||
atomic_ty: std::sync::atomic::AtomicU32,
|
||||
atomic_ty: core::sync::atomic::AtomicU32,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "64",
|
||||
ty: u64,
|
||||
atomic_ty: std::sync::atomic::AtomicU64,
|
||||
atomic_ty: core::sync::atomic::AtomicU64,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "8",
|
||||
ty: u8,
|
||||
atomic_ty: std::sync::atomic::AtomicU8,
|
||||
atomic_ty: core::sync::atomic::AtomicU8,
|
||||
}
|
||||
|
||||
impl_atomic! {
|
||||
width: "ptr",
|
||||
ty: usize,
|
||||
atomic_ty: std::sync::atomic::AtomicUsize,
|
||||
atomic_ty: core::sync::atomic::AtomicUsize,
|
||||
}
|
||||
|
|
|
@ -6,10 +6,9 @@
|
|||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use oct::encode::{Encode, SizedEncode};
|
||||
use std::convert::Infallible;
|
||||
use std::marker::PhantomData;
|
||||
use std::net::{
|
||||
use core::convert::Infallible;
|
||||
use core::marker::PhantomData;
|
||||
use core::net::{
|
||||
IpAddr,
|
||||
Ipv4Addr,
|
||||
Ipv6Addr,
|
||||
|
@ -17,7 +16,8 @@ use std::net::{
|
|||
SocketAddrV4,
|
||||
SocketAddrV6,
|
||||
};
|
||||
use std::num::NonZero;
|
||||
use core::num::NonZero;
|
||||
use oct::encode::{Encode, SizedEncode};
|
||||
|
||||
macro_rules! assert_encoded_size {
|
||||
($ty:ty, $value:expr$(,)?) => {{
|
|
@ -6,8 +6,10 @@
|
|||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
use core::hint::unreachable_unchecked;
|
||||
|
||||
/// A character could not be decoded.
|
||||
///
|
||||
|
@ -31,3 +33,12 @@ impl Display for CharDecodeError {
|
|||
}
|
||||
|
||||
impl Error for CharDecodeError { }
|
||||
|
||||
impl From<Infallible> for CharDecodeError {
|
||||
#[inline(always)]
|
||||
fn from(_value: Infallible) -> Self {
|
||||
// SAFETY: `Infallible` objects can never be con-
|
||||
// structed.
|
||||
unsafe { unreachable_unchecked() };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,12 +9,13 @@
|
|||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
use core::hint::unreachable_unchecked;
|
||||
|
||||
/// 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 [`BadLength`](Self::BadLength) variant -- and when an element is invalid -- see the [`Item`](Self::BadItem)) variant.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[must_use]
|
||||
pub enum CollectionDecodeError<L, I> {
|
||||
/// The collection length could not be decoded or was invalid.
|
||||
|
@ -63,6 +64,15 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<L, I> From<Infallible> for CollectionDecodeError<L, I> {
|
||||
#[inline(always)]
|
||||
fn from(_value: Infallible) -> Self {
|
||||
// SAFETY: `Infallible` objects can never be con-
|
||||
// structed.
|
||||
unsafe { unreachable_unchecked() };
|
||||
}
|
||||
}
|
||||
|
||||
impl<L, I> From<CollectionDecodeError<L, I>> for Infallible
|
||||
where
|
||||
L: Into<Self>,
|
||||
|
|
|
@ -9,12 +9,13 @@
|
|||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
use core::hint::unreachable_unchecked;
|
||||
|
||||
/// 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 [`BadLength`](Self::BadLength) variant -- and when an element is invalid -- see the [`Item`](Self::BadItem)) variant.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[must_use]
|
||||
pub enum CollectionEncodeError<L, I> {
|
||||
/// The collection length could not be encoded.
|
||||
|
@ -56,6 +57,15 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<L, I> From<Infallible> for CollectionEncodeError<L, I> {
|
||||
#[inline(always)]
|
||||
fn from(_value: Infallible) -> Self {
|
||||
// SAFETY: `Infallible` objects can never be con-
|
||||
// structed.
|
||||
unsafe { unreachable_unchecked() };
|
||||
}
|
||||
}
|
||||
|
||||
impl<L, I> From<CollectionEncodeError<L, I>> for Infallible
|
||||
where
|
||||
L: Into<Self>,
|
||||
|
|
|
@ -11,30 +11,29 @@ use crate::decode::Decode;
|
|||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Debug, Display, Formatter};
|
||||
use core::hint::unreachable_unchecked;
|
||||
|
||||
/// An enumeration could not be decoded.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[must_use]
|
||||
pub enum EnumDecodeError<D: Decode, F> {
|
||||
pub enum EnumDecodeError<T, D, F> {
|
||||
/// The discriminant could not be decoded.
|
||||
InvalidDiscriminant(D::Error),
|
||||
InvalidDiscriminant(D),
|
||||
|
||||
/// 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
|
||||
},
|
||||
UnassignedDiscriminant(T),
|
||||
|
||||
/// A field could not be encoded.
|
||||
BadField(F),
|
||||
}
|
||||
|
||||
impl<D, F> Display for EnumDecodeError<D, F>
|
||||
impl<T, D, F> Display for EnumDecodeError<T, D, F>
|
||||
where
|
||||
D: Decode<Error: Display> + Display,
|
||||
T: Display,
|
||||
D: Display,
|
||||
F: Display,
|
||||
{
|
||||
#[inline]
|
||||
|
@ -43,7 +42,7 @@ where
|
|||
Self::InvalidDiscriminant(ref e)
|
||||
=> write!(f, "discriminant could not be decoded: {e}"),
|
||||
|
||||
Self::UnassignedDiscriminant { ref value }
|
||||
Self::UnassignedDiscriminant(ref value)
|
||||
=> write!(f, "`{value}` is not an assigned discriminant for the given enumeration"),
|
||||
|
||||
Self::BadField(ref e)
|
||||
|
@ -52,9 +51,10 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<D, F> Error for EnumDecodeError<D, F>
|
||||
impl<T, D, F> Error for EnumDecodeError<T, D, F>
|
||||
where
|
||||
D: Debug + Decode<Error: Error + 'static> + Display,
|
||||
T: Debug + Display,
|
||||
D: Error + 'static,
|
||||
F: Error + 'static,
|
||||
{
|
||||
#[inline]
|
||||
|
@ -69,13 +69,22 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<D, F> From<EnumDecodeError<D, F>> for Infallible
|
||||
impl<T, D, F> From<Infallible> for EnumDecodeError<T, D, F> {
|
||||
#[inline(always)]
|
||||
fn from(_value: Infallible) -> Self {
|
||||
// SAFETY: `Infallible` objects can never be con-
|
||||
// structed.
|
||||
unsafe { unreachable_unchecked() };
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D, F> From<EnumDecodeError<T, D, F>> for Infallible
|
||||
where
|
||||
D: Decode<Error: Into<Self>>,
|
||||
T: Decode<Error: Into<Self>>,
|
||||
F: Into<Self>,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn from(_value: EnumDecodeError<D, F>) -> Self {
|
||||
fn from(_value: EnumDecodeError<T, D, F>) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,18 +6,17 @@
|
|||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use crate::encode::Encode;
|
||||
|
||||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Debug, Display, Formatter};
|
||||
use core::hint::unreachable_unchecked;
|
||||
|
||||
/// An enumeration could not be encoded.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[must_use]
|
||||
pub enum EnumEncodeError<D: Encode, F> {
|
||||
pub enum EnumEncodeError<D, F> {
|
||||
/// The discriminant could not be encoded.
|
||||
BadDiscriminant(D::Error),
|
||||
BadDiscriminant(D),
|
||||
|
||||
/// A field could not be encoded.
|
||||
BadField(F),
|
||||
|
@ -25,7 +24,7 @@ pub enum EnumEncodeError<D: Encode, F> {
|
|||
|
||||
impl<D, F> Display for EnumEncodeError<D, F>
|
||||
where
|
||||
D: Display + Encode<Error: Display>,
|
||||
D: Display,
|
||||
F: Display,
|
||||
{
|
||||
#[inline]
|
||||
|
@ -42,7 +41,7 @@ where
|
|||
|
||||
impl<D, F> Error for EnumEncodeError<D, F>
|
||||
where
|
||||
D: Debug + Display + Encode<Error: Error + 'static>,
|
||||
D: Error + 'static,
|
||||
F: Error + 'static,
|
||||
{
|
||||
#[inline]
|
||||
|
@ -55,9 +54,18 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<D, F> From<Infallible> for EnumEncodeError<D, F> {
|
||||
#[inline(always)]
|
||||
fn from(_value: Infallible) -> Self {
|
||||
// SAFETY: `Infallible` objects can never be con-
|
||||
// structed.
|
||||
unsafe { unreachable_unchecked() };
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, F> From<EnumEncodeError<D, F>> for Infallible
|
||||
where
|
||||
D: Encode<Error: Into<Self>>,
|
||||
D: Into<Self>,
|
||||
F: Into<Self>,
|
||||
{
|
||||
#[inline(always)]
|
||||
|
|
|
@ -6,16 +6,15 @@
|
|||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use crate::PrimitiveDiscriminant;
|
||||
use crate::decode::Decode;
|
||||
use crate::{PrimDiscriminant, PrimRepr};
|
||||
use crate::error::{
|
||||
CollectionDecodeError,
|
||||
EnumDecodeError,
|
||||
ItemDecodeError,
|
||||
NonZeroDecodeError,
|
||||
LengthError,
|
||||
StringError,
|
||||
SystemTimeDecodeError,
|
||||
Utf8Error,
|
||||
};
|
||||
|
||||
use core::convert::Infallible;
|
||||
|
@ -25,28 +24,25 @@ use core::hint::unreachable_unchecked;
|
|||
|
||||
/// A generic decoding error type.
|
||||
///
|
||||
/// The intended use of this type is by [derived](derive@Decode) implementations of [`Decode`].
|
||||
/// The intended use of this type is by [derived](derive@crate::decode::Decode) implementations of [`crate::decode::Decode`].
|
||||
/// Manual implementors are recommended to use a custom or less generic type for the sake of efficiency.
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[must_use]
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum GenericDecodeError {
|
||||
/// A string contained a non-UTF-8 sequence.
|
||||
BadString(StringError),
|
||||
BadString(Utf8Error),
|
||||
|
||||
/// A non-null integer was null.
|
||||
/// A non-zero integer was null.
|
||||
NullInteger(NonZeroDecodeError),
|
||||
|
||||
/// A statically-sized buffer was too small.
|
||||
/// A size-constrained buffer was too small.
|
||||
SmallBuffer(LengthError),
|
||||
|
||||
/// 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
|
||||
},
|
||||
UnassignedDiscriminant(PrimDiscriminant),
|
||||
|
||||
/// The [`SystemTime`](std::time::SystemTime) type was too narrow.
|
||||
#[cfg(feature = "std")]
|
||||
|
@ -67,7 +63,7 @@ impl Display for GenericDecodeError {
|
|||
Self::SmallBuffer(ref e)
|
||||
=> write!(f, "{e}"),
|
||||
|
||||
Self::UnassignedDiscriminant { value }
|
||||
Self::UnassignedDiscriminant(value)
|
||||
=> write!(f, "discriminant value `{value:#X} has not been assigned"),
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
|
@ -112,19 +108,20 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<D, F> From<EnumDecodeError<D, F>> for GenericDecodeError
|
||||
impl<T, D, F> From<EnumDecodeError<T, D, F>> for GenericDecodeError
|
||||
where
|
||||
D: Decode<Error: Into<Self>> + PrimitiveDiscriminant,
|
||||
T: PrimRepr,
|
||||
D: Into<Self>,
|
||||
F: Into<Self>,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn from(value: EnumDecodeError<D, F>) -> Self {
|
||||
fn from(value: EnumDecodeError<T, D, F>) -> Self {
|
||||
use EnumDecodeError as Error;
|
||||
|
||||
match value {
|
||||
Error::InvalidDiscriminant(e) => e.into(),
|
||||
|
||||
Error::UnassignedDiscriminant { value } => Self::UnassignedDiscriminant { value: value.to_u128() },
|
||||
Error::UnassignedDiscriminant(value) => Self::UnassignedDiscriminant(value.into_prim_discriminant()),
|
||||
|
||||
Error::BadField(e) => e.into(),
|
||||
}
|
||||
|
@ -161,13 +158,6 @@ impl From<LengthError> for GenericDecodeError {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<StringError> for GenericDecodeError {
|
||||
#[inline(always)]
|
||||
fn from(value: StringError) -> Self {
|
||||
Self::BadString(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl From<SystemTimeDecodeError> for GenericDecodeError {
|
||||
|
@ -176,3 +166,10 @@ impl From<SystemTimeDecodeError> for GenericDecodeError {
|
|||
Self::NarrowSystemTime(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Utf8Error> for GenericDecodeError {
|
||||
#[inline(always)]
|
||||
fn from(value: Utf8Error) -> Self {
|
||||
Self::BadString(value)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use crate::encode::Encode;
|
||||
use crate::error::{
|
||||
CollectionEncodeError,
|
||||
EnumEncodeError,
|
||||
|
@ -23,11 +22,11 @@ use core::hint::unreachable_unchecked;
|
|||
|
||||
/// A generic encoding error type.
|
||||
///
|
||||
/// The intended use of this type is by [derived](derive@crate::encode::Encode) implementations of [`Encode`].
|
||||
/// The intended use of this type is by [derived](derive@crate::encode::Encode) implementations of [`crate::encode::Encode`].
|
||||
/// Manual implementors are recommended to use a custom or less generic type for the sake of efficiency.
|
||||
#[derive(Debug)]
|
||||
#[must_use]
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug)]
|
||||
pub enum GenericEncodeError {
|
||||
/// A [`RefCell`](core::cell::RefCell) object could not be borrowed.
|
||||
BadBorrow(BorrowError),
|
||||
|
@ -96,7 +95,7 @@ where
|
|||
|
||||
impl<D, F> From<EnumEncodeError<D, F>> for GenericEncodeError
|
||||
where
|
||||
D: Encode<Error: Into<Self>>,
|
||||
D: Into<Self>,
|
||||
F: Into<Self>,
|
||||
{
|
||||
#[inline(always)]
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
use core::hint::unreachable_unchecked;
|
||||
|
||||
/// An input-related error.
|
||||
///
|
||||
|
@ -39,3 +41,12 @@ impl Display for InputError {
|
|||
}
|
||||
|
||||
impl Error for InputError { }
|
||||
|
||||
impl From<Infallible> for InputError {
|
||||
#[inline(always)]
|
||||
fn from(_value: Infallible) -> Self {
|
||||
// SAFETY: `Infallible` objects can never be con-
|
||||
// structed.
|
||||
unsafe { unreachable_unchecked() };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
use core::hint::unreachable_unchecked;
|
||||
|
||||
/// An [`isize`] value could not be decoded.
|
||||
///
|
||||
|
@ -33,3 +35,12 @@ impl Display for IsizeEncodeError {
|
|||
}
|
||||
|
||||
impl Error for IsizeEncodeError { }
|
||||
|
||||
impl From<Infallible> for IsizeEncodeError {
|
||||
#[inline(always)]
|
||||
fn from(_value: Infallible) -> Self {
|
||||
// SAFETY: `Infallible` objects can never be con-
|
||||
// structed.
|
||||
unsafe { unreachable_unchecked() };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,11 +9,12 @@
|
|||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Debug, Display, Formatter};
|
||||
use core::hint::unreachable_unchecked;
|
||||
|
||||
/// A collection's item could not be decoded.
|
||||
///
|
||||
/// See also [`CollectionDecodeError`](crate::error::CollectionDecodeError).
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[must_use]
|
||||
pub struct ItemDecodeError<I, E> {
|
||||
/// The index of the invalid item.
|
||||
|
@ -45,6 +46,15 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<I, E> From<Infallible> for ItemDecodeError<I, E> {
|
||||
#[inline(always)]
|
||||
fn from(_value: Infallible) -> Self {
|
||||
// SAFETY: `Infallible` objects can never be con-
|
||||
// structed.
|
||||
unsafe { unreachable_unchecked() };
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, E> From<ItemDecodeError<I, E>> for Infallible {
|
||||
#[inline(always)]
|
||||
fn from(_value: ItemDecodeError<I, E>) -> Self {
|
||||
|
|
|
@ -9,11 +9,12 @@
|
|||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Debug, Display, Formatter};
|
||||
use core::hint::unreachable_unchecked;
|
||||
|
||||
/// A collection's item could not be encoded.
|
||||
///
|
||||
/// See also [`CollectionEncodeError`](crate::error::CollectionEncodeError).
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[must_use]
|
||||
pub struct ItemEncodeError<I, E> {
|
||||
/// The index of the invalid item.
|
||||
|
@ -45,6 +46,15 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<I, E> From<Infallible> for ItemEncodeError<I, E> {
|
||||
#[inline(always)]
|
||||
fn from(_value: Infallible) -> Self {
|
||||
// SAFETY: `Infallible` objects can never be con-
|
||||
// structed.
|
||||
unsafe { unreachable_unchecked() };
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, E: Into<Self>> From<ItemEncodeError<I, E>> for Infallible {
|
||||
#[inline(always)]
|
||||
fn from(_value: ItemEncodeError<I, E>) -> Self {
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
use core::hint::unreachable_unchecked;
|
||||
|
||||
/// A collection buffer was too small to contain all of its elements.
|
||||
///
|
||||
|
@ -35,3 +37,12 @@ impl Display for LengthError {
|
|||
}
|
||||
|
||||
impl Error for LengthError { }
|
||||
|
||||
impl From<Infallible> for LengthError {
|
||||
#[inline(always)]
|
||||
fn from(_value: Infallible) -> Self {
|
||||
// SAFETY: `Infallible` objects can never be con-
|
||||
// structed.
|
||||
unsafe { unreachable_unchecked() };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ use_mod!(pub length_error);
|
|||
use_mod!(pub non_zero_decode_error);
|
||||
use_mod!(pub output_error);
|
||||
use_mod!(pub ref_cell_encode_error);
|
||||
use_mod!(pub string_error);
|
||||
use_mod!(pub usize_encode_error);
|
||||
use_mod!(pub utf8_error);
|
||||
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
use core::hint::unreachable_unchecked;
|
||||
|
||||
/// A non-zero integer could not be decoded.
|
||||
///
|
||||
|
@ -23,3 +25,12 @@ impl Display for NonZeroDecodeError {
|
|||
}
|
||||
|
||||
impl Error for NonZeroDecodeError { }
|
||||
|
||||
impl From<Infallible> for NonZeroDecodeError {
|
||||
#[inline(always)]
|
||||
fn from(_value: Infallible) -> Self {
|
||||
// SAFETY: `Infallible` objects can never be con-
|
||||
// structed.
|
||||
unsafe { unreachable_unchecked() };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
use core::hint::unreachable_unchecked;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[must_use]
|
||||
|
@ -39,3 +41,12 @@ impl Display for OutputError {
|
|||
}
|
||||
|
||||
impl Error for OutputError { }
|
||||
|
||||
impl From<Infallible> for OutputError {
|
||||
#[inline(always)]
|
||||
fn from(_value: Infallible) -> Self {
|
||||
// SAFETY: `Infallible` objects can never be con-
|
||||
// structed.
|
||||
unsafe { unreachable_unchecked() };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use core::cell::BorrowError;
|
||||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
use core::hint::unreachable_unchecked;
|
||||
|
||||
/// A reference cell could not be encoded.
|
||||
///
|
||||
|
@ -48,3 +50,12 @@ impl<E: Error + 'static> Error for RefCellEncodeError<E> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> From<Infallible> for RefCellEncodeError<E> {
|
||||
#[inline(always)]
|
||||
fn from(_value: Infallible) -> Self {
|
||||
// SAFETY: `Infallible` objects can never be con-
|
||||
// structed.
|
||||
unsafe { unreachable_unchecked() };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
// Copyright 2024-2025 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of
|
||||
// the Mozilla Public License, v. 2.0. If a copy of
|
||||
// the MPL was not distributed with this file, you
|
||||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use crate::error::{LengthError, Utf8Error};
|
||||
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
|
||||
/// String error variants.
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
#[must_use]
|
||||
pub enum StringError {
|
||||
/// An invalid UTF-8 sequence was encountered.
|
||||
BadUtf8(Utf8Error),
|
||||
|
||||
/// A fixed-size buffer was too small.
|
||||
SmallBuffer(LengthError),
|
||||
}
|
||||
|
||||
impl Display for StringError {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Self::BadUtf8(ref e)
|
||||
=> write!(f, "bad utf-8: {e}"),
|
||||
|
||||
Self::SmallBuffer(ref e)
|
||||
=> write!(f, "buffer too small: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for StringError {
|
||||
#[inline]
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
match *self {
|
||||
Self::BadUtf8(ref e) => Some(e),
|
||||
|
||||
Self::SmallBuffer(ref e) => Some(e),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,8 +6,10 @@
|
|||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
use core::hint::unreachable_unchecked;
|
||||
|
||||
/// The [`SystemTime`](std::time::SystemTime) type could not represent a UNIX timestamp.
|
||||
///
|
||||
|
@ -31,3 +33,13 @@ impl Display for SystemTimeDecodeError {
|
|||
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl Error for SystemTimeDecodeError { }
|
||||
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl From<Infallible> for SystemTimeDecodeError {
|
||||
#[inline(always)]
|
||||
fn from(_value: Infallible) -> Self {
|
||||
// SAFETY: `Infallible` objects can never be con-
|
||||
// structed.
|
||||
unsafe { unreachable_unchecked() };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
use core::hint::unreachable_unchecked;
|
||||
|
||||
/// A [`usize`] value could not be decoded.
|
||||
///
|
||||
|
@ -32,3 +34,12 @@ impl Display for UsizeEncodeError {
|
|||
}
|
||||
|
||||
impl Error for UsizeEncodeError { }
|
||||
|
||||
impl From<Infallible> for UsizeEncodeError {
|
||||
#[inline(always)]
|
||||
fn from(_value: Infallible) -> Self {
|
||||
// SAFETY: `Infallible` objects can never be con-
|
||||
// structed.
|
||||
unsafe { unreachable_unchecked() };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use core::convert::Infallible;
|
||||
use core::error::Error;
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
use core::hint::unreachable_unchecked;
|
||||
|
||||
/// An invalid UTF-8 sequence was encountered.
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
|
@ -28,3 +30,12 @@ impl Display for Utf8Error {
|
|||
}
|
||||
|
||||
impl Error for Utf8Error { }
|
||||
|
||||
impl From<Infallible> for Utf8Error {
|
||||
#[inline(always)]
|
||||
fn from(_value: Infallible) -> Self {
|
||||
// SAFETY: `Infallible` objects can never be con-
|
||||
// structed.
|
||||
unsafe { unreachable_unchecked() };
|
||||
}
|
||||
}
|
||||
|
|
108
oct/src/lib.rs
108
oct/src/lib.rs
|
@ -28,18 +28,19 @@
|
|||
//!
|
||||
//! | Benchmark | [Bincode] | [Borsh] | Oct | [Postcard] |
|
||||
//! | :--------------------------------- | --------: | ------: | -----: | ---------: |
|
||||
//! | `encode_u8` | 0.977 | 0.871 | 0.754 | 0.916 |
|
||||
//! | `encode_u32` | 0.967 | 0.983 | 0.730 | 2.727 |
|
||||
//! | `encode_u128` | 2.178 | 2.175 | 1.481 | 6.002 |
|
||||
//! | `encode_u8` | 0.927 | 0.939 | 0.742 | 0.896 |
|
||||
//! | `encode_u32` | 1.069 | 1.007 | 0.738 | 2.732 |
|
||||
//! | `encode_u128` | 2.180 | 2.204 | 1.522 | 6.412 |
|
||||
//! | `encode_char` | 2.474 | 1.261 | 0.817 | 2.480 |
|
||||
//! | `encode_struct_unit` | 0.000 | 0.000 | 0.000 | 0.000 |
|
||||
//! | `encode_struct_unnamed` | 1.206 | 1.168 | 0.805 | 2.356 |
|
||||
//! | `encode_struct_named` | 3.021 | 1.532 | 0.952 | 3.013 |
|
||||
//! | `encode_enum_unit` | 0.245 | 0.294 | 0.000 | 0.294 |
|
||||
//! | `decode_u8` | 0.952 | 0.895 | 0.885 | 0.894 |
|
||||
//! | `decode_non_zero_u8` | 1.215 | 1.250 | 1.229 | 1.232 |
|
||||
//! | `decode_bool` | 1.204 | 1.224 | 1.126 | 1.176 |
|
||||
//! | **Total time** → | 11.964 | 10.392 | 7.963 | 18.609 |
|
||||
//! | **Total deviation (p.c.)** → | +50 | +31 | ±0 | +134 |
|
||||
//! | `encode_struct_unnamed` | 1.245 | 1.146 | 0.834 | 2.378 |
|
||||
//! | `encode_struct_named` | 3.037 | 1.541 | 0.961 | 3.014 |
|
||||
//! | `encode_enum_unit` | 0.250 | 0.297 | 0.000 | 0.296 |
|
||||
//! | `decode_u8` | 0.992 | 0.926 | 0.915 | 0.981 |
|
||||
//! | `decode_non_zero_u8` | 1.218 | 1.215 | 1.225 | 1.238 |
|
||||
//! | `decode_bool` | 1.064 | 1.088 | 1.046 | 1.080 |
|
||||
//! | **Total time** → | 14.456 | 11.624 | 8.800 | 21.509 |
|
||||
//! | **Total deviation (p.c.)** → | +64 | +32 | ±0 | +144 |
|
||||
//!
|
||||
//! [Bincode]: https://crates.io/crates/bincode/
|
||||
//! [Borsh]: https://crates.io/crates/borsh/
|
||||
|
@ -53,11 +54,11 @@
|
|||
//!
|
||||
//! # 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).
|
||||
//! Primitives encode losslessly by default, although [`usize`] and [`isize`] are the exception to this.
|
||||
//! Due to their machine-dependent representation, these are truncated to the smallest subset of values guaranteed by Rust, with this equating to a cast to [`u16`] or [`i16`], respectively.
|
||||
//!
|
||||
//! Numerical primitives in general encode as little endian (and **not** ["network order"](https://en.wikipedia.org/wiki/Endianness#Networking)).
|
||||
//! It is recommended for implementors to follow this convention as well.
|
||||
//! Numerical types in general (including `char`) are encoded as little endian (and **not** ["network order"](https://en.wikipedia.org/wiki/Endianness#Networking) as is the norm in TCP/UDP/IP).
|
||||
//! It is recommended for implementors of custom types to adhere to this convention as well.
|
||||
//!
|
||||
//! See specific types' implementations for notes on their data models.
|
||||
//!
|
||||
|
@ -68,54 +69,10 @@
|
|||
//!
|
||||
//! This crate revolves around the [`Encode`](encode::Encode) and [`Decode`](decode::Decode) traits, both of which handle conversions to and from byte streams.
|
||||
//!
|
||||
//! Many standard types come implemented with Oct, 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.
|
||||
//! These traits are already implemented by Oct for a large set of the standard types, such as [`Option`] and [`Mutex`](std::sync::Mutex).
|
||||
//! Some [features](#feature-flags) enable an extended set of implementations that are locked behind unstable feature gates or other crates.
|
||||
//!
|
||||
//! It is recommended in most cases to simply derive these two traits for user-defined types, although this is only supported for enumerations and structures -- not untagged unions.
|
||||
//! When deriving, each field is *chained* according to declaration order:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use oct::decode::Decode;
|
||||
//! use oct::encode::Encode;
|
||||
//! use oct::slot::Slot;
|
||||
//!
|
||||
//! #[derive(Debug, Decode, Encode, PartialEq)]
|
||||
//! struct Ints {
|
||||
//! value0: u8,
|
||||
//! value1: u16,
|
||||
//! value2: u32,
|
||||
//! value3: u64,
|
||||
//! value4: u128,
|
||||
//! }
|
||||
//!
|
||||
//! 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,
|
||||
//! };
|
||||
//!
|
||||
//! let mut buf = Slot::with_capacity(0x100);
|
||||
//!
|
||||
//! buf.write(VALUE).unwrap();
|
||||
//!
|
||||
//! assert_eq!(buf.len(), 0x1F);
|
||||
//!
|
||||
//! assert_eq!(
|
||||
//! buf,
|
||||
//! [
|
||||
//! 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
//! 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
//! 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
//! 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E
|
||||
//! ].as_slice(),
|
||||
//! );
|
||||
//!
|
||||
//! assert_eq!(buf.read().unwrap(), VALUE);
|
||||
//! ```
|
||||
//!
|
||||
//! The following is a more complete example of a UDP server/client for geographic data:
|
||||
//! The following is an example of a UDP server/client for geographic data:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use oct::decode::Decode;
|
||||
|
@ -126,8 +83,8 @@
|
|||
//! use std::thread::spawn;
|
||||
//!
|
||||
//! // City, region, etc.:
|
||||
//! #[derive(Clone, Copy, Debug, Decode, Encode, Eq, PartialEq, SizedEncode)]
|
||||
//! #[non_exhaustive]
|
||||
//! #[derive(Clone, Copy, Debug, Decode, Encode, Eq, PartialEq, SizedEncode)]
|
||||
//! enum Area {
|
||||
//! AlQuds,
|
||||
//! Byzantion,
|
||||
|
@ -137,8 +94,8 @@
|
|||
//! }
|
||||
//!
|
||||
//! // Client-to-server message:
|
||||
//! #[derive(Debug, Decode, Encode, PartialEq, SizedEncode)]
|
||||
//! #[non_exhaustive]
|
||||
//! #[derive(Debug, Decode, Encode, PartialEq, SizedEncode)]
|
||||
//! enum Request {
|
||||
//! AtmosphericHumidity { area: Area },
|
||||
//! AtmosphericPressure { area: Area },
|
||||
|
@ -147,8 +104,8 @@
|
|||
//! }
|
||||
//!
|
||||
//! // Server-to-client message:
|
||||
//! #[derive(Debug, Decode, Encode, PartialEq, SizedEncode)]
|
||||
//! #[non_exhaustive]
|
||||
//! #[derive(Debug, Decode, Encode, PartialEq, SizedEncode)]
|
||||
//! enum Response {
|
||||
//! AtmosphericHumidity(f64),
|
||||
//! AtmosphericPressure(f64), // Pascal
|
||||
|
@ -159,8 +116,8 @@
|
|||
//! struct Party {
|
||||
//! pub socket: UdpSocket,
|
||||
//!
|
||||
//! pub request_buf: Slot::<Request>,
|
||||
//! pub response_buf: Slot::<Response>,
|
||||
//! pub request_buf: Slot<Request>,
|
||||
//! pub response_buf: Slot<Response>,
|
||||
//! }
|
||||
//!
|
||||
//! impl Party {
|
||||
|
@ -230,13 +187,18 @@
|
|||
//! * `proc-macro`: Pulls procedural macros from the [`oct-macros`](https://crates.io/crates/oct-macros/) crate
|
||||
//! * `std`: Enables implementations for types [`std`], e.g. [`Mutex`](std::sync::Mutex) and [`RwLock`](std::sync::RwLock)
|
||||
//!
|
||||
//! The following features can additionally be enabled for support with nightly-only constructs:
|
||||
//!
|
||||
//! * `f128`: Enable implementations for the [`f128`] type
|
||||
//! * `f16`: Enable implementations for the [`f16`] type
|
||||
//!
|
||||
//! # Documentation
|
||||
//!
|
||||
//! Oct has its documentation written alongside its source code for use by `rustdoc`.
|
||||
//! See [Docs.rs](https://docs.rs/oct/latest/oct/) 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.
|
||||
//! The nightly toolchain is therefore always required when rendering them or or running tests herein.
|
||||
//!
|
||||
//! # Contribution
|
||||
//!
|
||||
|
@ -256,9 +218,8 @@
|
|||
|
||||
#![no_std]
|
||||
|
||||
#![cfg_attr(feature = "f16", feature(f16))]
|
||||
#![cfg_attr(feature = "f128", feature(f128))]
|
||||
#![cfg_attr(feature = "never-type", feature(never_type))]
|
||||
#![cfg_attr(feature = "f128", feature(f128))]
|
||||
#![cfg_attr(feature = "f16", feature(f16))]
|
||||
|
||||
#![cfg_attr(doc, feature(doc_cfg, rustdoc_internals))]
|
||||
|
||||
|
@ -282,8 +243,6 @@ macro_rules! use_mod {
|
|||
}
|
||||
pub(crate) use use_mod;
|
||||
|
||||
use_mod!(pub primitive_discriminant);
|
||||
|
||||
pub mod decode;
|
||||
pub mod encode;
|
||||
pub mod error;
|
||||
|
@ -292,3 +251,6 @@ pub mod vec;
|
|||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod slot;
|
||||
|
||||
use_mod!(pub prim_discriminant);
|
||||
use_mod!(pub prim_repr);
|
||||
|
|
84
oct/src/prim_discriminant/mod.rs
Normal file
84
oct/src/prim_discriminant/mod.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
// Copyright 2024-2025 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of
|
||||
// the Mozilla Public License, v. 2.0. If a copy of
|
||||
// the MPL was not distributed with this file, you
|
||||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use crate::PrimRepr;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
/// A generic-but-primitive discriminant.
|
||||
///
|
||||
/// This type represents all types supported by the `repr` attribute on enumerations.
|
||||
#[expect(missing_docs)]
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
||||
pub enum PrimDiscriminant {
|
||||
U8(u8),
|
||||
U16(u16),
|
||||
U32(u32),
|
||||
U64(u64),
|
||||
U128(u128),
|
||||
Usize(usize),
|
||||
|
||||
I8(i8),
|
||||
I16(i16),
|
||||
I32(i32),
|
||||
I64(i64),
|
||||
I128(i128),
|
||||
Isize(isize),
|
||||
}
|
||||
|
||||
impl<T: PrimRepr> From<T> for PrimDiscriminant {
|
||||
#[inline(always)]
|
||||
fn from(value: T) -> Self {
|
||||
value.into_prim_discriminant()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_fmt {
|
||||
($fmt:path) => {
|
||||
impl $fmt for ::oct::PrimDiscriminant {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
macro_rules! match_ty {
|
||||
($variant:ident => $ty:ty) => {
|
||||
if let Self::$variant(ref v) = *self {
|
||||
<$ty as $fmt>::fmt(v, f)?;
|
||||
::core::write!(f, ::core::stringify!($ty))?;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
match_ty!(U8 => u8);
|
||||
match_ty!(U16 => u16);
|
||||
match_ty!(U32 => u32);
|
||||
match_ty!(U64 => u64);
|
||||
match_ty!(U128 => u128);
|
||||
match_ty!(Usize => usize);
|
||||
|
||||
match_ty!(I8 => i8);
|
||||
match_ty!(I16 => i16);
|
||||
match_ty!(I32 => i32);
|
||||
match_ty!(I64 => i64);
|
||||
match_ty!(I128 => i128);
|
||||
match_ty!(Isize => isize);
|
||||
|
||||
::core::result::Result::Ok(())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_fmt!(core::fmt::Binary);
|
||||
impl_fmt!(core::fmt::Debug);
|
||||
impl_fmt!(core::fmt::Display);
|
||||
impl_fmt!(core::fmt::LowerExp);
|
||||
impl_fmt!(core::fmt::LowerHex);
|
||||
impl_fmt!(core::fmt::Octal);
|
||||
impl_fmt!(core::fmt::UpperExp);
|
||||
impl_fmt!(core::fmt::UpperHex);
|
39
oct/src/prim_discriminant/test.rs
Normal file
39
oct/src/prim_discriminant/test.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
// Copyright 2024-2025 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of
|
||||
// the Mozilla Public License, v. 2.0. If a copy of
|
||||
// the MPL was not distributed with this file, you
|
||||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use alloc::format;
|
||||
use oct::PrimDiscriminant;
|
||||
|
||||
#[test]
|
||||
fn test_primitive_discriminant_fmt() {
|
||||
let value = PrimDiscriminant::I128(0x45);
|
||||
|
||||
assert_eq!(format!("{value}"), "69i128");
|
||||
assert_eq!(format!("{value:+}"), "+69i128");
|
||||
assert_eq!(format!("{value:b}"), "1000101i128");
|
||||
assert_eq!(format!("{value:#b}"), "0b1000101i128");
|
||||
assert_eq!(format!("{value:o}"), "105i128");
|
||||
assert_eq!(format!("{value:#o}"), "0o105i128");
|
||||
assert_eq!(format!("{value:x}"), "45i128");
|
||||
assert_eq!(format!("{value:#x}"), "0x45i128");
|
||||
assert_eq!(format!("{value:X}"), "45i128");
|
||||
assert_eq!(format!("{value:#X}"), "0x45i128");
|
||||
|
||||
let value = PrimDiscriminant::I8(-0x45);
|
||||
|
||||
assert_eq!(format!("{value}"), "-69i8");
|
||||
assert_eq!(format!("{value:+}"), "-69i8");
|
||||
assert_eq!(format!("{value:b}"), "10111011i8");
|
||||
assert_eq!(format!("{value:#b}"), "0b10111011i8");
|
||||
assert_eq!(format!("{value:o}"), "273i8");
|
||||
assert_eq!(format!("{value:#o}"), "0o273i8");
|
||||
assert_eq!(format!("{value:x}"), "bbi8");
|
||||
assert_eq!(format!("{value:#x}"), "0xbbi8");
|
||||
assert_eq!(format!("{value:X}"), "BBi8");
|
||||
assert_eq!(format!("{value:#X}"), "0xBBi8");
|
||||
}
|
52
oct/src/prim_repr/mod.rs
Normal file
52
oct/src/prim_repr/mod.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
// Copyright 2024-2025 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of
|
||||
// the Mozilla Public License, v. 2.0. If a copy of
|
||||
// the MPL was not distributed with this file, you
|
||||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use crate::PrimDiscriminant;
|
||||
|
||||
mod sealed {
|
||||
/// Denotes a primitive enumeration representation.
|
||||
///
|
||||
/// See the public [`PrimRepr`](crate::PrimRepr) trait for more information.
|
||||
pub trait PrimRepr { }
|
||||
}
|
||||
|
||||
pub(crate) use sealed::PrimRepr as SealedPrimRepr;
|
||||
|
||||
/// Denotes a primitive enumeration representation.
|
||||
pub trait PrimRepr: Copy + SealedPrimRepr + Sized {
|
||||
/// Converts `self` into a [`PrimDiscriminant`] object.
|
||||
#[must_use]
|
||||
fn into_prim_discriminant(self) -> PrimDiscriminant;
|
||||
}
|
||||
|
||||
macro_rules! impl_prim_repr {
|
||||
($ty:ty => $variant:ident) => {
|
||||
impl ::oct::SealedPrimRepr for $ty { }
|
||||
|
||||
impl PrimRepr for $ty {
|
||||
#[inline(always)]
|
||||
fn into_prim_discriminant(self) -> ::oct::PrimDiscriminant {
|
||||
::oct::PrimDiscriminant::$variant(self)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_prim_repr!(u8 => U8);
|
||||
impl_prim_repr!(u16 => U16);
|
||||
impl_prim_repr!(u32 => U32);
|
||||
impl_prim_repr!(u64 => U64);
|
||||
impl_prim_repr!(u128 => U128);
|
||||
impl_prim_repr!(usize => Usize);
|
||||
|
||||
impl_prim_repr!(i8 => I8);
|
||||
impl_prim_repr!(i16 => I16);
|
||||
impl_prim_repr!(i32 => I32);
|
||||
impl_prim_repr!(i64 => I64);
|
||||
impl_prim_repr!(i128 => I128);
|
||||
impl_prim_repr!(isize => Isize);
|
|
@ -1,56 +0,0 @@
|
|||
// Copyright 2024-2025 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of
|
||||
// the Mozilla Public License, v. 2.0. If a copy of
|
||||
// the MPL was not distributed with this file, you
|
||||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
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 trait 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 the tracking issue for [`repr128`](https://github.com/rust-lang/rust/issues/56071/)), although this trait is implemented for these two types anyhow.
|
||||
pub trait PrimitiveDiscriminant: Copy + SealedPrimitiveDiscriminant + Sized { }
|
||||
|
||||
macro_rules! impl_primitive_discriminant {
|
||||
($ty:ty) => {
|
||||
impl ::oct::SealedPrimitiveDiscriminant for $ty {
|
||||
#[allow(clippy::cast_lossless)]
|
||||
#[inline(always)]
|
||||
fn to_u128(self) -> u128 {
|
||||
self as u128
|
||||
}
|
||||
}
|
||||
|
||||
impl ::oct::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);
|
|
@ -7,7 +7,7 @@
|
|||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod test;
|
||||
|
||||
use crate::decode::{Decode, Input};
|
||||
use crate::encode::{Encode, Output, SizedEncode};
|
||||
|
@ -93,7 +93,7 @@ impl<T> Slot<T> {
|
|||
let buf = {
|
||||
let buf = ptr::slice_from_raw_parts_mut(ptr, cap);
|
||||
|
||||
Box::from_raw(buf)
|
||||
unsafe { Box::from_raw(buf) }
|
||||
};
|
||||
|
||||
Self {
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
// Copyright 2024-2025 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of
|
||||
// the Mozilla Public License, v. 2.0. If a copy of
|
||||
// the MPL was not distributed with this file, you
|
||||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use crate::string::String;
|
||||
|
||||
use core::cmp::Ordering;
|
||||
|
||||
impl<const N: usize> Ord for String<N> {
|
||||
#[inline(always)]
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.as_str().cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize, const M: usize> PartialEq<String<M>> for String<N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &String<M>) -> bool {
|
||||
self.as_str() == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> PartialEq<&str> for String<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<alloc::string::String> for String<N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &alloc::string::String) -> bool {
|
||||
self.as_str() == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize, const M: usize> PartialOrd<String<M>> for String<N> {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &String<M>) -> Option<Ordering> {
|
||||
self.as_str().partial_cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> PartialOrd<&str> for String<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<alloc::string::String> for String<N> {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &alloc::string::String) -> Option<Ordering> {
|
||||
self.as_str().partial_cmp(other.as_str())
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
// Copyright 2024-2025 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of
|
||||
// the Mozilla Public License, v. 2.0. If a copy of
|
||||
// the MPL was not distributed with this file, you
|
||||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use crate::string::String;
|
||||
use crate::decode::{Decode, DecodeBorrowed, Input};
|
||||
use crate::encode::{Encode, Output, SizedEncode};
|
||||
use crate::error::{CollectionDecodeError, LengthError, StringError, Utf8Error};
|
||||
|
||||
impl<const N: usize> Decode for String<N> {
|
||||
type Error = CollectionDecodeError<LengthError, Utf8Error>;
|
||||
|
||||
#[inline]
|
||||
fn decode(input: &mut Input) -> Result<Self, Self::Error> {
|
||||
let len = Decode::decode(input).unwrap();
|
||||
|
||||
let data = input.read(len).unwrap();
|
||||
|
||||
Self::from_utf8(data)
|
||||
.map_err(|e| match e {
|
||||
StringError::BadUtf8(e) => CollectionDecodeError::BadItem(e),
|
||||
|
||||
StringError::SmallBuffer(e) => CollectionDecodeError::BadLength(e),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> DecodeBorrowed<str> for String<N> { }
|
||||
|
||||
impl<const N: usize> Encode for String<N> {
|
||||
type Error = <str as Encode>::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, output: &mut Output) -> Result<(), Self::Error> {
|
||||
self.as_str().encode(output)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> SizedEncode for String<N> {
|
||||
const MAX_ENCODED_SIZE: usize =
|
||||
usize::MAX_ENCODED_SIZE
|
||||
+ u8::MAX_ENCODED_SIZE * N;
|
||||
}
|
|
@ -1,359 +0,0 @@
|
|||
// Copyright 2024-2025 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of
|
||||
// the Mozilla Public License, v. 2.0. If a copy of
|
||||
// the MPL was not distributed with this file, you
|
||||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use crate::error::{LengthError, StringError, Utf8Error};
|
||||
use crate::vec::Vec;
|
||||
use crate::string::String;
|
||||
|
||||
use core::borrow::{Borrow, BorrowMut};
|
||||
use core::mem::{ManuallyDrop, MaybeUninit};
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::ptr::copy_nonoverlapping;
|
||||
use core::slice;
|
||||
use core::str::{self, FromStr};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::boxed::Box;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use {
|
||||
std::ffi::OsStr,
|
||||
std::net::ToSocketAddrs,
|
||||
std::path::Path,
|
||||
};
|
||||
|
||||
impl<const N: usize> String<N> {
|
||||
/// Constructs a fixed-size string from raw parts.
|
||||
///
|
||||
/// The provided parts are not tested in any way.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The value of `len` may not exceed that of `N`.
|
||||
/// Additionally, the octets in `buf` (from index zero up to the value of `len`) must be valid UTF-8 codepoints.
|
||||
///
|
||||
/// If any of these requirements are violated, behaviour is undefined.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
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 buf = unsafe { buf.as_ptr().cast::<[MaybeUninit<u8>; N]>().read() };
|
||||
|
||||
Self(Vec::from_raw_parts(buf, len))
|
||||
}
|
||||
|
||||
/// Constructs a new string from UTF-8 octets.
|
||||
///
|
||||
/// The passed slice is checked for its validity.
|
||||
/// For a similar function *without* these checks, see [`from_utf8_unchecked`](Self::from_utf8_unchecked).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Each byte value must be a valid UTF-8 code point.
|
||||
#[inline]
|
||||
pub const fn from_utf8(s: &[u8]) -> Result<Self, StringError> {
|
||||
if s.len() > N {
|
||||
return Err(StringError::SmallBuffer(LengthError {
|
||||
remaining: N,
|
||||
count: s.len(),
|
||||
}));
|
||||
}
|
||||
|
||||
let s = match str::from_utf8(s) {
|
||||
Ok(s) => s,
|
||||
|
||||
Err(e) => {
|
||||
let i = e.valid_up_to();
|
||||
let c = s[i];
|
||||
|
||||
return Err(StringError::BadUtf8(Utf8Error { value: c, index: i }));
|
||||
}
|
||||
};
|
||||
|
||||
// SAFETY: `s` has been tested to only contain
|
||||
// valid octets.
|
||||
let this = unsafe { Self::from_utf8_unchecked(s.as_bytes()) };
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
/// Unsafely constructs a new string from UTF-8 octets.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Each byte value must be a valid UTF-8 code point.
|
||||
/// The behaviour of a programme that passes invalid values to this function is undefined.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const unsafe fn from_utf8_unchecked(s: &[u8]) -> Self {
|
||||
debug_assert!(s.len() <= N, "cannot construct string from utf-8 sequence that is longer");
|
||||
|
||||
let mut buf = [0x00; N];
|
||||
copy_nonoverlapping(s.as_ptr(), buf.as_mut_ptr(), s.len());
|
||||
|
||||
// SAFETY: `s` is guaranteed by the caller to only
|
||||
// contain valid octets. It has also been tested to
|
||||
// not exceed bounds.
|
||||
Self::from_raw_parts(buf, s.len())
|
||||
}
|
||||
|
||||
/// Gets a pointer to the first octet.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn as_ptr(&self) -> *const u8 {
|
||||
self.0.as_ptr()
|
||||
}
|
||||
|
||||
// This function can only be marked as `const` when
|
||||
// `const_mut_refs` is implemented. See tracking
|
||||
// issue #57349 for more information.
|
||||
/// Gets a mutable pointer to the first octet.
|
||||
///
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn as_mut_ptr(&mut self) -> *mut u8 {
|
||||
self.0.as_mut_ptr()
|
||||
}
|
||||
|
||||
/// Borrows the string as a byte slice.
|
||||
///
|
||||
/// The range of the returned slice only includes characters that are "used."
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn as_bytes(&self) -> &[u8] {
|
||||
// We need to use `from_raw_parts` to mark this
|
||||
// function `const`.
|
||||
|
||||
let ptr = self.as_ptr();
|
||||
let len = self.len();
|
||||
|
||||
unsafe { slice::from_raw_parts(ptr, len) }
|
||||
}
|
||||
|
||||
/// Borrows the string as a string slice.
|
||||
///
|
||||
/// The range of the returned slice only includes characters that are "used."
|
||||
#[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()) }
|
||||
}
|
||||
|
||||
/// Mutably borrows the string as a string slice.
|
||||
///
|
||||
/// The range of the returned slice only includes characters that are "used."
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn as_mut_str(&mut self) -> &mut str {
|
||||
// TODO: Mark with `const`.
|
||||
|
||||
unsafe {
|
||||
let ptr = self.as_mut_ptr();
|
||||
let len = self.len();
|
||||
|
||||
let data = slice::from_raw_parts_mut(ptr, len);
|
||||
core::str::from_utf8_unchecked_mut(data)
|
||||
}
|
||||
}
|
||||
|
||||
/// Destructs the provided string into its raw parts.
|
||||
///
|
||||
/// The returned parts are valid to pass back to [`from_raw_parts`](Self::from_raw_parts).
|
||||
///
|
||||
/// The returned byte array is guaranteed to be fully initialised.
|
||||
/// However, only octets up to an index of [`len`](Self::len) are also guaranteed to be valid UTF-8 codepoints.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn into_raw_parts(self) -> ([u8; N], usize) {
|
||||
let (buf, len) = self.into_bytes().into_raw_parts();
|
||||
|
||||
let init_buf = ManuallyDrop::new(buf);
|
||||
let buf = unsafe { (&raw const init_buf).cast::<[u8; N]>().read() };
|
||||
|
||||
(buf, len)
|
||||
}
|
||||
|
||||
/// Converts the string into a vector of bytes.
|
||||
///
|
||||
/// The underlying memory of the string is completely reused.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn into_bytes(self) -> Vec<u8, N> {
|
||||
let this = ManuallyDrop::new(self);
|
||||
|
||||
// SAFETY: `ManuallyDrop<T>` is transparent to `T`.
|
||||
// We also aren't dropping `this`, so we can safely
|
||||
// move out of it.
|
||||
unsafe { (&raw const this).cast::<Vec<u8, N>>().read() }
|
||||
|
||||
}
|
||||
|
||||
/// Converts the fixed-size string into a boxed string slice.
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn into_boxed_str(self) -> Box<str> {
|
||||
let Self(v) = self;
|
||||
unsafe { alloc::str::from_boxed_utf8_unchecked(v.into_boxed_slice()) }
|
||||
}
|
||||
|
||||
/// Converts the fixed-size string into a dynamic string.
|
||||
///
|
||||
/// The capacity of the resulting [`alloc::string::String`] object is equal to the value of `N`.
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn into_string(self) -> alloc::string::String {
|
||||
self.into_boxed_str().into_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> AsMut<str> for String<N> {
|
||||
#[inline(always)]
|
||||
fn as_mut(&mut self) -> &mut str {
|
||||
self.as_mut_str()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl<const N: usize> AsRef<OsStr> for String<N> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &OsStr {
|
||||
self.as_str().as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl<const N: usize> AsRef<Path> for String<N> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &Path {
|
||||
self.as_str().as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> AsRef<str> for String<N> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> AsRef<[u8]> for String<N> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Borrow<str> for String<N> {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> BorrowMut<str> for String<N> {
|
||||
#[inline(always)]
|
||||
fn borrow_mut(&mut self) -> &mut str {
|
||||
self.as_mut_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Deref for String<N> {
|
||||
type Target = str;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> DerefMut for String<N> {
|
||||
#[inline(always)]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.as_mut_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> FromStr for String<N> {
|
||||
type Err = LengthError;
|
||||
|
||||
#[inline]
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Self::new(s)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl<const N: usize> ToSocketAddrs for String<N> {
|
||||
type Iter = <str as ToSocketAddrs>::Iter;
|
||||
|
||||
#[inline(always)]
|
||||
fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
|
||||
self.as_str().to_socket_addrs()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> TryFrom<char> for String<N> {
|
||||
type Error = <Self as FromStr>::Err;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(value: char) -> Result<Self, Self::Error> {
|
||||
let mut buf = [0x00; 0x4];
|
||||
let s = value.encode_utf8(&mut buf);
|
||||
|
||||
s.parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> TryFrom<&str> for String<N> {
|
||||
type Error = <Self as FromStr>::Err;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
Self::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<const N: usize> TryFrom<alloc::string::String> for String<N> {
|
||||
type Error = <Self as FromStr>::Err;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(value: alloc::string::String) -> Result<Self, Self::Error> {
|
||||
Self::new(&value)
|
||||
}
|
||||
}
|
||||
|
||||
/// See [`into_boxed_str`](String::into_boxed_str).
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<const N: usize> From<String<N>> for Box<str> {
|
||||
#[inline(always)]
|
||||
fn from(value: String<N>) -> Self {
|
||||
value.into_boxed_str()
|
||||
}
|
||||
}
|
||||
|
||||
/// See [`into_string`](String::into_string).
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<const N: usize> From<String<N>> for alloc::string::String {
|
||||
#[inline(always)]
|
||||
fn from(value: String<N>) -> Self {
|
||||
value.into_string()
|
||||
}
|
||||
}
|
|
@ -7,33 +7,41 @@
|
|||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod test;
|
||||
|
||||
use crate::error::LengthError;
|
||||
use crate::decode::{self, Decode, DecodeBorrowed};
|
||||
use crate::encode::{self, Encode, SizedEncode};
|
||||
use crate::error::{CollectionDecodeError, LengthError, Utf8Error};
|
||||
use crate::vec::Vec;
|
||||
|
||||
use core::borrow::{Borrow, BorrowMut};
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt::{self, Debug, Display, Formatter};
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::ops::{Index, IndexMut};
|
||||
use core::slice::SliceIndex;
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ops::{Deref, DerefMut, Index, IndexMut};
|
||||
use core::ptr::{copy_nonoverlapping, write_bytes};
|
||||
use core::slice::{self, SliceIndex};
|
||||
use core::str::{self, FromStr};
|
||||
|
||||
// Comparison facilities:
|
||||
mod cmp;
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::boxed::Box;
|
||||
|
||||
// Encode/decode facilities:
|
||||
mod code;
|
||||
|
||||
// Conversion facilities:
|
||||
mod conv;
|
||||
#[cfg(feature = "std")]
|
||||
use {
|
||||
std::ffi::OsStr,
|
||||
std::net::ToSocketAddrs,
|
||||
std::path::Path,
|
||||
};
|
||||
|
||||
/// String container with maximum length.
|
||||
///
|
||||
/// This is in contrast to [`str`] and the standard library's [`String`](alloc::string::String) type -- both of which have no size limit in practice.
|
||||
/// This is in contrast to [`str`](prim@str) and the standard library's [`String`](alloc::string::String) type -- both of which have no size limit in practice.
|
||||
///
|
||||
/// 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` then denotes *octets* (or "bytes") and **not** *characters* -- i.e. a value of `8` may translate to anywhere between two and eight characters due to variable-length encoding.
|
||||
///
|
||||
/// See [`Vec`] for an equivalent alternative to [`alloc::vec::Vec`].
|
||||
/// See [`Vec`] for an equivalent alternative to the standard library's [`Vec`](alloc::vec::Vec).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -44,66 +52,381 @@ mod conv;
|
|||
/// use oct::string::String;
|
||||
/// use std::str::FromStr;
|
||||
///
|
||||
/// let str0 = String::<0x40>::default(); // Empty string.
|
||||
/// let str1 = String::<0x40>::from_str("Hello there!").unwrap();
|
||||
/// let str2 = String::<0x40>::from_str("أنا من أوروپا").unwrap();
|
||||
/// let str3 = String::<0x40>::from_str("COGITO ERGO SUM").unwrap();
|
||||
/// let s0 = String::<0x40>::default(); // Empty string.
|
||||
/// let s1 = String::<0x40>::from_str("Hello there!").unwrap();
|
||||
/// let s2 = String::<0x40>::from_str("أنا من أوروپا").unwrap();
|
||||
/// let s3 = String::<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));
|
||||
/// assert_eq!(size_of_val(&s0), size_of_val(&s1));
|
||||
/// assert_eq!(size_of_val(&s0), size_of_val(&s2));
|
||||
/// assert_eq!(size_of_val(&s0), size_of_val(&s3));
|
||||
/// assert_eq!(size_of_val(&s1), size_of_val(&s2));
|
||||
/// assert_eq!(size_of_val(&s1), size_of_val(&s3));
|
||||
/// assert_eq!(size_of_val(&s2), size_of_val(&s3));
|
||||
/// ```
|
||||
#[derive(Clone, Default, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct String<const N: usize>(Vec<u8, N>);
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct String<const N: usize> {
|
||||
len: usize,
|
||||
buf: [u8; N],
|
||||
}
|
||||
|
||||
impl<const N: usize> String<N> {
|
||||
/// Constructs a new, fixed-size string.
|
||||
/// Constructs a new, size-constrained string.
|
||||
///
|
||||
/// Note that string is not required to completely fill out its size-constraint.
|
||||
/// The provided string `s` is checked to be containable within `N` bytes.
|
||||
/// See also [`new_unchecked`](Self::new_unchecked).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If the internal buffer cannot contain the entirety of `s`, then an error is returned.
|
||||
#[inline]
|
||||
pub const fn new(s: &str) -> Result<Self, LengthError> {
|
||||
if s.len() > N {
|
||||
let len = s.len();
|
||||
if len > N {
|
||||
return Err(LengthError {
|
||||
remaining: N,
|
||||
count: s.len(),
|
||||
count: len,
|
||||
});
|
||||
}
|
||||
|
||||
let this = unsafe { Self::from_utf8_unchecked(s.as_bytes()) };
|
||||
let mut buf = [0x00; N];
|
||||
|
||||
unsafe {
|
||||
let src = s.as_ptr();
|
||||
let dst = buf.as_mut_ptr().cast();
|
||||
|
||||
copy_nonoverlapping(src, dst, len);
|
||||
}
|
||||
|
||||
// SAFETY: `str` can be assumed to only contain
|
||||
// valid UTF-8 data.
|
||||
let this = unsafe { Self::from_raw_parts(buf, len) };
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
/// Returns the length of the string.
|
||||
/// Unsafely constructs a new, size-constrained string.
|
||||
///
|
||||
/// This does not necessarily equate to the value of `N`, as the internal buffer may be used but partially.
|
||||
/// See also [`new`](Self::new) for a safe alternative to this constructor.
|
||||
///
|
||||
/// Also remember that the returned value only denotes the octet count and not characters, graphemes, etc.
|
||||
/// # Safety
|
||||
///
|
||||
/// If the internal buffer cannot contain the entirety of `s`, then the call to this constructor will result in undefined behaviour.
|
||||
#[inline]
|
||||
pub const unsafe fn new_unchecked(s: &str) -> Self {
|
||||
let len = s.len();
|
||||
let mut buf = [0x00; N];
|
||||
|
||||
debug_assert!(len <= N, "cannot construct string from string slice that is longer");
|
||||
|
||||
unsafe {
|
||||
let src = s.as_ptr();
|
||||
let dst = buf.as_mut_ptr().cast();
|
||||
|
||||
copy_nonoverlapping(src, dst, len);
|
||||
}
|
||||
|
||||
// SAFETY: `str` is guaranteed by the caller to be
|
||||
// valid.
|
||||
unsafe { Self::from_raw_parts(buf, len) }
|
||||
}
|
||||
|
||||
/// Constructs a new string from UTF-8 octets.
|
||||
///
|
||||
/// The passed slice is checked for its validity.
|
||||
/// For a similar function *without* these checks, see [`from_utf8_unchecked`](Self::from_utf8_unchecked).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Each byte value must be a valid UTF-8 code point.
|
||||
/// If an invalid sequence is found, then this function will return an error.
|
||||
#[inline]
|
||||
pub const fn from_utf8(v: Vec<u8, N>) -> Result<Self, (Utf8Error, Vec<u8, N>)> {
|
||||
if let Err(e) = str::from_utf8(v.as_slice()) {
|
||||
let i = e.valid_up_to();
|
||||
|
||||
let c = unsafe { *v.as_ptr().add(i) };
|
||||
|
||||
return Err((Utf8Error { value: c, index: i }, v));
|
||||
}
|
||||
|
||||
// SAFETY: `s` has been tested to only contain
|
||||
// valid octets.
|
||||
let this = unsafe { Self::from_utf8_unchecked(v) };
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
/// Unsafely constructs a new string from UTF-8 octets.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Each byte value of the vector must be a valid UTF-8 code point.
|
||||
/// The behaviour of a programme that passes invalid values to this function is undefined.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const unsafe fn from_utf8_unchecked(v: Vec<u8, N>) -> Self {
|
||||
let (mut buf, len) = v.into_raw_parts();
|
||||
|
||||
// Zero-initialise bytes that may be uninitialised.
|
||||
unsafe {
|
||||
let dst = buf.as_mut_ptr().add(len);
|
||||
let count = N - len;
|
||||
|
||||
write_bytes(dst, 0x00, count);
|
||||
}
|
||||
|
||||
// SAFETY: We can safely transmute here as
|
||||
// `MaybeUninit<u8>` is transparent to `u8` and
|
||||
// we have initialised the remaining bytes.
|
||||
let buf = unsafe { buf.as_ptr().cast::<[u8; N]>().read() };
|
||||
|
||||
// SAFETY: `Vec::into_raw_parts` guarantees that
|
||||
// the returned length is not greater than `N`.
|
||||
unsafe { Self::from_raw_parts(buf, len) }
|
||||
}
|
||||
|
||||
/// Constructs a size-constrained string from raw parts.
|
||||
///
|
||||
/// The provided parts are not tested in any way.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The value of `len` may not exceed that of `N`.
|
||||
/// Additionally, the octets in `buf` (from index zero up to the value of `len`) must be valid UTF-8 codepoints.
|
||||
///
|
||||
/// If any of these requirements are violated, behaviour is undefined.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
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");
|
||||
|
||||
Self { len, buf }
|
||||
}
|
||||
|
||||
/// Returns the current length of the string.
|
||||
///
|
||||
/// Remember that this value only denotes the octet count and **not** the amount of characters, graphemes, etc.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
self.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()
|
||||
self.len() == 0x0
|
||||
}
|
||||
|
||||
/// Checks if the string is full, i.e. it cannot hold any more characters.
|
||||
/// Checks if specified index is on the boundary of a character.
|
||||
///
|
||||
/// In this case, character is defined as a set of one to four UTF-8 octets that represent a Unicode code point (specifically a Unicode scalar).
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn is_full(&self) -> bool {
|
||||
self.0.is_full()
|
||||
pub fn is_char_boundary(&self, index: usize) -> bool {
|
||||
// TODO: Mark with `const`.
|
||||
|
||||
self.as_str().is_char_boundary(index)
|
||||
}
|
||||
|
||||
/// Gets a pointer to the first octet.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn as_ptr(&self) -> *const u8 {
|
||||
self.buf.as_ptr()
|
||||
}
|
||||
|
||||
/// Gets a mutable pointer to the first octet.
|
||||
///
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn as_mut_ptr(&mut self) -> *mut u8 {
|
||||
self.buf.as_mut_ptr()
|
||||
}
|
||||
|
||||
/// Borrows the string as a byte slice.
|
||||
///
|
||||
/// The range of the returned slice only includes characters that are "used."
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn as_bytes(&self) -> &[u8] {
|
||||
// We need to use `from_raw_parts` to mark this
|
||||
// function `const`.
|
||||
|
||||
let ptr = self.as_ptr();
|
||||
let len = self.len();
|
||||
|
||||
unsafe { slice::from_raw_parts(ptr, len) }
|
||||
}
|
||||
|
||||
/// Borrows the string as a mutable byte slice.
|
||||
///
|
||||
/// The range of the returned slice only includes characters that are "used."
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Writes to the returned slice must not contain invalid UTF-8 octets.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const unsafe fn as_mut_bytes(&mut self) -> &mut [u8] {
|
||||
let ptr = self.as_mut_ptr();
|
||||
let len = self.len();
|
||||
|
||||
unsafe { slice::from_raw_parts_mut(ptr, len) }
|
||||
}
|
||||
|
||||
/// Borrows the string as a string slice.
|
||||
///
|
||||
/// The range of the returned slice only includes characters that are "used."
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn as_str(&self) -> &str {
|
||||
// SAFETY: We guarantee that all octets are always
|
||||
// valid UTF-8 code points.
|
||||
unsafe { core::str::from_utf8_unchecked(self.as_bytes()) }
|
||||
}
|
||||
|
||||
/// Mutably borrows the string as a string slice.
|
||||
///
|
||||
/// The range of the returned slice only includes characters that are "used."
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn as_mut_str(&mut self) -> &mut str {
|
||||
unsafe {
|
||||
let ptr = self.as_mut_ptr();
|
||||
let len = self.len();
|
||||
|
||||
let data = slice::from_raw_parts_mut(ptr, len);
|
||||
core::str::from_utf8_unchecked_mut(data)
|
||||
}
|
||||
}
|
||||
|
||||
/// Destructs the provided string into its raw parts.
|
||||
///
|
||||
/// The returned parts are valid to pass back to [`from_raw_parts`](Self::from_raw_parts).
|
||||
///
|
||||
/// The returned byte array is guaranteed to be fully initialised.
|
||||
/// However, only octets up to an index of [`len`](Self::len) are also guaranteed to be valid UTF-8 codepoints.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn into_raw_parts(self) -> ([u8; N], usize) {
|
||||
let Self { buf, len } = self;
|
||||
(buf, len)
|
||||
}
|
||||
|
||||
/// Converts the string into a vector of bytes.
|
||||
///
|
||||
/// The underlying memory of the string is completely reused.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn into_bytes(self) -> Vec<u8, N> {
|
||||
let (buf, len) = self.into_raw_parts();
|
||||
|
||||
// SAFETY: `MaybeUninit<u8>` is transparent to `u8`.
|
||||
let buf = unsafe { (&raw const buf).cast::<[MaybeUninit<u8>; N]>().read() };
|
||||
|
||||
unsafe { Vec::from_raw_parts(buf, len) }
|
||||
|
||||
}
|
||||
|
||||
/// Converts the size-constrained string into a boxed string slice.
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn into_boxed_str(self) -> Box<str> {
|
||||
let v = self.into_bytes();
|
||||
unsafe { alloc::str::from_boxed_utf8_unchecked(v.into_boxed_slice()) }
|
||||
}
|
||||
|
||||
/// Converts the size-constrained string into a dynamic string.
|
||||
///
|
||||
/// The capacity of the resulting [`alloc::string::String`] object is equal to the value of `N`.
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn into_string(self) -> alloc::string::String {
|
||||
self.into_boxed_str().into_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> AsMut<str> for String<N> {
|
||||
#[inline(always)]
|
||||
fn as_mut(&mut self) -> &mut str {
|
||||
self.as_mut_str()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl<const N: usize> AsRef<OsStr> for String<N> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &OsStr {
|
||||
self.as_str().as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl<const N: usize> AsRef<Path> for String<N> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &Path {
|
||||
self.as_str().as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> AsRef<str> for String<N> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> AsRef<[u8]> for String<N> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Borrow<str> for String<N> {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> BorrowMut<str> for String<N> {
|
||||
#[inline(always)]
|
||||
fn borrow_mut(&mut self) -> &mut str {
|
||||
self.as_mut_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Deref for String<N> {
|
||||
type Target = str;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Default for String<N> {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
let buf = [Default::default(); N];
|
||||
let len = 0x0;
|
||||
|
||||
unsafe { Self::from_raw_parts(buf, len) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> DerefMut for String<N> {
|
||||
#[inline(always)]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.as_mut_str()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,6 +437,24 @@ impl<const N: usize> Debug for String<N> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Decode for String<N> {
|
||||
type Error = CollectionDecodeError<LengthError, Utf8Error>;
|
||||
|
||||
#[inline]
|
||||
fn decode(input: &mut decode::Input) -> Result<Self, Self::Error> {
|
||||
let v = Vec::<u8, N>::decode(input)
|
||||
.map_err(|e| {
|
||||
let CollectionDecodeError::BadLength(e) = e;
|
||||
CollectionDecodeError::BadLength(e)
|
||||
})?;
|
||||
|
||||
Self::from_utf8(v)
|
||||
.map_err(|(e, ..)| CollectionDecodeError::BadItem(e))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> DecodeBorrowed<str> for String<N> { }
|
||||
|
||||
impl<const N: usize> Display for String<N> {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
|
@ -121,6 +462,17 @@ impl<const N: usize> Display for String<N> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Encode for String<N> {
|
||||
type Error = <str as Encode>::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, output: &mut encode::Output) -> Result<(), Self::Error> {
|
||||
self.as_str().encode(output)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Eq for String<N> { }
|
||||
|
||||
impl<const N: usize> FromIterator<char> for String<N> {
|
||||
#[inline]
|
||||
fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> Self {
|
||||
|
@ -147,6 +499,15 @@ impl<const N: usize> FromIterator<char> for String<N> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> FromStr for String<N> {
|
||||
type Err = LengthError;
|
||||
|
||||
#[inline]
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Self::new(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Hash for String<N> {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
|
@ -169,3 +530,125 @@ impl<I: SliceIndex<str>, const N: usize> IndexMut<I> for String<N> {
|
|||
self.get_mut(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Ord for String<N> {
|
||||
#[inline(always)]
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.as_str().cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize, const M: usize> PartialEq<String<M>> for String<N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &String<M>) -> bool {
|
||||
self.as_str() == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> PartialEq<str> for String<N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.as_str() == other
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> PartialEq<&str> for String<N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &&str) -> bool {
|
||||
self == *other
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<const N: usize> PartialEq<alloc::string::String> for String<N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &alloc::string::String) -> bool {
|
||||
self.as_str() == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize, const M: usize> PartialOrd<String<M>> for String<N> {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &String<M>) -> Option<Ordering> {
|
||||
self.as_str().partial_cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> SizedEncode for String<N> {
|
||||
const MAX_ENCODED_SIZE: usize =
|
||||
usize::MAX_ENCODED_SIZE
|
||||
+ u8::MAX_ENCODED_SIZE * N;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||
impl<const N: usize> ToSocketAddrs for String<N> {
|
||||
type Iter = <str as ToSocketAddrs>::Iter;
|
||||
|
||||
#[inline(always)]
|
||||
fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
|
||||
self.as_str().to_socket_addrs()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> TryFrom<char> for String<N> {
|
||||
type Error = <Self as FromStr>::Err;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(value: char) -> Result<Self, Self::Error> {
|
||||
let mut buf = [0x00; 0x4];
|
||||
let s = value.encode_utf8(&mut buf);
|
||||
|
||||
s.parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> TryFrom<&str> for String<N> {
|
||||
type Error = <Self as FromStr>::Err;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
Self::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<const N: usize> TryFrom<alloc::string::String> for String<N> {
|
||||
type Error = <Self as FromStr>::Err;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(value: alloc::string::String) -> Result<Self, Self::Error> {
|
||||
Self::new(&value)
|
||||
}
|
||||
}
|
||||
|
||||
/// See [`into_boxed_str`](String::into_boxed_str).
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<const N: usize> From<String<N>> for Box<str> {
|
||||
#[inline(always)]
|
||||
fn from(value: String<N>) -> Self {
|
||||
value.into_boxed_str()
|
||||
}
|
||||
}
|
||||
|
||||
/// See [`into_string`](String::into_string).
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<const N: usize> From<String<N>> for alloc::string::String {
|
||||
#[inline(always)]
|
||||
fn from(value: String<N>) -> Self {
|
||||
value.into_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<const N: usize> PartialEq<String<N>> for alloc::string::String {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &String<N>) -> bool {
|
||||
self.as_str() == other.as_str()
|
||||
}
|
||||
}
|
||||
|
|
83
oct/src/string/string/test.rs
Normal file
83
oct/src/string/string/test.rs
Normal file
|
@ -0,0 +1,83 @@
|
|||
// Copyright 2024-2025 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of
|
||||
// the Mozilla Public License, v. 2.0. If a copy of
|
||||
// the MPL was not distributed with this file, you
|
||||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use core::cmp::Ordering;
|
||||
use oct::error::Utf8Error;
|
||||
use oct::string::String;
|
||||
use oct::vec::Vec;
|
||||
|
||||
#[test]
|
||||
fn test_string() {
|
||||
let s: String::<0x4> = "hello world".chars().collect();
|
||||
assert_eq!(s, "hell")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_size() {
|
||||
let s0 = String::<0x0C>::new("Hello there!").unwrap();
|
||||
let s1 = String::<0x12>::new("MEIN_GRO\u{1E9E}_GOTT").unwrap();
|
||||
let s2 = String::<0x05>::new("Hello").unwrap();
|
||||
|
||||
assert_eq!(s0.partial_cmp(&s0), Some(Ordering::Equal));
|
||||
assert_eq!(s0.partial_cmp(&s1), Some(Ordering::Less));
|
||||
assert_eq!(s0.partial_cmp(&s2), Some(Ordering::Greater));
|
||||
|
||||
assert_eq!(s1.partial_cmp(&s0), Some(Ordering::Greater));
|
||||
assert_eq!(s1.partial_cmp(&s1), Some(Ordering::Equal));
|
||||
assert_eq!(s1.partial_cmp(&s2), Some(Ordering::Greater));
|
||||
|
||||
assert_eq!(s2.partial_cmp(&s0), Some(Ordering::Less));
|
||||
assert_eq!(s2.partial_cmp(&s1), Some(Ordering::Less));
|
||||
assert_eq!(s2.partial_cmp(&s2), Some(Ordering::Equal));
|
||||
|
||||
assert_eq!(s0, "Hello there!");
|
||||
assert_eq!(s1, "MEIN_GRO\u{1E9E}_GOTT");
|
||||
assert_eq!(s2, "Hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_from_utf8() {
|
||||
macro_rules! test_utf8 {
|
||||
{
|
||||
len: $len:literal,
|
||||
utf8: $utf8:expr,
|
||||
result: $result:pat$(,)?
|
||||
} => {{
|
||||
let utf8 = core::borrow::Borrow::<[u8]>::borrow($utf8);
|
||||
|
||||
assert!(matches!(
|
||||
String::<$len>::from_utf8(Vec::new(utf8).unwrap()),
|
||||
$result,
|
||||
));
|
||||
}};
|
||||
}
|
||||
|
||||
test_utf8!(
|
||||
len: 0x3,
|
||||
utf8: b"A\xF7c",
|
||||
result: Err((Utf8Error { value: 0xF7, index: 0x1 }, ..)),
|
||||
);
|
||||
|
||||
test_utf8!(
|
||||
len: 0x4,
|
||||
utf8: b"A\xC3\xBCc",
|
||||
result: Ok(..),
|
||||
);
|
||||
|
||||
test_utf8!(
|
||||
len: 0x4,
|
||||
utf8: b"20\x20\xAC",
|
||||
result: Err((Utf8Error { value: 0xAC, index: 0x3 }, ..)),
|
||||
);
|
||||
|
||||
test_utf8!(
|
||||
len: 0x5,
|
||||
utf8: b"20\xE2\x82\xAC",
|
||||
result: Ok(..),
|
||||
);
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
// Copyright 2024-2025 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of
|
||||
// the Mozilla Public License, v. 2.0. If a copy of
|
||||
// the MPL was not distributed with this file, you
|
||||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use oct::error::{StringError, Utf8Error};
|
||||
use oct::string::String;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[test]
|
||||
fn test_string() {
|
||||
let s: String::<0x4> = "hello world".chars().collect();
|
||||
assert_eq!(s, "hell")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_size() {
|
||||
let string0 = String::<0x0C>::try_from("Hello there!").unwrap();
|
||||
let string1 = String::<0x12>::try_from("MEIN_GRO\u{1E9E}_GOTT").unwrap();
|
||||
let string2 = String::<0x05>::try_from("Hello").unwrap();
|
||||
|
||||
assert_eq!(string0.partial_cmp(&string0), Some(Ordering::Equal));
|
||||
assert_eq!(string0.partial_cmp(&string1), Some(Ordering::Less));
|
||||
assert_eq!(string0.partial_cmp(&string2), Some(Ordering::Greater));
|
||||
|
||||
assert_eq!(string1.partial_cmp(&string0), Some(Ordering::Greater));
|
||||
assert_eq!(string1.partial_cmp(&string1), Some(Ordering::Equal));
|
||||
assert_eq!(string1.partial_cmp(&string2), Some(Ordering::Greater));
|
||||
|
||||
assert_eq!(string2.partial_cmp(&string0), Some(Ordering::Less));
|
||||
assert_eq!(string2.partial_cmp(&string1), Some(Ordering::Less));
|
||||
assert_eq!(string2.partial_cmp(&string2), Some(Ordering::Equal));
|
||||
|
||||
assert_eq!(string0, "Hello there!");
|
||||
assert_eq!(string1, "MEIN_GRO\u{1E9E}_GOTT");
|
||||
assert_eq!(string2, "Hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_from_utf8() {
|
||||
macro_rules! test_utf8 {
|
||||
{
|
||||
len: $len:literal,
|
||||
utf8: $utf8:expr,
|
||||
result: $result:pat$(,)?
|
||||
} => {{
|
||||
let utf8: &[u8] = $utf8.as_ref();
|
||||
|
||||
assert!(matches!(
|
||||
String::<$len>::from_utf8(utf8),
|
||||
$result,
|
||||
));
|
||||
}};
|
||||
}
|
||||
|
||||
test_utf8!(
|
||||
len: 0x3,
|
||||
utf8: b"A\xF7c",
|
||||
result: Err(StringError::BadUtf8(Utf8Error { value: 0xF7, index: 0x1 })),
|
||||
);
|
||||
|
||||
test_utf8!(
|
||||
len: 0x4,
|
||||
utf8: "A\u{00F7}c",
|
||||
result: Ok(..),
|
||||
);
|
||||
|
||||
test_utf8!(
|
||||
len: 0x4,
|
||||
utf8: b"20\x20\xAC",
|
||||
result: Err(StringError::BadUtf8(Utf8Error { value: 0xAC, index: 0x3 })),
|
||||
);
|
||||
|
||||
test_utf8!(
|
||||
len: 0x5,
|
||||
utf8: "20\u{20AC}",
|
||||
result: Ok(..),
|
||||
);
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod test;
|
||||
|
||||
use core::iter::{DoubleEndedIterator, ExactSizeIterator, FusedIterator};
|
||||
use core::mem::MaybeUninit;
|
||||
|
@ -20,19 +20,19 @@ use core::slice;
|
|||
/// When just borrowing such vectors, the standard library's <code>core::slice::{[Iter](core::slice::Iter), [IterMut](core::slice::IterMut)}</code> types are used instead.
|
||||
#[must_use]
|
||||
pub struct IntoIter<T, const N: usize> {
|
||||
buf: [MaybeUninit<T>; N],
|
||||
|
||||
pos: usize,
|
||||
len: usize,
|
||||
pos: usize,
|
||||
|
||||
buf: [MaybeUninit<T>; N],
|
||||
}
|
||||
|
||||
impl<T, const N: usize> IntoIter<T, N> {
|
||||
/// Constructs a new, fixed-size iterator.
|
||||
/// Constructs a new, size-constrained iterator.
|
||||
#[inline(always)]
|
||||
pub(crate) const unsafe fn new(buf: [MaybeUninit<T>; N], len: usize) -> Self {
|
||||
debug_assert!(len <= N, "cannot construct iterator longer than its capacity");
|
||||
|
||||
Self { buf, pos: 0x0, len }
|
||||
Self { len, pos: 0x0, buf }
|
||||
}
|
||||
|
||||
/// Gets a slice of the remaining elements.
|
||||
|
@ -79,7 +79,7 @@ impl<T, const N: usize> AsRef<[T]> for IntoIter<T, N> {
|
|||
impl<T: Clone, const N: usize> Clone for IntoIter<T, N> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
let mut buf = [const { MaybeUninit::<T>::uninit() };N];
|
||||
let mut buf = [const { MaybeUninit::<T>::uninit() }; N];
|
||||
let Self { pos, len, .. } = *self;
|
||||
|
||||
let start = pos;
|
||||
|
@ -95,7 +95,7 @@ impl<T: Clone, const N: usize> Clone for IntoIter<T, N> {
|
|||
}
|
||||
}
|
||||
|
||||
Self { buf, pos, len }
|
||||
Self { len, pos, buf }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use core::str::FromStr;
|
||||
use oct::string::String;
|
||||
use oct::vec::Vec;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn test_vec_iter_clone() {
|
|
@ -1,80 +0,0 @@
|
|||
// Copyright 2024-2025 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of
|
||||
// the Mozilla Public License, v. 2.0. If a copy of
|
||||
// the MPL was not distributed with this file, you
|
||||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use crate::vec::Vec;
|
||||
|
||||
use core::cmp::Ordering;
|
||||
|
||||
impl<T: Eq, const N: usize> Eq for Vec<T, N> { }
|
||||
|
||||
impl<T: Ord, const N: usize> Ord for Vec<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<Vec<U, M>> for Vec<T, N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Vec<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 Vec<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 Vec<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<alloc::vec::Vec<U>> for Vec<T, N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &alloc::vec::Vec<U>) -> bool {
|
||||
self.as_slice() == other.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialOrd, const N: usize, const M: usize> PartialOrd<Vec<T, M>> for Vec<T, N> {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &Vec<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 Vec<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 Vec<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<alloc::vec::Vec<T>> for Vec<T, N> {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &alloc::vec::Vec<T>) -> Option<Ordering> {
|
||||
self.as_slice().partial_cmp(other.as_slice())
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
// Copyright 2024-2025 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of
|
||||
// the Mozilla Public License, v. 2.0. If a copy of
|
||||
// the MPL was not distributed with this file, you
|
||||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use crate::vec::Vec;
|
||||
use crate::decode::{Decode, DecodeBorrowed, Input};
|
||||
use crate::encode::{Encode, Output, SizedEncode};
|
||||
use crate::error::{CollectionDecodeError, ItemDecodeError, LengthError};
|
||||
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
impl<T: Decode, const N: usize> Decode for Vec<T, N> {
|
||||
type Error = CollectionDecodeError<LengthError, ItemDecodeError<usize, T::Error>>;
|
||||
|
||||
#[inline]
|
||||
fn decode(input: &mut Input) -> Result<Self, Self::Error> {
|
||||
let len = Decode::decode(input).unwrap();
|
||||
if len > N {
|
||||
return Err(CollectionDecodeError::BadLength(LengthError {
|
||||
remaining: N,
|
||||
count: len,
|
||||
}));
|
||||
}
|
||||
|
||||
let mut buf = [const { MaybeUninit::<T>::uninit() }; N];
|
||||
|
||||
for (i, slot) in buf[..len].iter_mut().enumerate() {
|
||||
let v = Decode::decode(input)
|
||||
.map_err(|e| CollectionDecodeError::BadItem(ItemDecodeError { index: i, error: e }))?;
|
||||
|
||||
slot.write(v);
|
||||
}
|
||||
|
||||
let this = unsafe { Self::from_raw_parts(buf, len) };
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decode, const N: usize> DecodeBorrowed<[T]> for Vec<T, N> { }
|
||||
|
||||
impl<T: Encode, const N: usize> Encode for Vec<T, N> {
|
||||
type Error = <[T] as Encode>::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, output: &mut Output) -> Result<(), Self::Error> {
|
||||
self.as_slice().encode(output)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SizedEncode, const N: usize> SizedEncode for Vec<T, N> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE * N;
|
||||
}
|
|
@ -1,226 +0,0 @@
|
|||
// Copyright 2024-2025 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of
|
||||
// the Mozilla Public License, v. 2.0. If a copy of
|
||||
// the MPL was not distributed with this file, you
|
||||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use crate::vec::Vec;
|
||||
use crate::error::LengthError;
|
||||
|
||||
use core::borrow::{Borrow, BorrowMut};
|
||||
use core::mem::{ManuallyDrop, MaybeUninit};
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::ptr::copy_nonoverlapping;
|
||||
use core::slice;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use {
|
||||
alloc::alloc::alloc,
|
||||
alloc::boxed::Box,
|
||||
core::alloc::Layout,
|
||||
};
|
||||
|
||||
impl<T, const N: usize> Vec<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 parts are valid to pass back to [`from_raw_parts`](Self::from_raw_parts).
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn into_raw_parts(self) -> ([MaybeUninit<T>; N], usize) {
|
||||
let this = ManuallyDrop::new(self);
|
||||
|
||||
unsafe {
|
||||
// SAFETY: `ManuallyDrop<T>` is transparent to `T`.
|
||||
// We also aren't dropping `this`, so we can safely
|
||||
// move out of it.
|
||||
let this = &*(&raw const this).cast::<Self>();
|
||||
|
||||
let buf = (&raw const this.buf).read();
|
||||
let len = this.len;
|
||||
|
||||
(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 dynamically-allocated 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_alloc_vec(self) -> alloc::vec::Vec<T> {
|
||||
self.into_boxed_slice().into_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> AsMut<[T]> for Vec<T, N> {
|
||||
#[inline(always)]
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.as_mut_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> AsRef<[T]> for Vec<T, N> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Borrow<[T]> for Vec<T, N> {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &[T] {
|
||||
self.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> BorrowMut<[T]> for Vec<T, N> {
|
||||
#[inline(always)]
|
||||
fn borrow_mut(&mut self) -> &mut [T] {
|
||||
self.as_mut_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Deref for Vec<T, N> {
|
||||
type Target = [T];
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> DerefMut for Vec<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 Vec<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: Copy, const N: usize> TryFrom<&[T]> for Vec<T, N> {
|
||||
type Error = LengthError;
|
||||
|
||||
#[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<Vec<T, N>> for Box<[T]> {
|
||||
#[inline(always)]
|
||||
fn from(value: Vec<T, N>) -> Self {
|
||||
value.into_boxed_slice()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T, const N: usize> From<Vec<T, N>> for alloc::vec::Vec<T> {
|
||||
#[inline(always)]
|
||||
fn from(value: Vec<T, N>) -> Self {
|
||||
value.into_alloc_vec()
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
// Copyright 2024-2025 Gabriel Bjørnager Jensen.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of
|
||||
// the Mozilla Public License, v. 2.0. If a copy of
|
||||
// the MPL was not distributed with this file, you
|
||||
// can obtain one at:
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
use crate::vec::{IntoIter, Vec};
|
||||
|
||||
use core::mem::MaybeUninit;
|
||||
use core::slice;
|
||||
|
||||
impl<T, const N: usize> FromIterator<T> for Vec<T, N> {
|
||||
#[inline]
|
||||
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
|
||||
let mut iter = iter.into_iter();
|
||||
|
||||
let mut buf = [const { MaybeUninit::<T>::uninit() };N];
|
||||
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 Vec<T, N> {
|
||||
type Item = T;
|
||||
|
||||
type IntoIter = IntoIter<T, N>;
|
||||
|
||||
#[inline(always)]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
let (buf, len) = self.into_raw_parts();
|
||||
|
||||
unsafe { IntoIter::new(buf, len) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, const N: usize> IntoIterator for &'a Vec<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 Vec<T, N> {
|
||||
type Item = &'a mut T;
|
||||
|
||||
type IntoIter = slice::IterMut<'a, T>;
|
||||
|
||||
#[inline(always)]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter_mut()
|
||||
}
|
||||
}
|
|
@ -7,28 +7,28 @@
|
|||
// <https://mozilla.org/MPL/2.0/>.
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod test;
|
||||
|
||||
use crate::error::LengthError;
|
||||
use crate::decode::{self, Decode, DecodeBorrowed};
|
||||
use crate::encode::{self, Encode, SizedEncode};
|
||||
use crate::error::{CollectionDecodeError, ItemDecodeError, LengthError};
|
||||
use crate::vec::IntoIter;
|
||||
|
||||
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::{Index, IndexMut};
|
||||
use core::mem::{ManuallyDrop, MaybeUninit};
|
||||
use core::ops::{Deref, DerefMut, Index, IndexMut};
|
||||
use core::ptr::{copy_nonoverlapping, drop_in_place, null, null_mut};
|
||||
use core::slice::SliceIndex;
|
||||
use core::slice::{self, SliceIndex};
|
||||
|
||||
// Encode/decode facilities:
|
||||
mod code;
|
||||
|
||||
// Conversion facilities:
|
||||
mod conv;
|
||||
|
||||
// Comparison facilities:
|
||||
mod cmp;
|
||||
|
||||
// Iterator facilities:
|
||||
mod iter;
|
||||
#[cfg(feature = "alloc")]
|
||||
use {
|
||||
alloc::alloc::alloc,
|
||||
alloc::boxed::Box,
|
||||
core::alloc::Layout,
|
||||
};
|
||||
|
||||
/// Vector container with maximum length.
|
||||
///
|
||||
|
@ -45,21 +45,21 @@ mod iter;
|
|||
/// ```rust
|
||||
/// use oct::vec::Vec;
|
||||
///
|
||||
/// let vec0 = Vec::<u8, 0x4>::try_from([0x3].as_slice()).unwrap();
|
||||
/// let vec1 = Vec::<u8, 0x4>::try_from([0x3, 0x2].as_slice()).unwrap();
|
||||
/// let vec2 = Vec::<u8, 0x4>::try_from([0x3, 0x2, 0x4].as_slice()).unwrap();
|
||||
/// let vec3 = Vec::<u8, 0x4>::try_from([0x3, 0x2, 0x4, 0x3].as_slice()).unwrap();
|
||||
/// let v0 = Vec::<u8, 0x4>::try_from([0x3].as_slice()).unwrap();
|
||||
/// let v1 = Vec::<u8, 0x4>::try_from([0x3, 0x2].as_slice()).unwrap();
|
||||
/// let v2 = Vec::<u8, 0x4>::try_from([0x3, 0x2, 0x4].as_slice()).unwrap();
|
||||
/// let v3 = Vec::<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));
|
||||
/// assert_eq!(size_of_val(&v0), size_of_val(&v1));
|
||||
/// assert_eq!(size_of_val(&v0), size_of_val(&v2));
|
||||
/// assert_eq!(size_of_val(&v0), size_of_val(&v3));
|
||||
/// assert_eq!(size_of_val(&v1), size_of_val(&v2));
|
||||
/// assert_eq!(size_of_val(&v1), size_of_val(&v3));
|
||||
/// assert_eq!(size_of_val(&v2), size_of_val(&v3));
|
||||
/// ```
|
||||
pub struct Vec<T, const N: usize> {
|
||||
buf: [MaybeUninit<T>; N],
|
||||
len: usize,
|
||||
buf: [MaybeUninit<T>; N],
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Vec<T, N> {
|
||||
|
@ -81,18 +81,54 @@ impl<T, const N: usize> Vec<T, N> {
|
|||
});
|
||||
}
|
||||
|
||||
let mut buf = [const { MaybeUninit::<T>::uninit() };N];
|
||||
// SAFETY: We have checked that the length is with-
|
||||
// in bounds.
|
||||
let this = unsafe { Self::new_unchecked(data) };
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
let this = unsafe {
|
||||
/// Constructs a new slice from existing data without checking bounds.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The entirety of `data` must be able to fit into an array of `N` elements.
|
||||
#[inline(always)]
|
||||
pub const unsafe fn new_unchecked(data: &[T]) -> Self
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
let len = data.len();
|
||||
let mut buf = [const { MaybeUninit::<T>::uninit() }; N];
|
||||
|
||||
debug_assert!(len <= N, "cannot construct vector from slice that is longer");
|
||||
|
||||
unsafe {
|
||||
let src = data.as_ptr();
|
||||
let dst = buf.as_mut_ptr().cast();
|
||||
|
||||
copy_nonoverlapping(src, dst, len);
|
||||
}
|
||||
|
||||
Self::from_raw_parts(buf, len)
|
||||
};
|
||||
// SAFETY: The relevant elements have been initialised and `len`
|
||||
unsafe { Self::from_raw_parts(buf, len) }
|
||||
}
|
||||
|
||||
Ok(this)
|
||||
/// Constructs a size-constrained 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 { len, buf }
|
||||
}
|
||||
|
||||
/// Copies elements from a slice.
|
||||
|
@ -120,7 +156,7 @@ impl<T, const N: usize> Vec<T, N> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Generates a sized slice referencing the elements of `self`.
|
||||
/// Generates a vector referencing the elements of `self`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn each_ref(&self) -> Vec<&T, N> {
|
||||
|
@ -147,7 +183,7 @@ impl<T, const N: usize> Vec<T, N> {
|
|||
unsafe { Vec::from_raw_parts(buf, len) }
|
||||
}
|
||||
|
||||
/// Generates a sized slice mutably referencing the elements of `self`.
|
||||
/// Generates a vector mutably referencing the elements of `self`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn each_mut(&mut self) -> Vec<&mut T, N> {
|
||||
|
@ -214,7 +250,7 @@ impl<T, const N: usize> Vec<T, N> {
|
|||
|
||||
/// Returns the length of the vector.
|
||||
///
|
||||
/// This value may necessarily be smaller than `N`.
|
||||
/// This value denotes the current amount of elements contained in the vector, which may be any value between zero and `N` (inclusive).
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn len(&self) -> usize {
|
||||
|
@ -230,11 +266,133 @@ impl<T, const N: usize> Vec<T, N> {
|
|||
self.len() == 0x0
|
||||
}
|
||||
|
||||
/// Checks if the vector is full, i.e. it cannot hold any more elements.
|
||||
/// 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 is_full(&self) -> bool {
|
||||
self.len() == N
|
||||
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 parts are valid to pass back to [`from_raw_parts`](Self::from_raw_parts).
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn into_raw_parts(self) -> ([MaybeUninit<T>; N], usize) {
|
||||
let this = ManuallyDrop::new(self);
|
||||
|
||||
unsafe {
|
||||
// SAFETY: `ManuallyDrop<T>` is transparent to `T`.
|
||||
// We also aren't dropping `this`, so we can safely
|
||||
// move out of it.
|
||||
let this = &*(&raw const this).cast::<Self>();
|
||||
|
||||
let buf = (&raw const this.buf).read();
|
||||
let len = this.len;
|
||||
|
||||
(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 dynamically-allocated 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_alloc_vec(self) -> alloc::vec::Vec<T> {
|
||||
self.into_boxed_slice().into_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> AsMut<[T]> for Vec<T, N> {
|
||||
#[inline(always)]
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.as_mut_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> AsRef<[T]> for Vec<T, N> {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Borrow<[T]> for Vec<T, N> {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &[T] {
|
||||
self.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> BorrowMut<[T]> for Vec<T, N> {
|
||||
#[inline(always)]
|
||||
fn borrow_mut(&mut self) -> &mut [T] {
|
||||
self.as_mut_slice()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -261,6 +419,35 @@ impl<T: Debug, const N: usize> Debug for Vec<T, N> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Decode, const N: usize> Decode for Vec<T, N> {
|
||||
type Error = CollectionDecodeError<LengthError, ItemDecodeError<usize, T::Error>>;
|
||||
|
||||
#[inline]
|
||||
fn decode(input: &mut decode::Input) -> Result<Self, Self::Error> {
|
||||
let len = Decode::decode(input).unwrap();
|
||||
if len > N {
|
||||
return Err(CollectionDecodeError::BadLength(LengthError {
|
||||
remaining: N,
|
||||
count: len,
|
||||
}));
|
||||
}
|
||||
|
||||
let mut buf = [const { MaybeUninit::<T>::uninit() }; N];
|
||||
|
||||
for (i, slot) in buf[..len].iter_mut().enumerate() {
|
||||
let v = Decode::decode(input)
|
||||
.map_err(|e| CollectionDecodeError::BadItem(ItemDecodeError { index: i, error: e }))?;
|
||||
|
||||
slot.write(v);
|
||||
}
|
||||
|
||||
let this = unsafe { Self::from_raw_parts(buf, len) };
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decode, const N: usize> DecodeBorrowed<[T]> for Vec<T, N> { }
|
||||
|
||||
impl<T, const N: usize> Default for Vec<T, N> {
|
||||
#[inline(always)]
|
||||
fn default() -> Self {
|
||||
|
@ -273,6 +460,22 @@ impl<T, const N: usize> Default for Vec<T, N> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Deref for Vec<T, N> {
|
||||
type Target = [T];
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> DerefMut for Vec<T, N> {
|
||||
#[inline(always)]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.as_mut_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Drop for Vec<T, N> {
|
||||
#[inline(always)]
|
||||
fn drop(&mut self) {
|
||||
|
@ -287,6 +490,47 @@ impl<T, const N: usize> Drop for Vec<T, N> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Encode, const N: usize> Encode for Vec<T, N> {
|
||||
type Error = <[T] as Encode>::Error;
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, output: &mut encode::Output) -> Result<(), Self::Error> {
|
||||
self.as_slice().encode(output)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq, const N: usize> Eq for Vec<T, N> { }
|
||||
|
||||
impl<T, const N: usize> From<[T; N]> for Vec<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 Vec<T, N> {
|
||||
#[inline]
|
||||
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
|
||||
let mut iter = iter.into_iter();
|
||||
|
||||
let mut buf = [const { MaybeUninit::<T>::uninit() }; N];
|
||||
let mut len = 0x0;
|
||||
|
||||
for item in &mut buf {
|
||||
let Some(value) = iter.next() else { break };
|
||||
item.write(value);
|
||||
|
||||
len += 0x1;
|
||||
}
|
||||
|
||||
Self { len, buf }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash, const N: usize> Hash for Vec<T, N> {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
|
@ -311,3 +555,129 @@ impl<T, I: SliceIndex<[T]>, const N: usize> IndexMut<I> for Vec<T, N> {
|
|||
self.get_mut(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> IntoIterator for Vec<T, N> {
|
||||
type Item = T;
|
||||
|
||||
type IntoIter = IntoIter<T, N>;
|
||||
|
||||
#[inline(always)]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
let (buf, len) = self.into_raw_parts();
|
||||
|
||||
unsafe { IntoIter::new(buf, len) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, const N: usize> IntoIterator for &'a Vec<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 Vec<T, N> {
|
||||
type Item = &'a mut T;
|
||||
|
||||
type IntoIter = slice::IterMut<'a, T>;
|
||||
|
||||
#[inline(always)]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ord, const N: usize> Ord for Vec<T, N> {
|
||||
#[inline(always)]
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.as_slice().cmp(other.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq<U>, U, const N: usize, const M: usize> PartialEq<Vec<U, M>> for Vec<T, N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Vec<U, M>) -> bool {
|
||||
self.as_slice() == other.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq<U>, U, const N: usize, const M: usize> PartialEq<[U; M]> for Vec<T, N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &[U; M]) -> bool {
|
||||
self == other.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq<U>, U, const N: usize> PartialEq<[U]> for Vec<T, N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &[U]) -> bool {
|
||||
self.as_slice() == other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq<U>, U, const N: usize> PartialEq<&[U]> for Vec<T, N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &&[U]) -> bool {
|
||||
self == *other
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: PartialEq<U>, U, const N: usize> PartialEq<alloc::vec::Vec<U>> for Vec<T, N> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &alloc::vec::Vec<U>) -> bool {
|
||||
self.as_slice() == other.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialOrd, const N: usize, const M: usize> PartialOrd<Vec<T, M>> for Vec<T, N> {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &Vec<T, M>) -> Option<Ordering> {
|
||||
self.as_slice().partial_cmp(other.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SizedEncode, const N: usize> SizedEncode for Vec<T, N> {
|
||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE * N;
|
||||
}
|
||||
|
||||
impl<T: Copy, const N: usize> TryFrom<&[T]> for Vec<T, N> {
|
||||
type Error = LengthError;
|
||||
|
||||
#[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<Vec<T, N>> for Box<[T]> {
|
||||
#[inline(always)]
|
||||
fn from(value: Vec<T, N>) -> Self {
|
||||
value.into_boxed_slice()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T, const N: usize> From<Vec<T, N>> for alloc::vec::Vec<T> {
|
||||
#[inline(always)]
|
||||
fn from(value: Vec<T, N>) -> Self {
|
||||
value.into_alloc_vec()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||
impl<T: PartialEq<U>, U, const N: usize> PartialEq<Vec<U, N>> for alloc::vec::Vec<T> {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &Vec<U, N>) -> bool {
|
||||
self.as_slice() == other.as_slice()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue