Add 'into_bytes' destructor to 'SizedStr'; Clean up code; Update and add more tests; Manually implement '<SizedIter as Iterator>::nth'; Implement 'Encode' and 'Decode' for 'CString', 'SystemTime', 'Duration'; Implement 'Encode' for 'CStr'; Update docs; Fix includes in '/bzipper/src/decode/mod.rs' and '/bzipper/src/sized_encode/mod.rs'; Add new 'NullCString' and 'NarrowSystemTime' error variants to 'DecodeError'; Optimise '<String as Decode>::decode'; Update lints; Implement 'SizedEncode' for 'SystemTime' and 'Duration'; Update benchmark stats; Update readme;
This commit is contained in:
parent
b7a72dc0f9
commit
74b8d2caa2
17 changed files with 366 additions and 78 deletions
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -3,6 +3,23 @@
|
||||||
This is the changelog of bzipper.
|
This is the changelog of bzipper.
|
||||||
See `README.md` for more information.
|
See `README.md` for more information.
|
||||||
|
|
||||||
|
## 0.11.0
|
||||||
|
|
||||||
|
* Add `into_bytes` destructor to `SizedStr`
|
||||||
|
* Clean up code
|
||||||
|
* Update and add more tests
|
||||||
|
* Manually implement `<SizedIter as Iterator>::nth`
|
||||||
|
* Implement `Encode` and `Decode` for `CString`, `SystemTime`, `Duration`
|
||||||
|
* Implement `Encode` for `CStr`
|
||||||
|
* Update docs
|
||||||
|
* Fix includes in `/bzipper/src/decode/mod.rs` and `/bzipper/src/sized_encode/mod.rs`
|
||||||
|
* Add new `NullCString` and `NarrowSystemTime` error variants to `DecodeError`
|
||||||
|
* Optimise `<String as Decode>::decode`
|
||||||
|
* Update lints
|
||||||
|
* Implement `SizedEncode` for `SystemTime` and `Duration`
|
||||||
|
* Update benchmark stats
|
||||||
|
* Update readme
|
||||||
|
|
||||||
## 0.10.1
|
## 0.10.1
|
||||||
|
|
||||||
* Clean up and refactor code
|
* Clean up and refactor code
|
||||||
|
|
|
@ -82,6 +82,7 @@ match_bool = "warn"
|
||||||
match_on_vec_items = "warn"
|
match_on_vec_items = "warn"
|
||||||
match_same_arms = "warn"
|
match_same_arms = "warn"
|
||||||
mismatching_type_param_order = "warn"
|
mismatching_type_param_order = "warn"
|
||||||
|
missing_transmute_annotations = "forbid"
|
||||||
mixed_read_write_in_expression = "deny"
|
mixed_read_write_in_expression = "deny"
|
||||||
mut_mut = "deny"
|
mut_mut = "deny"
|
||||||
mutex_atomic = "deny"
|
mutex_atomic = "deny"
|
||||||
|
|
14
README.md
14
README.md
|
@ -20,13 +20,13 @@ According to my runs on an AMD Ryzen 7 3700X, these benchmarks indicate that bZi
|
||||||
|
|
||||||
| Benchmark | [Bincode] | [Borsh] | bZipper | [Ciborium] | [Postcard] |
|
| Benchmark | [Bincode] | [Borsh] | bZipper | [Ciborium] | [Postcard] |
|
||||||
| :--------------------------------- | --------: | ------: | ------: | ---------: | ---------: |
|
| :--------------------------------- | --------: | ------: | ------: | ---------: | ---------: |
|
||||||
| `encode_u8` | 1.262 | 1.271 | 1.153 | 2.854 | 1.270 |
|
| `encode_u8` | 1.234 | 1.096 | 0.881 | 3.076 | 1.223 |
|
||||||
| `encode_struct_unit` | 0.000 | 0.000 | 0.000 | 0.447 | 0.000 |
|
| `encode_struct_unit` | 0.000 | 0.000 | 0.000 | 0.516 | 0.000 |
|
||||||
| `encode_struct_unnamed` | 1.270 | 1.102 | 0.998 | 1.948 | 1.182 |
|
| `encode_struct_unnamed` | 1.367 | 1.154 | 1.009 | 2.051 | 1.191 |
|
||||||
| `encode_struct_named` | 4.205 | 1.186 | 1.136 | 10.395 | 1.168 |
|
| `encode_struct_named` | 4.101 | 1.271 | 1.181 | 9.342 | 1.182 |
|
||||||
| `encode_enum_unit` | 0.328 | 0.008 | 0.000 | 2.293 | 0.004 |
|
| `encode_enum_unit` | 0.306 | 0.008 | 0.000 | 2.304 | 0.004 |
|
||||||
| **Total time** → | 7.065 | 3.567 | 3.286 | 17.937 | 3.625 |
|
| **Total time** → | 7.009 | 3.528 | 3.071 | 17.289 | 3.599 |
|
||||||
| **Total deviation (p.c.)** → | +115 | +9 | ±0 | +446 | +10 |
|
| **Total deviation (p.c.)** → | +128 | +15 | ±0 | +463 | +17 |
|
||||||
|
|
||||||
[Bincode]: https://crates.io/crates/bincode/
|
[Bincode]: https://crates.io/crates/bincode/
|
||||||
[Borsh]: https://crates.io/crates/borsh/
|
[Borsh]: https://crates.io/crates/borsh/
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "bzipper"
|
name = "bzipper"
|
||||||
version = "0.10.1"
|
version = "0.11.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.83"
|
rust-version = "1.83"
|
||||||
documentation = "https://docs.rs/bzipper/"
|
documentation = "https://docs.rs/bzipper/"
|
||||||
|
@ -24,7 +24,7 @@ alloc = []
|
||||||
std = []
|
std = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bzipper_macros = { path = "../bzipper_macros", version = "0.10.1" }
|
bzipper_macros = { path = "../bzipper_macros", version = "0.11.0" }
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
|
@ -48,12 +48,18 @@ use core::ops::{
|
||||||
RangeTo,
|
RangeTo,
|
||||||
RangeToInclusive,
|
RangeToInclusive,
|
||||||
};
|
};
|
||||||
|
use core::ptr::copy_nonoverlapping;
|
||||||
|
use core::str;
|
||||||
|
use core::time::Duration;
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use std::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use std::collections::LinkedList;
|
use alloc::collections::LinkedList;
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
use alloc::ffi::CString;
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
|
@ -76,6 +82,9 @@ use std::hash::BuildHasher;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::sync::{Mutex, RwLock};
|
use std::sync::{Mutex, RwLock};
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
// Should we require `Encode` for `Decode`?
|
// Should we require `Encode` for `Decode`?
|
||||||
|
|
||||||
/// Denotes a type capable of being decoded.
|
/// Denotes a type capable of being decoded.
|
||||||
|
@ -96,7 +105,6 @@ where
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||||
let value = (Decode::decode(stream)?, );
|
let value = (Decode::decode(stream)?, );
|
||||||
|
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,6 +114,7 @@ impl<T: Decode, const N: usize> Decode for [T; N] {
|
||||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||||
// Initialise the array incrementally.
|
// Initialise the array incrementally.
|
||||||
|
|
||||||
|
// SAFETY: Always safe.
|
||||||
let mut buf: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
|
let mut buf: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
|
||||||
|
|
||||||
for item in &mut buf {
|
for item in &mut buf {
|
||||||
|
@ -207,6 +216,46 @@ impl Decode for char {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||||
|
impl Decode for CString {
|
||||||
|
#[inline(always)]
|
||||||
|
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||||
|
let len = Decode::decode(stream)?;
|
||||||
|
|
||||||
|
let data = stream.read(len);
|
||||||
|
|
||||||
|
for (i, c) in data.iter().enumerate() {
|
||||||
|
if *c == b'\x00' { return Err(DecodeError::NullCString { index: i }) };
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buf = Vec::with_capacity(len);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let src = data.as_ptr();
|
||||||
|
let dst = buf.as_mut_ptr();
|
||||||
|
|
||||||
|
copy_nonoverlapping(src, dst, len);
|
||||||
|
buf.set_len(len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: We have already tested the data.
|
||||||
|
let this = unsafe { Self::from_vec_unchecked(buf) };
|
||||||
|
Ok(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode for Duration {
|
||||||
|
#[inline(always)]
|
||||||
|
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||||
|
let secs = Decode::decode(stream)?;
|
||||||
|
let nanos = Decode::decode(stream)?;
|
||||||
|
|
||||||
|
let this = Self::new(secs, nanos);
|
||||||
|
Ok(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||||
impl<K, V, S> Decode for HashMap<K, V, S>
|
impl<K, V, S> Decode for HashMap<K, V, S>
|
||||||
|
@ -283,7 +332,6 @@ impl Decode for Ipv4Addr {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||||
let value = Decode::decode(stream)?;
|
let value = Decode::decode(stream)?;
|
||||||
|
|
||||||
Ok(Self::from_bits(value))
|
Ok(Self::from_bits(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -292,7 +340,6 @@ impl Decode for Ipv6Addr {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||||
let value = Decode::decode(stream)?;
|
let value = Decode::decode(stream)?;
|
||||||
|
|
||||||
Ok(Self::from_bits(value))
|
Ok(Self::from_bits(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -512,15 +559,52 @@ impl Decode for SocketAddrV6 {
|
||||||
impl Decode for String {
|
impl Decode for String {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||||
let data = <Vec::<u8>>::decode(stream)?;
|
let len = Decode::decode(stream)?;
|
||||||
|
|
||||||
Self::from_utf8(data)
|
let data = stream.read(len);
|
||||||
|
|
||||||
|
str::from_utf8(data)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
let data = e.as_bytes();
|
let i = e.valid_up_to();
|
||||||
let i = e.utf8_error().valid_up_to();
|
let c = data[i];
|
||||||
|
|
||||||
DecodeError::BadString(Utf8Error { value: data[i], index: i })
|
DecodeError::BadString(Utf8Error { value: c, index: i })
|
||||||
})
|
})?;
|
||||||
|
|
||||||
|
let mut v = Vec::with_capacity(len);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let src = data.as_ptr();
|
||||||
|
let dst = v.as_mut_ptr();
|
||||||
|
|
||||||
|
copy_nonoverlapping(src, dst, len);
|
||||||
|
v.set_len(len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: We have already tested the raw data.
|
||||||
|
let this = unsafe { Self::from_utf8_unchecked(v) };
|
||||||
|
Ok(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||||
|
impl Decode for SystemTime {
|
||||||
|
#[inline]
|
||||||
|
fn decode(stream: &mut IStream) -> Result<Self, DecodeError> {
|
||||||
|
let time = i64::decode(stream)?;
|
||||||
|
|
||||||
|
let this = if time.is_positive() {
|
||||||
|
let time = time as u64;
|
||||||
|
|
||||||
|
UNIX_EPOCH.checked_add(Duration::from_secs(time))
|
||||||
|
} else {
|
||||||
|
let time = time.unsigned_abs();
|
||||||
|
|
||||||
|
UNIX_EPOCH.checked_sub(Duration::from_secs(time))
|
||||||
|
};
|
||||||
|
|
||||||
|
this.ok_or_else(|| DecodeError::NarrowSystemTime { timestamp: time })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
// not, see <https://www.gnu.org/licenses/>.
|
// not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
use alloc::string::String;
|
||||||
use bzipper::{Decode, IStream, SizedEncode};
|
use bzipper::{Decode, IStream, SizedEncode};
|
||||||
use core::char;
|
use core::char;
|
||||||
|
|
||||||
|
@ -118,5 +119,7 @@ fn test_decode() {
|
||||||
0xC8, 0x4C,
|
0xC8, 0x4C,
|
||||||
] => UnitOrFields::Named { timestamp: 1724237900 });
|
] => UnitOrFields::Named { timestamp: 1724237900 });
|
||||||
|
|
||||||
test!(Vec<u16>: [0x00, 0x02, 0xFF, 0xEE, 0xDD, 0xCC] => [0xFF_EE, 0xDD_CC].as_slice());
|
test!(Vec<u16>: [0x00, 0x02, 0xAA, 0xBB, 0xCC, 0xDD] => [0xAA_BB, 0xCC_DD].as_slice());
|
||||||
|
|
||||||
|
test!(String: [0x00, 0x06, 0xE6, 0x97, 0xA5, 0xE6, 0x9C, 0xAC] => "\u{65E5}\u{672C}");
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ use crate::error::EncodeError;
|
||||||
|
|
||||||
use core::cell::{Cell, LazyCell, RefCell};
|
use core::cell::{Cell, LazyCell, RefCell};
|
||||||
use core::convert::Infallible;
|
use core::convert::Infallible;
|
||||||
|
use core::ffi::CStr;
|
||||||
use core::hash::BuildHasher;
|
use core::hash::BuildHasher;
|
||||||
use core::hint::unreachable_unchecked;
|
use core::hint::unreachable_unchecked;
|
||||||
use core::marker::{PhantomData, PhantomPinned};
|
use core::marker::{PhantomData, PhantomPinned};
|
||||||
|
@ -48,6 +49,7 @@ use core::ops::{
|
||||||
RangeTo,
|
RangeTo,
|
||||||
RangeToInclusive,
|
RangeToInclusive,
|
||||||
};
|
};
|
||||||
|
use core::time::Duration;
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use alloc::borrow::{Cow, ToOwned};
|
use alloc::borrow::{Cow, ToOwned};
|
||||||
|
@ -58,6 +60,9 @@ use alloc::boxed::Box;
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use alloc::collections::LinkedList;
|
use alloc::collections::LinkedList;
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
use alloc::ffi::CString;
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
|
|
||||||
|
@ -76,6 +81,9 @@ use std::collections::{HashMap, HashSet};
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::sync::{LazyLock, Mutex, RwLock};
|
use std::sync::{LazyLock, Mutex, RwLock};
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
/// Denotes a type capable of being encoded.
|
/// Denotes a type capable of being encoded.
|
||||||
///
|
///
|
||||||
/// It is recommended to simply derive this trait for custom types.
|
/// It is recommended to simply derive this trait for custom types.
|
||||||
|
@ -148,6 +156,8 @@ impl<T: Encode> Encode for (T, ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Encode, const N: usize> Encode for [T; N] {
|
impl<T: Encode, const N: usize> Encode for [T; N] {
|
||||||
|
/// Encodes each element sequentially.
|
||||||
|
/// The length is hard-coded into the type and is therefore not encoded.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||||
for value in self {
|
for value in self {
|
||||||
|
@ -159,6 +169,7 @@ impl<T: Encode, const N: usize> Encode for [T; N] {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Encode> Encode for [T] {
|
impl<T: Encode> Encode for [T] {
|
||||||
|
/// Encodes each element sequentially with an extra length specifier (of type [`usize`]) prepended first.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||||
self.len().encode(stream)?;
|
self.len().encode(stream)?;
|
||||||
|
@ -242,6 +253,35 @@ impl<T: Encode + ToOwned> Encode for Cow<'_, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Encode for CStr {
|
||||||
|
/// Encodes the string identically to [a byte slice](slice) containing the string's byte values **excluding** the null terminator.
|
||||||
|
#[inline(always)]
|
||||||
|
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||||
|
self.to_bytes().encode(stream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||||
|
impl Encode for CString {
|
||||||
|
/// See the the implementation of [`CStr`].
|
||||||
|
#[inline(always)]
|
||||||
|
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||||
|
self.as_c_str().encode(stream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encode for Duration {
|
||||||
|
/// Encodes the duration's seconds and nanoseconds counters sequentially.
|
||||||
|
#[inline(always)]
|
||||||
|
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||||
|
self.as_secs().encode(stream)?;
|
||||||
|
self.subsec_nanos().encode(stream)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||||
impl<K, V, S> Encode for HashMap<K, V, S>
|
impl<K, V, S> Encode for HashMap<K, V, S>
|
||||||
|
@ -289,9 +329,10 @@ impl Encode for Infallible {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This implementation encoded as discriminant denoting the IP version of the address (i.e. `4` for IPv4 and `6` for IPv6).
|
|
||||||
/// This is then followed by the respective address' own encoding (either [`Ipv4Addr`] or [`Ipv6Addr`]).
|
|
||||||
impl Encode for IpAddr {
|
impl Encode for IpAddr {
|
||||||
|
/// Encodes a the address with a preceding discriminant denoting the IP version of the address (i.e. `4` for IPv4 and `6` for IPv6).
|
||||||
|
///
|
||||||
|
/// See also the implementations of [`Ipv4Addr`] and [`Ipv6Addr`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||||
// The discriminant here is the IP version.
|
// The discriminant here is the IP version.
|
||||||
|
@ -312,8 +353,8 @@ impl Encode for IpAddr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This implementation encodes the address's bits in big-endian.
|
|
||||||
impl Encode for Ipv4Addr {
|
impl Encode for Ipv4Addr {
|
||||||
|
/// Encodes the address's bits in big-endian.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||||
let value = self.to_bits();
|
let value = self.to_bits();
|
||||||
|
@ -321,8 +362,8 @@ impl Encode for Ipv4Addr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This implementation encodes the address's bits in big-endian.
|
|
||||||
impl Encode for Ipv6Addr {
|
impl Encode for Ipv6Addr {
|
||||||
|
/// Encodes the address's bits in big-endian.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||||
let value = self.to_bits();
|
let value = self.to_bits();
|
||||||
|
@ -330,9 +371,10 @@ impl Encode for Ipv6Addr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This implementation casts `self` to `i16` before encoding.
|
|
||||||
/// If this conversion isn't possible for the given value, then the [`IsizeOutOfRange`](EncodeError::IsizeOutOfRange) error is returned.
|
|
||||||
impl Encode for isize {
|
impl Encode for isize {
|
||||||
|
/// Casts `self` to [`i16`] and encodes.
|
||||||
|
///
|
||||||
|
/// If this conversion isn't possible for the given value, then the [`IsizeOutOfRange`](EncodeError::IsizeOutOfRange) error is returned.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||||
let value = i16::try_from(*self)
|
let value = i16::try_from(*self)
|
||||||
|
@ -383,10 +425,11 @@ impl<T: Encode> Encode for Mutex<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This implementation encodes a sign denoting the optional's variant.
|
|
||||||
/// The sign is `false` for `None` instances and `true` for `Some` instances.
|
|
||||||
/// The contained value is encoded proceeding the sign.
|
|
||||||
impl<T: Encode> Encode for Option<T> {
|
impl<T: Encode> Encode for Option<T> {
|
||||||
|
/// Encodes a sign denoting the optional's variant.
|
||||||
|
/// This is `false` for `None` instances and `true` for `Some` instances.
|
||||||
|
///
|
||||||
|
/// If `Some`, then the contained value is encoded after this sign..
|
||||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||||
match *self {
|
match *self {
|
||||||
None => false.encode(stream)?,
|
None => false.encode(stream)?,
|
||||||
|
@ -485,10 +528,12 @@ impl<T: Encode> Encode for RefCell<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This implementation encodes a sign denoting the optional's variant.
|
|
||||||
/// The sign is `false` for denoting `Ok` and `true` for denoting `Err`.
|
|
||||||
/// The contained value is encoded proceeding the sign.
|
|
||||||
impl<T: Encode, E: Encode> Encode for core::result::Result<T, E> {
|
impl<T: Encode, E: Encode> Encode for core::result::Result<T, E> {
|
||||||
|
/// Encodes a sign denoting the result's variant.
|
||||||
|
/// This is `false` for `Ok` instances and `true` for `Err` instances.
|
||||||
|
///
|
||||||
|
/// If `Ok`, then the contained value is encoded after this sign.
|
||||||
|
#[inline]
|
||||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||||
// The sign here is `false` for `Ok` objects and
|
// The sign here is `false` for `Ok` objects and
|
||||||
// `true` for `Err` objects.
|
// `true` for `Err` objects.
|
||||||
|
@ -528,9 +573,9 @@ impl<T: Encode> Encode for Saturating<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This implementation encoded as discriminant denoting the IP version of the address (i.e. `4` for IPv4 and `6` for IPv6).
|
|
||||||
/// This is then followed by the respective address' own encoding (either [`SocketAddrV4`] or [`SocketAddrV6`]).
|
|
||||||
impl Encode for SocketAddr {
|
impl Encode for SocketAddr {
|
||||||
|
/// This implementation encoded as discriminant denoting the IP version of the address (i.e. `4` for IPv4 and `6` for IPv6).
|
||||||
|
/// This is then followed by the respective address' own encoding (either [`SocketAddrV4`] or [`SocketAddrV6`]).
|
||||||
#[inline]
|
#[inline]
|
||||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||||
// The discriminant here is the IP version.
|
// The discriminant here is the IP version.
|
||||||
|
@ -551,8 +596,8 @@ impl Encode for SocketAddr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This implementation encodes the address's bits followed by the port number, all of which in big-endian.
|
|
||||||
impl Encode for SocketAddrV4 {
|
impl Encode for SocketAddrV4 {
|
||||||
|
/// Encodes the address's bits followed by the port number, both of which in big-endian.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||||
self.ip().encode(stream)?;
|
self.ip().encode(stream)?;
|
||||||
|
@ -562,8 +607,8 @@ impl Encode for SocketAddrV4 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This implementation encodes the address's bits followed by the port number, all of which in big-endian.
|
|
||||||
impl Encode for SocketAddrV6 {
|
impl Encode for SocketAddrV6 {
|
||||||
|
/// Encodes the address's bits followed by the port number, flow information, and scope identifier -- all of which in big-endian.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||||
self.ip().encode(stream)?;
|
self.ip().encode(stream)?;
|
||||||
|
@ -576,6 +621,7 @@ impl Encode for SocketAddrV6 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encode for str {
|
impl Encode for str {
|
||||||
|
/// Encodes the string identically to [a byte slice](slice) containing the string's byte values.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||||
// Optimised encode. Don't just rely on `[char]`.
|
// Optimised encode. Don't just rely on `[char]`.
|
||||||
|
@ -590,12 +636,47 @@ impl Encode for str {
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||||
impl Encode for String {
|
impl Encode for String {
|
||||||
|
/// See [`str`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||||
self.as_str().encode(stream)
|
self.as_str().encode(stream)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||||
|
impl Encode for SystemTime {
|
||||||
|
/// Encodes the time point as the nearest, signed UNIX timestamp.
|
||||||
|
///
|
||||||
|
/// Examples of some timestamps and their encodings include:
|
||||||
|
///
|
||||||
|
/// | ISO 8601 | UNIX / bZipper |
|
||||||
|
/// | :-------------------------- | -------------: |
|
||||||
|
/// | `2024-11-03T12:02:01+01:00` | +1730631721 |
|
||||||
|
/// | `1989-06-03T20:00:00+09:00` | +13258800 |
|
||||||
|
/// | `1970-01-01T00:00:00Z` | +0 |
|
||||||
|
/// | `1945-05-04T18:30:00+02:00` | -778231800 |
|
||||||
|
#[expect(clippy::cast_possible_wrap)]
|
||||||
|
#[inline]
|
||||||
|
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||||
|
let time = if *self >= UNIX_EPOCH {
|
||||||
|
let duration = self
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.expect("cannot compute duration since the epoch");
|
||||||
|
|
||||||
|
duration.as_secs() as i64
|
||||||
|
} else {
|
||||||
|
let duration = UNIX_EPOCH
|
||||||
|
.duration_since(*self)
|
||||||
|
.expect("cannot compute duration until the epoch");
|
||||||
|
|
||||||
|
0x0 - duration.as_secs() as i64
|
||||||
|
};
|
||||||
|
|
||||||
|
time.encode(stream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Encode for () {
|
impl Encode for () {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn encode(&self, _stream: &mut OStream) -> Result<(), EncodeError> {
|
fn encode(&self, _stream: &mut OStream) -> Result<(), EncodeError> {
|
||||||
|
@ -603,9 +684,11 @@ impl Encode for () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This implementation casts `self` to `u16` before encoding.
|
|
||||||
/// If this conversion isn't possible for the given value, then the [`IsizeOutOfRange`](EncodeError::IsizeOutOfRange) error is returned.
|
|
||||||
impl Encode for usize {
|
impl Encode for usize {
|
||||||
|
/// Casts `self` to [`u16`] and encodes.
|
||||||
|
///
|
||||||
|
/// If this conversion isn't possible for the given value, then the [`IsizeOutOfRange`](EncodeError::UsizeOutOfRange) error is returned.
|
||||||
|
#[inline]
|
||||||
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
fn encode(&self, stream: &mut OStream) -> Result<(), EncodeError> {
|
||||||
let value = u16::try_from(*self)
|
let value = u16::try_from(*self)
|
||||||
.map_err(|_| EncodeError::UsizeOutOfRange(*self))?;
|
.map_err(|_| EncodeError::UsizeOutOfRange(*self))?;
|
||||||
|
@ -680,11 +763,12 @@ macro_rules! impl_atomic {
|
||||||
ty: $ty:ty,
|
ty: $ty:ty,
|
||||||
atomic_ty: $atomic_ty:ty$(,)?
|
atomic_ty: $atomic_ty:ty$(,)?
|
||||||
} => {
|
} => {
|
||||||
/// This implementation uses the same format as the atomic's primitive counterpart.
|
|
||||||
/// The atomic object itself is read with the [`Relaxed`](core::sync::atomic::Ordering) ordering scheme.
|
|
||||||
#[cfg(target_has_atomic = $width)]
|
#[cfg(target_has_atomic = $width)]
|
||||||
#[cfg_attr(doc, doc(cfg(target_has_atomic = $width)))]
|
#[cfg_attr(doc, doc(cfg(target_has_atomic = $width)))]
|
||||||
impl ::bzipper::Encode for $atomic_ty {
|
impl ::bzipper::Encode for $atomic_ty {
|
||||||
|
/// Encodes the atomic with the same scheme as that of the atomic type's primitive counterpart.
|
||||||
|
///
|
||||||
|
/// The atomic object itself is read with the [`Relaxed`](core::sync::atomic::Ordering) ordering scheme.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn encode(&self, stream: &mut ::bzipper::OStream) -> ::core::result::Result<(), ::bzipper::error::EncodeError> {
|
fn encode(&self, stream: &mut ::bzipper::OStream) -> ::core::result::Result<(), ::bzipper::error::EncodeError> {
|
||||||
self.load(::std::sync::atomic::Ordering::Relaxed).encode(stream)
|
self.load(::std::sync::atomic::Ordering::Relaxed).encode(stream)
|
||||||
|
|
|
@ -18,6 +18,9 @@
|
||||||
// er General Public License along with bzipper. If
|
// er General Public License along with bzipper. If
|
||||||
// not, see <https://www.gnu.org/licenses/>.
|
// not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use core::time::Duration;
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use bzipper::{Encode, OStream, SizedEncode, SizedStr};
|
use bzipper::{Encode, OStream, SizedEncode, SizedStr};
|
||||||
|
@ -96,5 +99,11 @@ fn test_encode() {
|
||||||
0x00, 0x4C, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x4C, 0x00, 0x00, 0x00, 0x00,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
test!(Vec<u8>: Vec::from([0xAA, 0xBB, 0xCC]) => [0x00, 0x03, 0xAA, 0xBB, 0xCC]);
|
test!(Vec<u16>: From::from([0x8000, 0x8000, 0x8000]) => [0x00, 0x03, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00]);
|
||||||
|
|
||||||
|
test!(SystemTime: UNIX_EPOCH => [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
|
||||||
|
|
||||||
|
test!(SystemTime: UNIX_EPOCH - Duration::from_secs(0x1) => [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]);
|
||||||
|
|
||||||
|
test!(SystemTime: UNIX_EPOCH + Duration::from_secs(0x1) => [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,9 +56,27 @@ pub enum DecodeError {
|
||||||
/// An invalid enumeration descriminant was provided.
|
/// An invalid enumeration descriminant was provided.
|
||||||
InvalidDiscriminant(isize),
|
InvalidDiscriminant(isize),
|
||||||
|
|
||||||
|
/// The [`SystemTime`](std::time::SystemTime) type could not represent a UNIX timestamp.
|
||||||
|
///
|
||||||
|
/// This error should not occur on systems that represent timestamps with at least a signed 64-bits seconds counter.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||||
|
NarrowSystemTime {
|
||||||
|
/// The unrepresentable timestamp.
|
||||||
|
timestamp: i64,
|
||||||
|
},
|
||||||
|
|
||||||
/// A non-zero integer had the value `0`.
|
/// A non-zero integer had the value `0`.
|
||||||
NullInteger,
|
NullInteger,
|
||||||
|
|
||||||
|
/// A C-like string encountered a null value within bounds.
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||||
|
NullCString {
|
||||||
|
/// The index of the null value.
|
||||||
|
index: usize,
|
||||||
|
},
|
||||||
|
|
||||||
/// An array could not hold the requested amount of elements.
|
/// An array could not hold the requested amount of elements.
|
||||||
SmallBuffer(SizeError),
|
SmallBuffer(SizeError),
|
||||||
}
|
}
|
||||||
|
@ -77,16 +95,24 @@ impl Display for DecodeError {
|
||||||
=> write!(f, "{source}"),
|
=> write!(f, "{source}"),
|
||||||
|
|
||||||
InvalidBoolean(value)
|
InvalidBoolean(value)
|
||||||
=> write!(f, "expected boolean but got {value:#02X}"),
|
=> write!(f, "expected boolean but got `{value:#02X}`"),
|
||||||
|
|
||||||
InvalidCodePoint(value)
|
InvalidCodePoint(value)
|
||||||
=> write!(f, "code point U+{value:04X} is not defined"),
|
=> write!(f, "code point U+{value:04X} is not defined"),
|
||||||
|
|
||||||
InvalidDiscriminant(value)
|
InvalidDiscriminant(value)
|
||||||
=> write!(f, "discriminant ({value}) is not valid for the given enumeration"),
|
=> write!(f, "discriminant `{value}` is not valid for the given enumeration"),
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
NarrowSystemTime { timestamp }
|
||||||
|
=> write!(f, "could not represent `{timestamp}` as a system timestamp"),
|
||||||
|
|
||||||
NullInteger
|
NullInteger
|
||||||
=> write!(f, "expected non-zero integer but got (0)"),
|
=> write!(f, "expected non-zero integer but got `0`"),
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
NullCString { index }
|
||||||
|
=> write!(f, "expected c string but found null value at '{index}`"),
|
||||||
|
|
||||||
SmallBuffer(ref source)
|
SmallBuffer(ref source)
|
||||||
=> write!(f, "buffer too small: {source}"),
|
=> write!(f, "buffer too small: {source}"),
|
||||||
|
|
|
@ -41,13 +41,13 @@
|
||||||
//!
|
//!
|
||||||
//! | Benchmark | [Bincode] | [Borsh] | bZipper | [Ciborium] | [Postcard] |
|
//! | Benchmark | [Bincode] | [Borsh] | bZipper | [Ciborium] | [Postcard] |
|
||||||
//! | :--------------------------------- | --------: | ------: | ------: | ---------: | ---------: |
|
//! | :--------------------------------- | --------: | ------: | ------: | ---------: | ---------: |
|
||||||
//! | `encode_u8` | 1.262 | 1.271 | 1.153 | 2.854 | 1.270 |
|
//! | `encode_u8` | 1.234 | 1.096 | 0.881 | 3.076 | 1.223 |
|
||||||
//! | `encode_struct_unit` | 0.000 | 0.000 | 0.000 | 0.447 | 0.000 |
|
//! | `encode_struct_unit` | 0.000 | 0.000 | 0.000 | 0.516 | 0.000 |
|
||||||
//! | `encode_struct_unnamed` | 1.270 | 1.102 | 0.998 | 1.948 | 1.182 |
|
//! | `encode_struct_unnamed` | 1.367 | 1.154 | 1.009 | 2.051 | 1.191 |
|
||||||
//! | `encode_struct_named` | 4.205 | 1.186 | 1.136 | 10.395 | 1.168 |
|
//! | `encode_struct_named` | 4.101 | 1.271 | 1.181 | 9.342 | 1.182 |
|
||||||
//! | `encode_enum_unit` | 0.328 | 0.008 | 0.000 | 2.293 | 0.004 |
|
//! | `encode_enum_unit` | 0.306 | 0.008 | 0.000 | 2.304 | 0.004 |
|
||||||
//! | **Total time** → | 7.065 | 3.567 | 3.286 | 17.937 | 3.625 |
|
//! | **Total time** → | 7.009 | 3.528 | 3.071 | 17.289 | 3.599 |
|
||||||
//! | **Total deviation (p.c.)** → | +115 | +9 | ±0 | +446 | +10 |
|
//! | **Total deviation (p.c.)** → | +128 | +15 | ±0 | +463 | +17 |
|
||||||
//!
|
//!
|
||||||
//! [Bincode]: https://crates.io/crates/bincode/
|
//! [Bincode]: https://crates.io/crates/bincode/
|
||||||
//! [Borsh]: https://crates.io/crates/borsh/
|
//! [Borsh]: https://crates.io/crates/borsh/
|
||||||
|
@ -430,8 +430,8 @@ use_mod!(pub i_stream);
|
||||||
use_mod!(pub o_stream);
|
use_mod!(pub o_stream);
|
||||||
use_mod!(pub sized_encode);
|
use_mod!(pub sized_encode);
|
||||||
use_mod!(pub sized_iter);
|
use_mod!(pub sized_iter);
|
||||||
use_mod!(pub sized_str);
|
|
||||||
use_mod!(pub sized_slice);
|
use_mod!(pub sized_slice);
|
||||||
|
use_mod!(pub sized_str);
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use_mod!(pub buf);
|
use_mod!(pub buf);
|
||||||
|
|
|
@ -45,10 +45,10 @@ use core::ops::{
|
||||||
RangeTo,
|
RangeTo,
|
||||||
RangeToInclusive,
|
RangeToInclusive,
|
||||||
};
|
};
|
||||||
use std::borrow::ToOwned;
|
use core::time::Duration;
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use alloc::borrow::Cow;
|
use alloc::borrow::{Cow, ToOwned};
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
|
@ -62,6 +62,9 @@ use alloc::sync::Arc;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::sync::{LazyLock, Mutex, RwLock};
|
use std::sync::{LazyLock, Mutex, RwLock};
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
/// Denotes a size-constrained, encodable type.
|
/// Denotes a size-constrained, encodable type.
|
||||||
///
|
///
|
||||||
/// When using [`Encode`], the size of the resulting encoding cannot always be known beforehand.
|
/// When using [`Encode`], the size of the resulting encoding cannot always be known beforehand.
|
||||||
|
@ -131,6 +134,12 @@ unsafe impl<T: SizedEncode + ToOwned> SizedEncode for Cow<'_, T> {
|
||||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe impl SizedEncode for Duration {
|
||||||
|
const MAX_ENCODED_SIZE: usize =
|
||||||
|
u64::MAX_ENCODED_SIZE
|
||||||
|
+ u32::MAX_ENCODED_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl SizedEncode for Infallible {
|
unsafe impl SizedEncode for Infallible {
|
||||||
const MAX_ENCODED_SIZE: usize = 0x0;
|
const MAX_ENCODED_SIZE: usize = 0x0;
|
||||||
}
|
}
|
||||||
|
@ -244,6 +253,12 @@ unsafe impl SizedEncode for SocketAddrV6 {
|
||||||
+ u32::MAX_ENCODED_SIZE;
|
+ u32::MAX_ENCODED_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||||
|
unsafe impl SizedEncode for SystemTime {
|
||||||
|
const MAX_ENCODED_SIZE: usize = i64::MAX_ENCODED_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl SizedEncode for () {
|
unsafe impl SizedEncode for () {
|
||||||
const MAX_ENCODED_SIZE: usize = 0x0;
|
const MAX_ENCODED_SIZE: usize = 0x0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ mod test;
|
||||||
|
|
||||||
use core::iter::{DoubleEndedIterator, ExactSizeIterator, FusedIterator};
|
use core::iter::{DoubleEndedIterator, ExactSizeIterator, FusedIterator};
|
||||||
use core::mem::MaybeUninit;
|
use core::mem::MaybeUninit;
|
||||||
|
use core::ptr::drop_in_place;
|
||||||
use core::slice;
|
use core::slice;
|
||||||
|
|
||||||
/// Iterator to a sized slice.
|
/// Iterator to a sized slice.
|
||||||
|
@ -86,6 +87,7 @@ impl<T, const N: usize> AsRef<[T]> for SizedIter<T, N> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone, const N: usize> Clone for SizedIter<T, N> {
|
impl<T: Clone, const N: usize> Clone for SizedIter<T, N> {
|
||||||
|
#[expect(clippy::borrow_deref_ref)] // Clippy is gaslighting me into believing pointers and references are identical???
|
||||||
#[inline]
|
#[inline]
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
let mut buf: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
|
let mut buf: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
|
||||||
|
@ -95,14 +97,12 @@ impl<T: Clone, const N: usize> Clone for SizedIter<T, N> {
|
||||||
let stop = start + len;
|
let stop = start + len;
|
||||||
|
|
||||||
for i in start..stop {
|
for i in start..stop {
|
||||||
unsafe {
|
unsafe {
|
||||||
let item = (&raw const *self.buf.get_unchecked(i)).cast();
|
let item = (&raw const *self.buf.get_unchecked(i)).cast();
|
||||||
|
|
||||||
let value = Clone::clone(&*item);
|
let value = Clone::clone(&*item);
|
||||||
|
|
||||||
buf
|
buf.get_unchecked_mut(i).write(value);
|
||||||
.get_unchecked_mut(i)
|
|
||||||
.write(value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,11 +117,7 @@ impl<T, const N: usize> DoubleEndedIterator for SizedIter<T, N> {
|
||||||
|
|
||||||
let index = self.pos + self.len - 0x1;
|
let index = self.pos + self.len - 0x1;
|
||||||
|
|
||||||
let item = unsafe {
|
let item = unsafe { self.buf.get_unchecked(index).assume_init_read() };
|
||||||
self.buf
|
|
||||||
.get_unchecked(index)
|
|
||||||
.assume_init_read()
|
|
||||||
};
|
|
||||||
|
|
||||||
self.len -= 0x1;
|
self.len -= 0x1;
|
||||||
|
|
||||||
|
@ -142,11 +138,7 @@ impl<T, const N: usize> Iterator for SizedIter<T, N> {
|
||||||
|
|
||||||
let index = self.pos;
|
let index = self.pos;
|
||||||
|
|
||||||
let item = unsafe {
|
let item = unsafe { self.buf.get_unchecked(index).assume_init_read() };
|
||||||
self.buf
|
|
||||||
.get_unchecked(index)
|
|
||||||
.assume_init_read()
|
|
||||||
};
|
|
||||||
|
|
||||||
self.len -= 0x1;
|
self.len -= 0x1;
|
||||||
self.pos += 0x1;
|
self.pos += 0x1;
|
||||||
|
@ -154,6 +146,31 @@ impl<T, const N: usize> Iterator for SizedIter<T, N> {
|
||||||
Some(item)
|
Some(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn nth(&mut self, index: usize) -> Option<Self::Item> {
|
||||||
|
if index > self.len { return None };
|
||||||
|
|
||||||
|
let start = self.pos;
|
||||||
|
let stop = start + index - 0x1;
|
||||||
|
|
||||||
|
// Drop each skipped element.
|
||||||
|
for i in start..stop {
|
||||||
|
unsafe {
|
||||||
|
let item = &raw mut *self.buf.get_unchecked_mut(i);
|
||||||
|
|
||||||
|
drop_in_place(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the final element.
|
||||||
|
let item = unsafe { self.buf.get_unchecked(index).assume_init_read() };
|
||||||
|
|
||||||
|
self.len -= index;
|
||||||
|
self.pos += index;
|
||||||
|
|
||||||
|
Some(item)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
let rem = unsafe { self.len.unchecked_sub(self.pos) };
|
let rem = unsafe { self.len.unchecked_sub(self.pos) };
|
||||||
|
|
|
@ -19,7 +19,31 @@
|
||||||
// er General Public License along with bZipper. If
|
// er General Public License along with bZipper. If
|
||||||
// not, see <https://www.gnu.org/licenses/>.
|
// not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use bzipper::SizedSlice;
|
use bzipper::{SizedSlice, SizedStr};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sized_iter_clone() {
|
||||||
|
let data = SizedStr::<0x9>::new("fran\u{00E7}ais").unwrap();
|
||||||
|
|
||||||
|
let mut data0 = data.into_bytes().into_iter();
|
||||||
|
|
||||||
|
let _ = data0.nth(0x4);
|
||||||
|
|
||||||
|
let mut data1 = data0.clone();
|
||||||
|
|
||||||
|
assert_eq!(data0.next(), Some(0xC3));
|
||||||
|
assert_eq!(data1.next(), Some(0xC3));
|
||||||
|
assert_eq!(data0.next(), Some(0xA7));
|
||||||
|
assert_eq!(data1.next(), Some(0xA7));
|
||||||
|
assert_eq!(data0.next(), Some(b'a'));
|
||||||
|
assert_eq!(data1.next(), Some(b'a'));
|
||||||
|
assert_eq!(data0.next(), Some(b'i'));
|
||||||
|
assert_eq!(data1.next(), Some(b'i'));
|
||||||
|
assert_eq!(data0.next(), Some(b's'));
|
||||||
|
assert_eq!(data1.next(), Some(b's'));
|
||||||
|
assert_eq!(data0.next(), None);
|
||||||
|
assert_eq!(data1.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sized_iter_double_ended() {
|
fn test_sized_iter_double_ended() {
|
||||||
|
|
|
@ -138,8 +138,9 @@ impl<const N: usize> SizedStr<N> {
|
||||||
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let i = e.valid_up_to();
|
let i = e.valid_up_to();
|
||||||
|
let c = data[i];
|
||||||
|
|
||||||
return Err(StringError::BadUtf8(Utf8Error { value: data[i], index: i }));
|
return Err(StringError::BadUtf8(Utf8Error { value: c, index: i }));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -310,15 +311,22 @@ impl<const N: usize> SizedStr<N> {
|
||||||
(buf, len)
|
(buf, len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Deconstructs the string into a fixed-size byte slice.
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn into_bytes(self) -> SizedSlice<u8, N> {
|
||||||
|
let Self(v) = self;
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
/// Converts the fixed-size string into a boxed string slice.
|
/// Converts the fixed-size string into a boxed string slice.
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn into_boxed_str(self) -> Box<str> {
|
pub fn into_boxed_str(self) -> Box<str> {
|
||||||
let Self(vec) = self;
|
let Self(v) = self;
|
||||||
|
unsafe { alloc::str::from_boxed_utf8_unchecked(v.into_boxed_slice()) }
|
||||||
unsafe { alloc::str::from_boxed_utf8_unchecked(vec.into_boxed_slice()) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts the fixed-size string into a dynamic string.
|
/// Converts the fixed-size string into a dynamic string.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "bzipper_benchmarks"
|
name = "bzipper_benchmarks"
|
||||||
version = "0.10.1"
|
version = "0.11.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "bZipper benchmarks."
|
description = "bZipper benchmarks."
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ homepage.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bzipper = { path = "../bzipper", version = "0.10.1" }
|
bzipper = { path = "../bzipper", version = "0.11.0" }
|
||||||
|
|
||||||
bincode = "1.3.3"
|
bincode = "1.3.3"
|
||||||
ciborium = "0.2.2"
|
ciborium = "0.2.2"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "bzipper_macros"
|
name = "bzipper_macros"
|
||||||
version = "0.10.1"
|
version = "0.11.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
documentation = "https://docs.rs/bzipper_macros/"
|
documentation = "https://docs.rs/bzipper_macros/"
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
#![doc(html_logo_url = "https://gitlab.com/bjoernager/bzipper/-/raw/master/doc-icon.svg")]
|
#![doc(html_logo_url = "https://gitlab.com/bjoernager/bzipper/-/raw/master/doc-icon.svg")]
|
||||||
|
|
||||||
//! This crate implements procedural macros for [`bzipper`](https://crates.io/crates/bzipper/).
|
//! This crate implements procedural macros for [`bZipper`](https://crates.io/crates/bzipper/).
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue