Clean up code; Again make 'bool' decoding fallible; Bring back 'BoolDecodeError' error; Add 'BadChar' and 'NonBool' variants to 'GenericDecodeError'; Fix 'GenericDecodeError' not implementing 'From<CharDecodeError>'; Implement 'From<BoolDecodeError>' for 'GenericDecodeError'; Fix '<Option as Decode>::Error' not being a form of 'EnumDecodeError'; Remove 'string' and 'vec' modules (including contents); Remove 'string' and 'vec' macros; Update readme; Fix '<{Option, Result, SocketAddr} as Encode>::Error' not being forms of 'EnumEncodeError';
This commit is contained in:
parent
40ff018433
commit
ffb19c5a97
26 changed files with 231 additions and 2370 deletions
14
CHANGELOG.md
14
CHANGELOG.md
|
@ -3,6 +3,20 @@
|
||||||
This is the changelog of [Oct](https://crates.io/crates/oct/).
|
This is the changelog of [Oct](https://crates.io/crates/oct/).
|
||||||
See `README.md` for more information.
|
See `README.md` for more information.
|
||||||
|
|
||||||
|
## 0.22.0
|
||||||
|
|
||||||
|
* Clean up code
|
||||||
|
* Again make `bool` decoding fallible
|
||||||
|
* Bring back `BoolDecodeError` error
|
||||||
|
* Add `BadChar` and `NonBool` variants to `GenericDecodeError`
|
||||||
|
* Fix `GenericDecodeError` not implementing `From<CharDecodeError>`
|
||||||
|
* Implement `From<BoolDecodeError>` for `GenericDecodeError`
|
||||||
|
* Fix `<Option as Decode>::Error` not being a form of `EnumDecodeError`
|
||||||
|
* Remove `string` and `vec` modules (including contents)
|
||||||
|
* Remove `string` and `vec` macros
|
||||||
|
* Update readme
|
||||||
|
* Fix `<{Option, Result, SocketAddr} as Encode>::Error` not being forms of `EnumEncodeError`
|
||||||
|
|
||||||
## 0.21.2
|
## 0.21.2
|
||||||
|
|
||||||
* Update tests
|
* Update tests
|
||||||
|
|
|
@ -9,7 +9,7 @@ members = ["oct", "oct-benchmarks", "oct-macros"]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.21.2"
|
version = "0.22.0"
|
||||||
authors = ["Gabriel Bjørnager Jensen"]
|
authors = ["Gabriel Bjørnager Jensen"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://mandelbrot.dk/bjoernager/oct/"
|
repository = "https://mandelbrot.dk/bjoernager/oct/"
|
||||||
|
|
|
@ -197,7 +197,7 @@ The nightly toolchain is therefore always required when rendering them or or run
|
||||||
Oct does not accept source code contributions at the moment.
|
Oct does not accept source code contributions at the moment.
|
||||||
This is a personal choice by the maintainer and may be undone in the future.
|
This is a personal choice by the maintainer and may be undone in the future.
|
||||||
|
|
||||||
Do however feel free to open an issue on [GitLab](https://gitlab.com/bjoernager/oct/issues/) or [GitHub](https://github.com/bjoernager/oct/issues/) if you feel the need to express any concerns over the project.
|
Do however feel free to open an issue on [GitLab](https://gitlab.com/bjoernager/oct/issues/), on [GitHub](https://github.com/bjoernager/oct/issues/), or on [`mandelbrot.dk`](https://mandelbrot.dk/bjoernager/oct/issues/) (if a member) if you feel the need to express any concerns over the project.
|
||||||
|
|
||||||
## Copyright & Licence
|
## Copyright & Licence
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ readme.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
oct = { path = "../oct", version = "0.21", features = ["proc-macro"]}
|
oct = { path = "../oct", version = "0.22", features = ["proc-macro"]}
|
||||||
|
|
||||||
bincode = "2.0"
|
bincode = "2.0"
|
||||||
rand = "0.9"
|
rand = "0.9"
|
||||||
|
|
|
@ -775,7 +775,7 @@ fn main() {
|
||||||
let mut stream = Input::new(&buf);
|
let mut stream = Input::new(&buf);
|
||||||
|
|
||||||
for _ in 0x0..VALUE_COUNT {
|
for _ in 0x0..VALUE_COUNT {
|
||||||
let Ok(_) = bool::decode(&mut stream);
|
let _ = bool::decode(&mut stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,18 +29,6 @@ pub fn sized_encode_enum(data: DataEnum, repr: Repr) -> TokenStream {
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
const MAX_ENCODED_SIZE: usize = {
|
const MAX_ENCODED_SIZE: usize = ::oct::enum_encoded_size!(#((#(#tys, )*), )*);
|
||||||
let mut total_size = 0x0usize;
|
|
||||||
|
|
||||||
let mut current_size = 0x0usize;
|
|
||||||
|
|
||||||
#(
|
|
||||||
current_size = 0x0 #(+ <#tys as ::oct::encode::SizedEncode>::MAX_ENCODED_SIZE)*;
|
|
||||||
|
|
||||||
if current_size > total_size { total_size = current_size };
|
|
||||||
)*
|
|
||||||
|
|
||||||
total_size
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ categories.workspace = true
|
||||||
all-features = true
|
all-features = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
oct-macros = { path = "../oct-macros", version = "0.21", optional = true}
|
oct-macros = { path = "../oct-macros", version = "0.22", optional = true}
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["alloc", "proc-macro", "std"]
|
default = ["alloc", "proc-macro", "std"]
|
||||||
|
|
|
@ -11,6 +11,7 @@ mod test;
|
||||||
use crate::__cold_path;
|
use crate::__cold_path;
|
||||||
use crate::decode::{DecodeBorrowed, Input};
|
use crate::decode::{DecodeBorrowed, Input};
|
||||||
use crate::error::{
|
use crate::error::{
|
||||||
|
BoolDecodeError,
|
||||||
CharDecodeError,
|
CharDecodeError,
|
||||||
CollectionDecodeError,
|
CollectionDecodeError,
|
||||||
EnumDecodeError,
|
EnumDecodeError,
|
||||||
|
@ -95,7 +96,7 @@ pub trait Decode: Sized {
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// If `input` unexpectedly terminates before a full encoding was read, then this method should panic.
|
/// If `input` unexpectedly terminates before a full encoding was read, then this method should panic.
|
||||||
/// #[track_caller]
|
#[track_caller]
|
||||||
fn decode(input: &mut Input) -> Result<Self, Self::Error>;
|
fn decode(input: &mut Input) -> Result<Self, Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,7 +179,7 @@ impl<T: Decode + Ord> Decode for BinaryHeap<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Decode for bool {
|
impl Decode for bool {
|
||||||
type Error = Infallible;
|
type Error = BoolDecodeError;
|
||||||
|
|
||||||
/// Lossily reinterprets a byte value as a boolean.
|
/// Lossily reinterprets a byte value as a boolean.
|
||||||
///
|
///
|
||||||
|
@ -188,6 +189,11 @@ impl Decode for bool {
|
||||||
fn decode(input: &mut Input) -> Result<Self, Self::Error> {
|
fn decode(input: &mut Input) -> Result<Self, Self::Error> {
|
||||||
let Ok(value) = u8::decode(input);
|
let Ok(value) = u8::decode(input);
|
||||||
|
|
||||||
|
if value > 0x1 {
|
||||||
|
__cold_path();
|
||||||
|
return Err(BoolDecodeError { value });
|
||||||
|
}
|
||||||
|
|
||||||
let this = value != 0x0;
|
let this = value != 0x0;
|
||||||
Ok(this)
|
Ok(this)
|
||||||
}
|
}
|
||||||
|
@ -297,12 +303,12 @@ impl Decode for char {
|
||||||
let Ok(code_point) = u32::decode(input);
|
let Ok(code_point) = u32::decode(input);
|
||||||
|
|
||||||
match code_point {
|
match code_point {
|
||||||
code_point @ (0x0000..=0xD7FF | 0xDE00..=0x10FFFF) => {
|
0x0000..=0xD7FF | 0xDE00..=0x10FFFF => {
|
||||||
let this = unsafe { Self::from_u32_unchecked(code_point) };
|
let this = unsafe { Self::from_u32_unchecked(code_point) };
|
||||||
Ok(this)
|
Ok(this)
|
||||||
},
|
},
|
||||||
|
|
||||||
code_point => {
|
_ => {
|
||||||
__cold_path();
|
__cold_path();
|
||||||
Err(CharDecodeError { code_point })
|
Err(CharDecodeError { code_point })
|
||||||
},
|
},
|
||||||
|
@ -452,15 +458,27 @@ impl Decode for IpAddr {
|
||||||
fn decode(input: &mut Input) -> Result<Self, Self::Error> {
|
fn decode(input: &mut Input) -> Result<Self, Self::Error> {
|
||||||
let Ok(discriminant) = u8::decode(input);
|
let Ok(discriminant) = u8::decode(input);
|
||||||
|
|
||||||
let this = match discriminant {
|
match discriminant {
|
||||||
0x4 => Self::V4(Decode::decode(input).unwrap()),
|
0x4 => {
|
||||||
0x6 => Self::V6(Decode::decode(input).unwrap()),
|
let Ok(addr) = Decode::decode(input);
|
||||||
|
|
||||||
value => return Err(EnumDecodeError::UnassignedDiscriminant(value))
|
|
||||||
};
|
|
||||||
|
|
||||||
|
let this = Self::V4(addr);
|
||||||
Ok(this)
|
Ok(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
0x6 => {
|
||||||
|
let Ok(addr) = Decode::decode(input);
|
||||||
|
|
||||||
|
let this = Self::V6(addr);
|
||||||
|
Ok(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
value => {
|
||||||
|
__cold_path();
|
||||||
|
Err(EnumDecodeError::UnassignedDiscriminant(value))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Decode for Ipv4Addr {
|
impl Decode for Ipv4Addr {
|
||||||
|
@ -540,16 +558,19 @@ impl<T: Decode> Decode for Mutex<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Decode> Decode for Option<T> {
|
impl<T: Decode> Decode for Option<T> {
|
||||||
type Error = T::Error;
|
type Error = EnumDecodeError<bool, <bool as Decode>::Error, T::Error>;
|
||||||
|
|
||||||
#[expect(clippy::if_then_some_else_none)] // ???
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn decode(input: &mut Input) -> Result<Self, Self::Error> {
|
fn decode(input: &mut Input) -> Result<Self, Self::Error> {
|
||||||
let Ok(sign) = bool::decode(input);
|
let sign = bool::decode(input)
|
||||||
|
.map_err(EnumDecodeError::InvalidDiscriminant)?;
|
||||||
|
|
||||||
let this = if sign {
|
let this = if sign {
|
||||||
Some(Decode::decode(input)?)
|
let value = Decode::decode(input)
|
||||||
|
.map_err(EnumDecodeError::BadField)?;
|
||||||
|
|
||||||
|
Some(value)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -700,7 +721,8 @@ where
|
||||||
#[inline]
|
#[inline]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn decode(input: &mut Input) -> Result<Self, Self::Error> {
|
fn decode(input: &mut Input) -> Result<Self, Self::Error> {
|
||||||
let Ok(sign) = bool::decode(input);
|
let sign = bool::decode(input)
|
||||||
|
.map_err(EnumDecodeError::InvalidDiscriminant)?;
|
||||||
|
|
||||||
let this = if sign {
|
let this = if sign {
|
||||||
let value = Decode::decode(input)
|
let value = Decode::decode(input)
|
||||||
|
@ -756,8 +778,19 @@ impl Decode for SocketAddr {
|
||||||
let Ok(discriminant) = u8::decode(input);
|
let Ok(discriminant) = u8::decode(input);
|
||||||
|
|
||||||
match discriminant {
|
match discriminant {
|
||||||
0x4 => Ok(Self::V4(Decode::decode(input).unwrap())),
|
0x4 => {
|
||||||
0x6 => Ok(Self::V6(Decode::decode(input).unwrap())),
|
let Ok(addr) = Decode::decode(input);
|
||||||
|
|
||||||
|
let this = Self::V4(addr);
|
||||||
|
Ok(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
0x6 => {
|
||||||
|
let Ok(addr) = Decode::decode(input);
|
||||||
|
|
||||||
|
let this = Self::V6(addr);
|
||||||
|
Ok(this)
|
||||||
|
}
|
||||||
|
|
||||||
value => {
|
value => {
|
||||||
__cold_path();
|
__cold_path();
|
||||||
|
|
|
@ -12,8 +12,6 @@ use core::char;
|
||||||
use oct::decode::{Decode, Input};
|
use oct::decode::{Decode, Input};
|
||||||
use oct::encode::{Encode, SizedEncode};
|
use oct::encode::{Encode, SizedEncode};
|
||||||
use oct::error::EnumDecodeError;
|
use oct::error::EnumDecodeError;
|
||||||
use oct::string::String;
|
|
||||||
use oct::vec::Vec;
|
|
||||||
|
|
||||||
macro_rules! test {
|
macro_rules! test {
|
||||||
{
|
{
|
||||||
|
@ -22,7 +20,7 @@ macro_rules! test {
|
||||||
$($data:expr => $value:expr),+$(,)?
|
$($data:expr => $value:expr),+$(,)?
|
||||||
}$(,)?
|
}$(,)?
|
||||||
)*
|
)*
|
||||||
} => {{
|
} => {
|
||||||
$($({
|
$($({
|
||||||
let data: &[u8] = &$data;
|
let data: &[u8] = &$data;
|
||||||
|
|
||||||
|
@ -33,7 +31,7 @@ macro_rules! test {
|
||||||
|
|
||||||
::std::assert_eq!(left, right);
|
::std::assert_eq!(left, right);
|
||||||
})*)*
|
})*)*
|
||||||
}};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -95,14 +93,6 @@ fn test_decode() {
|
||||||
[0x00, 0x00] => Ok(Ok(())),
|
[0x00, 0x00] => Ok(Ok(())),
|
||||||
[0x01, 0x7F] => Ok(Err(i8::MAX)),
|
[0x01, 0x7F] => Ok(Err(i8::MAX)),
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec<u16, 0x6> {
|
|
||||||
[0x02, 0x00, 0xBB, 0xAA, 0xDD, 0xCC] => Ok(Vec::copy_from_slice(&[0xAA_BB, 0xCC_DD]).unwrap()),
|
|
||||||
}
|
|
||||||
|
|
||||||
String<0x6> {
|
|
||||||
[0x06, 0x00, 0xE6, 0x97, 0xA5, 0xE6, 0x9C, 0xAC] => Ok(String::new("\u{65E5}\u{672C}").unwrap()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,14 +237,3 @@ fn test_decode_derive_custom_error() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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();
|
|
||||||
}
|
|
||||||
|
|
|
@ -34,8 +34,6 @@ use std::ffi::{OsStr, OsString};
|
||||||
/// This trait in the form <code>DecodeBorrowed<[\[T\]]></code> is not implemented for [`[T; N]`](array) due to the fact that arrays do not encode their length, instead having it hard-coded into the type, thus rendering their scheme incompatible with that of slices.
|
/// This trait in the form <code>DecodeBorrowed<[\[T\]]></code> is not implemented for [`[T; N]`](array) due to the fact that arrays do not encode their length, instead having it hard-coded into the type, thus rendering their scheme incompatible with that of slices.
|
||||||
///
|
///
|
||||||
/// [\[T\]]: slice
|
/// [\[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"))]
|
#[doc(alias("DeserialiseBorrowed", "DeserializeBorrowed"))]
|
||||||
pub trait DecodeBorrowed<B: ?Sized>: Borrow<B> + Decode { }
|
pub trait DecodeBorrowed<B: ?Sized>: Borrow<B> + Decode { }
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
mod test;
|
mod test;
|
||||||
|
|
||||||
|
use crate::__cold_path;
|
||||||
use crate::encode::Output;
|
use crate::encode::Output;
|
||||||
use crate::error::{
|
use crate::error::{
|
||||||
CollectionEncodeError,
|
CollectionEncodeError,
|
||||||
|
@ -458,12 +459,12 @@ impl Encode for IpAddr {
|
||||||
match *self {
|
match *self {
|
||||||
Self::V4(ref addr) => {
|
Self::V4(ref addr) => {
|
||||||
0x4u8.encode(output).map_err(EnumEncodeError::BadDiscriminant)?;
|
0x4u8.encode(output).map_err(EnumEncodeError::BadDiscriminant)?;
|
||||||
addr.encode(output).map_err(EnumEncodeError::BadField)?;
|
let Ok(()) = addr.encode(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::V6(ref addr) => {
|
Self::V6(ref addr) => {
|
||||||
0x6u8.encode(output).map_err(EnumEncodeError::BadDiscriminant)?;
|
0x6u8.encode(output).map_err(EnumEncodeError::BadDiscriminant)?;
|
||||||
addr.encode(output).map_err(EnumEncodeError::BadField)?;
|
let Ok(()) = addr.encode(output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,11 +503,17 @@ impl Encode for isize {
|
||||||
#[inline]
|
#[inline]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn encode(&self, output: &mut Output) -> Result<(), Self::Error> {
|
fn encode(&self, output: &mut Output) -> Result<(), Self::Error> {
|
||||||
let value = i16::try_from(*self)
|
let value = *self;
|
||||||
.map_err(|_| IsizeEncodeError(*self))?;
|
|
||||||
|
|
||||||
|
if value <= i16::MAX as Self {
|
||||||
|
let value = value as i16;
|
||||||
let Ok(()) = value.encode(output);
|
let Ok(()) = value.encode(output);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
} else {
|
||||||
|
__cold_path();
|
||||||
|
Err(IsizeEncodeError(value))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -571,7 +578,7 @@ impl<T: Encode + ?Sized> Encode for Mutex<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Encode> Encode for Option<T> {
|
impl<T: Encode> Encode for Option<T> {
|
||||||
type Error = T::Error;
|
type Error = EnumEncodeError<<bool as Encode>::Error, T::Error>;
|
||||||
|
|
||||||
/// Encodes a sign denoting the optional's variant.
|
/// Encodes a sign denoting the optional's variant.
|
||||||
/// This is `false` for `None` instances and `true` for `Some` instances.
|
/// This is `false` for `None` instances and `true` for `Some` instances.
|
||||||
|
@ -581,17 +588,12 @@ impl<T: Encode> Encode for Option<T> {
|
||||||
fn encode(&self, output: &mut Output) -> Result<(), Self::Error> {
|
fn encode(&self, output: &mut Output) -> Result<(), Self::Error> {
|
||||||
match *self {
|
match *self {
|
||||||
None => {
|
None => {
|
||||||
false
|
false.encode(output).map_err(EnumEncodeError::BadDiscriminant)?;
|
||||||
.encode(output)
|
|
||||||
.map_err::<Self::Error, _>(|_v| unreachable!())?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(ref v) => {
|
Some(ref v) => {
|
||||||
true
|
true.encode(output).map_err(EnumEncodeError::BadDiscriminant)?;
|
||||||
.encode(output)
|
v.encode(output).map_err(EnumEncodeError::BadField)?;
|
||||||
.map_err::<Self::Error, _>(|_v| unreachable!())?;
|
|
||||||
|
|
||||||
v.encode(output)?;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -759,12 +761,8 @@ impl<T: Encode + ?Sized> Encode for RefCell<T> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn encode(&self, output: &mut Output) -> Result<(), Self::Error> {
|
fn encode(&self, output: &mut Output) -> Result<(), Self::Error> {
|
||||||
let value = self
|
let value = self.try_borrow().map_err(RefCellEncodeError::BadBorrow)?;
|
||||||
.try_borrow()
|
T::encode(&value, output).map_err(RefCellEncodeError::BadValue)?;
|
||||||
.map_err(RefCellEncodeError::BadBorrow)?;
|
|
||||||
|
|
||||||
T::encode(&value, output)
|
|
||||||
.map_err(RefCellEncodeError::BadValue)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -775,7 +773,7 @@ where
|
||||||
T: Encode<Error = Err>,
|
T: Encode<Error = Err>,
|
||||||
E: Encode<Error: Into<Err>>,
|
E: Encode<Error: Into<Err>>,
|
||||||
{
|
{
|
||||||
type Error = Err;
|
type Error = EnumEncodeError<<bool as Encode>::Error, Err>;
|
||||||
|
|
||||||
/// Encodes a sign denoting the result's variant.
|
/// Encodes a sign denoting the result's variant.
|
||||||
/// This is `false` for `Ok` instances and `true` for `Err` instances.
|
/// This is `false` for `Ok` instances and `true` for `Err` instances.
|
||||||
|
@ -784,20 +782,18 @@ where
|
||||||
#[inline]
|
#[inline]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn encode(&self, output: &mut Output) -> Result<(), Self::Error> {
|
fn encode(&self, output: &mut Output) -> Result<(), Self::Error> {
|
||||||
// The sign here is `false` for `Ok` objects and
|
|
||||||
// `true` for `Err` objects.
|
|
||||||
|
|
||||||
match *self {
|
match *self {
|
||||||
Ok(ref v) => {
|
Ok(ref v) => {
|
||||||
let Ok(()) = false.encode(output);
|
false.encode(output).map_err(EnumEncodeError::BadDiscriminant)?;
|
||||||
|
v.encode(output).map_err(EnumEncodeError::BadField)?;
|
||||||
v.encode(output)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(ref e) => {
|
Err(ref e) => {
|
||||||
let Ok(()) = true.encode(output);
|
true.encode(output).map_err(EnumEncodeError::BadDiscriminant)?;
|
||||||
|
|
||||||
e.encode(output).map_err(Into::into)?;
|
e.encode(output)
|
||||||
|
.map_err(Into::<Err>::into)
|
||||||
|
.map_err(EnumEncodeError::BadField)?;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -831,7 +827,7 @@ impl<T: Encode> Encode for Saturating<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encode for SocketAddr {
|
impl Encode for SocketAddr {
|
||||||
type Error = Infallible;
|
type Error = EnumEncodeError<<u8 as Encode>::Error, Infallible>;
|
||||||
|
|
||||||
/// This implementation encoded as discriminant denoting the IP version of the address (i.e. `4` for IPv4 and `6` for IPv6).
|
/// 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`]).
|
/// This is then followed by the respective address' own encoding (either [`SocketAddrV4`] or [`SocketAddrV6`]).
|
||||||
|
@ -842,12 +838,12 @@ impl Encode for SocketAddr {
|
||||||
|
|
||||||
match *self {
|
match *self {
|
||||||
Self::V4(ref addr) => {
|
Self::V4(ref addr) => {
|
||||||
let Ok(()) = 0x4u8.encode(output);
|
0x4u8.encode(output).map_err(EnumEncodeError::BadDiscriminant)?;
|
||||||
let Ok(()) = addr.encode(output);
|
let Ok(()) = addr.encode(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::V6(ref addr) => {
|
Self::V6(ref addr) => {
|
||||||
let Ok(()) = 0x6u8.encode(output);
|
0x6u8.encode(output).map_err(EnumEncodeError::BadDiscriminant)?;
|
||||||
let Ok(()) = addr.encode(output);
|
let Ok(()) = addr.encode(output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -979,11 +975,17 @@ impl Encode for usize {
|
||||||
#[inline]
|
#[inline]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn encode(&self, output: &mut Output) -> Result<(), Self::Error> {
|
fn encode(&self, output: &mut Output) -> Result<(), Self::Error> {
|
||||||
let value = u16::try_from(*self)
|
let value = *self;
|
||||||
.map_err(|_| UsizeEncodeError(*self))?;
|
|
||||||
|
|
||||||
|
if value <= u16::MAX as Self {
|
||||||
|
let value = value as u16;
|
||||||
let Ok(()) = value.encode(output);
|
let Ok(()) = value.encode(output);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
} else {
|
||||||
|
__cold_path();
|
||||||
|
Err(UsizeEncodeError(value))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
mod test;
|
mod test;
|
||||||
|
|
||||||
|
use crate::enum_encoded_size;
|
||||||
use crate::encode::Encode;
|
use crate::encode::Encode;
|
||||||
|
|
||||||
use core::cell::{Cell, LazyCell, RefCell, UnsafeCell};
|
use core::cell::{Cell, LazyCell, RefCell, UnsafeCell};
|
||||||
|
@ -231,14 +232,14 @@ impl<T: SizedEncode + ?Sized> SizedEncode for RefCell<T> {
|
||||||
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
const MAX_ENCODED_SIZE: usize = T::MAX_ENCODED_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, E, Err> SizedEncode for core::result::Result<T, E>
|
impl<T, E, Err> SizedEncode for Result<T, E>
|
||||||
where
|
where
|
||||||
T: SizedEncode + Encode<Error = Err>,
|
T: Encode<Error = Err> + SizedEncode,
|
||||||
E: SizedEncode + Encode<Error: Into<Err>>,
|
E: Encode<Error: Into<Err>> + SizedEncode,
|
||||||
{
|
{
|
||||||
const MAX_ENCODED_SIZE: usize =
|
const MAX_ENCODED_SIZE: usize =
|
||||||
bool::MAX_ENCODED_SIZE
|
bool::MAX_ENCODED_SIZE
|
||||||
+ if size_of::<T>() > size_of::<E>() { size_of::<T>() } else { size_of::<E>() };
|
+ enum_encoded_size!((T), (E));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
|
@ -308,7 +309,7 @@ macro_rules! impl_tuple {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
impl<$($tys, )* E> ::oct::encode::SizedEncode for ($($tys, )*)
|
impl<$($tys, )* E> ::oct::encode::SizedEncode for ($($tys, )*)
|
||||||
where
|
where
|
||||||
$($tys: SizedEncode + Encode<Error = E>, )* {
|
$($tys: Encode<Error = E> + SizedEncode, )* {
|
||||||
const MAX_ENCODED_SIZE: usize = 0x0 $(+ <$tys as ::oct::encode::SizedEncode>::MAX_ENCODED_SIZE)*;
|
const MAX_ENCODED_SIZE: usize = 0x0 $(+ <$tys as ::oct::encode::SizedEncode>::MAX_ENCODED_SIZE)*;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
41
oct/src/error/bool_decode_error.rs
Normal file
41
oct/src/error/bool_decode_error.rs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
// 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::convert::Infallible;
|
||||||
|
use core::error::Error;
|
||||||
|
use core::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
|
/// A boolean could not be decoded.
|
||||||
|
///
|
||||||
|
/// Boolean values are boolean.
|
||||||
|
/// This error type is emitted when <code><[bool] as [Decode]>::[decode]</code> encounters a non-boolean octet.
|
||||||
|
///
|
||||||
|
/// [Decode]: [crate::decode::Decode]
|
||||||
|
/// [decode]: [crate::decode::Decode::decode]
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
#[must_use]
|
||||||
|
pub struct BoolDecodeError {
|
||||||
|
/// The non-boolean value.
|
||||||
|
pub value: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for BoolDecodeError {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(f, "value {:#02X} is not boolean", self.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for BoolDecodeError { }
|
||||||
|
|
||||||
|
impl From<Infallible> for BoolDecodeError {
|
||||||
|
#[inline(always)]
|
||||||
|
fn from(_value: Infallible) -> Self {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
use crate::{PrimDiscriminant, PrimRepr};
|
use crate::{PrimDiscriminant, PrimRepr};
|
||||||
use crate::error::{
|
use crate::error::{
|
||||||
|
BoolDecodeError,
|
||||||
|
CharDecodeError,
|
||||||
CollectionDecodeError,
|
CollectionDecodeError,
|
||||||
EnumDecodeError,
|
EnumDecodeError,
|
||||||
ItemDecodeError,
|
ItemDecodeError,
|
||||||
|
@ -31,9 +33,20 @@ use crate::error::SystemTimeDecodeError;
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub enum GenericDecodeError {
|
pub enum GenericDecodeError {
|
||||||
|
/// A character was not a valid UTF-32 unit.
|
||||||
|
BadChar(CharDecodeError),
|
||||||
|
|
||||||
/// A string contained a non-UTF-8 sequence.
|
/// A string contained a non-UTF-8 sequence.
|
||||||
BadString(Utf8Error),
|
BadString(Utf8Error),
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||||
|
/// The [`SystemTime`](std::time::SystemTime) type was too narrow.
|
||||||
|
NarrowSystemTime(SystemTimeDecodeError),
|
||||||
|
|
||||||
|
/// A boolean was not boolean.
|
||||||
|
NonBool(BoolDecodeError),
|
||||||
|
|
||||||
/// A non-zero integer was null.
|
/// A non-zero integer was null.
|
||||||
NullInteger(NonZeroDecodeError),
|
NullInteger(NonZeroDecodeError),
|
||||||
|
|
||||||
|
@ -44,20 +57,26 @@ pub enum GenericDecodeError {
|
||||||
///
|
///
|
||||||
/// The contained value denotes the raw, numerical value of the discriminant.
|
/// The contained value denotes the raw, numerical value of the discriminant.
|
||||||
UnassignedDiscriminant(PrimDiscriminant),
|
UnassignedDiscriminant(PrimDiscriminant),
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
|
||||||
/// The [`SystemTime`](std::time::SystemTime) type was too narrow.
|
|
||||||
NarrowSystemTime(SystemTimeDecodeError),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for GenericDecodeError {
|
impl Display for GenericDecodeError {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
|
Self::BadChar(ref e)
|
||||||
|
=> write!(f, "{e}"),
|
||||||
|
|
||||||
Self::BadString(ref e)
|
Self::BadString(ref e)
|
||||||
=> write!(f, "{e}"),
|
=> write!(f, "{e}"),
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||||
|
Self::NarrowSystemTime(ref e)
|
||||||
|
=> write!(f, "{e}"),
|
||||||
|
|
||||||
|
Self::NonBool(ref e)
|
||||||
|
=> write!(f, "{e}"),
|
||||||
|
|
||||||
Self::NullInteger(ref e)
|
Self::NullInteger(ref e)
|
||||||
=> write!(f, "{e}"),
|
=> write!(f, "{e}"),
|
||||||
|
|
||||||
|
@ -66,11 +85,6 @@ impl Display for GenericDecodeError {
|
||||||
|
|
||||||
Self::UnassignedDiscriminant(value)
|
Self::UnassignedDiscriminant(value)
|
||||||
=> write!(f, "discriminant value `{value:#X} has not been assigned"),
|
=> write!(f, "discriminant value `{value:#X} has not been assigned"),
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
|
||||||
Self::NarrowSystemTime(ref e)
|
|
||||||
=> write!(f, "{e}"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,21 +93,39 @@ impl Error for GenericDecodeError {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
match *self {
|
match *self {
|
||||||
|
Self::BadChar(ref e) => Some(e),
|
||||||
|
|
||||||
Self::BadString(ref e) => Some(e),
|
Self::BadString(ref e) => Some(e),
|
||||||
|
|
||||||
Self::NullInteger(ref e) => Some(e),
|
|
||||||
|
|
||||||
Self::SmallBuffer(ref e) => Some(e),
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
#[cfg_attr(doc, doc(cfg(feature = "std")))]
|
||||||
Self::NarrowSystemTime(ref e) => Some(e),
|
Self::NarrowSystemTime(ref e) => Some(e),
|
||||||
|
|
||||||
|
Self::NonBool(ref e) => Some(e),
|
||||||
|
|
||||||
|
Self::NullInteger(ref e) => Some(e),
|
||||||
|
|
||||||
|
Self::SmallBuffer(ref e) => Some(e),
|
||||||
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<BoolDecodeError> for GenericDecodeError {
|
||||||
|
#[inline(always)]
|
||||||
|
fn from(value: BoolDecodeError) -> Self {
|
||||||
|
Self::NonBool(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CharDecodeError> for GenericDecodeError {
|
||||||
|
#[inline(always)]
|
||||||
|
fn from(value: CharDecodeError) -> Self {
|
||||||
|
Self::BadChar(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<L, I> From<CollectionDecodeError<L, I>> for GenericDecodeError
|
impl<L, I> From<CollectionDecodeError<L, I>> for GenericDecodeError
|
||||||
where
|
where
|
||||||
L: Into<Self>,
|
L: Into<Self>,
|
||||||
|
|
|
@ -12,12 +12,8 @@ use core::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
/// A collection buffer was too small to contain all of its elements.
|
/// A collection buffer was too small to contain all of its elements.
|
||||||
///
|
///
|
||||||
/// Some data types use a statically-sized buffer whilst still allowing for partial usage of this buffer (e.g. [`Vec`](crate::vec::Vec)).
|
/// Some data types use a statically-sized buffer whilst still allowing for partial usage of this buffer.
|
||||||
/// These types should return this error in cases where their size limit has exceeded.
|
/// These types should return this error in cases where their size limit has exceeded.
|
||||||
///
|
|
||||||
/// Taking `Vec` as an example, it encodes its actual length before encoding its elements.
|
|
||||||
/// It is allowed for any smaller-sized `Vec` instance to decode a larger-sized encoding **if** the actual length is still within bounds.
|
|
||||||
/// Otherwise, this error type is used to denote the error state.
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct LengthError {
|
pub struct LengthError {
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
//! This module defines the error types used by Oct.
|
//! This module defines the error types used by Oct.
|
||||||
//! All of these types define (at least conditionally) the [`Error`](core::error::Error) trait.
|
//! All of these types define (at least conditionally) the [`Error`](core::error::Error) trait.
|
||||||
|
|
||||||
|
mod bool_decode_error;
|
||||||
mod char_decode_error;
|
mod char_decode_error;
|
||||||
mod collection_decode_error;
|
mod collection_decode_error;
|
||||||
mod collection_encode_error;
|
mod collection_encode_error;
|
||||||
|
@ -30,6 +31,7 @@ mod system_time_decode_error;
|
||||||
mod usize_encode_error;
|
mod usize_encode_error;
|
||||||
mod utf8_error;
|
mod utf8_error;
|
||||||
|
|
||||||
|
pub use bool_decode_error::BoolDecodeError;
|
||||||
pub use char_decode_error::CharDecodeError;
|
pub use char_decode_error::CharDecodeError;
|
||||||
pub use collection_decode_error::CollectionDecodeError;
|
pub use collection_decode_error::CollectionDecodeError;
|
||||||
pub use collection_encode_error::CollectionEncodeError;
|
pub use collection_encode_error::CollectionEncodeError;
|
||||||
|
|
|
@ -209,7 +209,7 @@
|
||||||
//! Oct does not accept source code contributions at the moment.
|
//! Oct does not accept source code contributions at the moment.
|
||||||
//! This is a personal choice by the maintainer and may be undone in the future.
|
//! This is a personal choice by the maintainer and may be undone in the future.
|
||||||
//!
|
//!
|
||||||
//! Do however feel free to open an issue on [GitLab](https://gitlab.com/bjoernager/oct/issues/) or [GitHub](https://github.com/bjoernager/oct/issues/) if you feel the need to express any concerns over the project.
|
//! Do however feel free to open an issue on [GitLab](https://gitlab.com/bjoernager/oct/issues/), on [GitHub](https://github.com/bjoernager/oct/issues/), or on [`mandelbrot.dk`](https://mandelbrot.dk/bjoernager/oct/issues/) (if a member) if you feel the need to express any concerns over the project.
|
||||||
//!
|
//!
|
||||||
//! # Copyright & Licence
|
//! # Copyright & Licence
|
||||||
//!
|
//!
|
||||||
|
@ -245,8 +245,6 @@ mod prim_repr;
|
||||||
pub mod decode;
|
pub mod decode;
|
||||||
pub mod encode;
|
pub mod encode;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod string;
|
|
||||||
pub mod vec;
|
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub mod slot;
|
pub mod slot;
|
||||||
|
@ -256,45 +254,32 @@ pub use prim_repr::PrimRepr;
|
||||||
|
|
||||||
pub(crate) use prim_repr::SealedPrimRepr;
|
pub(crate) use prim_repr::SealedPrimRepr;
|
||||||
|
|
||||||
/// Directly constructs a [`Vec`](crate::vec::Vec) object.
|
#[doc(hidden)]
|
||||||
///
|
|
||||||
/// This macro tests at compile-time whether the provided data can fit into the inferred length.
|
|
||||||
/// Compilation will fail if this is not the case.
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! vec {
|
macro_rules! enum_encoded_size {
|
||||||
[$value:literal; $len:expr] => {{
|
($(($($tys:ty),+$(,)?)),*$(,)?) => {
|
||||||
const { $crate::vec::Vec::new([$value; $len]) }
|
const {
|
||||||
}};
|
#[allow(unused)]
|
||||||
|
use ::oct::encode::SizedEncode;
|
||||||
|
|
||||||
[$($value:expr),+ $(,)?] => {{
|
let mut total_size = 0x0usize;
|
||||||
const { $crate::vec::Vec::new([$($value,)*]) }
|
|
||||||
}};
|
|
||||||
|
|
||||||
[] => {{
|
$({
|
||||||
const { $crate::vec::Vec::new([]) }
|
let current_size = 0x0usize $(+ <$tys as SizedEncode>::MAX_ENCODED_SIZE)*;
|
||||||
}}
|
|
||||||
|
if current_size > total_size {
|
||||||
|
total_size = current_size;
|
||||||
}
|
}
|
||||||
|
})*
|
||||||
|
|
||||||
|
total_size
|
||||||
/// Directly constructs a [`String`](crate::string::String) object.
|
}
|
||||||
///
|
};
|
||||||
/// This macro tests at compile-time whether the string literal can fit into the inferred length.
|
|
||||||
/// Compilation will fail if this is not the case.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! string {
|
|
||||||
($s:expr) => {{
|
|
||||||
const { $crate::string::__string($s) }
|
|
||||||
}};
|
|
||||||
|
|
||||||
() => {{
|
|
||||||
const { $crate::string::__string("") }
|
|
||||||
}};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Stable equivalent of `core::hint::
|
// NOTE: Stable equivalent of `core::hint::
|
||||||
// cold_path`. Should not be used directly, but is
|
// cold_path`. Should not be used directly, but is
|
||||||
// used in derive macros.
|
// used in derive macros.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[inline(always)]
|
|
||||||
#[cold]
|
#[cold]
|
||||||
pub const fn __cold_path() { }
|
pub const fn __cold_path() { }
|
||||||
|
|
|
@ -32,23 +32,25 @@ use core::slice::{self, SliceIndex};
|
||||||
/// Create a slot for holding a `Request` enumeration:
|
/// Create a slot for holding a `Request` enumeration:
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use oct::string;
|
|
||||||
/// use oct::encode::{Encode, Output, SizedEncode};
|
/// use oct::encode::{Encode, Output, SizedEncode};
|
||||||
/// use oct::slot::Slot;
|
/// use oct::slot::Slot;
|
||||||
/// use oct::string::String;
|
/// use std::string::String;
|
||||||
///
|
///
|
||||||
|
/// #[non_exhaustive]
|
||||||
/// #[derive(Debug, Encode)]
|
/// #[derive(Debug, Encode)]
|
||||||
/// enum Request {
|
/// enum Request {
|
||||||
/// Join { username: String<0x40> },
|
/// Join { username: String },
|
||||||
///
|
///
|
||||||
/// Quit { username: String<0x40> },
|
/// Quit { username: String },
|
||||||
///
|
///
|
||||||
/// SendMessage { message: String<0x80> },
|
/// SendMessage { message: String },
|
||||||
|
///
|
||||||
|
/// // ...
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// let mut buf = Slot::with_capacity(0x100);
|
/// let mut buf = Slot::with_capacity(0x100);
|
||||||
///
|
///
|
||||||
/// buf.write(&Request::Join { username: string!("epsiloneridani") }).unwrap();
|
/// buf.write(&Request::Join { username: "epsiloneridani".into() }).unwrap();
|
||||||
/// assert_eq!(buf.as_slice(), b"\0\0\x0E\0epsiloneridani");
|
/// assert_eq!(buf.as_slice(), b"\0\0\x0E\0epsiloneridani");
|
||||||
///
|
///
|
||||||
/// // ...
|
/// // ...
|
||||||
|
|
|
@ -1,13 +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/>.
|
|
||||||
|
|
||||||
//! String container.
|
|
||||||
|
|
||||||
mod string;
|
|
||||||
|
|
||||||
pub use string::{__string, String};
|
|
|
@ -1,738 +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 test;
|
|
||||||
|
|
||||||
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::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};
|
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
use {
|
|
||||||
alloc::borrow::Cow,
|
|
||||||
alloc::boxed::Box,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use {
|
|
||||||
std::ffi::OsStr,
|
|
||||||
std::net::ToSocketAddrs,
|
|
||||||
std::path::Path,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// String container with maximum length.
|
|
||||||
///
|
|
||||||
/// 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 the standard library's [`Vec`](alloc::vec::Vec).
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// All instances of this type have the same size if the value of `N` is also the same.
|
|
||||||
/// Therefore, the following four strings have -- despite their different contents -- the same total size.
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// use oct::string::String;
|
|
||||||
/// use std::str::FromStr;
|
|
||||||
///
|
|
||||||
/// 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(&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, Copy)]
|
|
||||||
pub struct String<const N: usize> {
|
|
||||||
len: usize,
|
|
||||||
buf: [u8; N],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<const N: usize> String<N> {
|
|
||||||
/// Constructs a new, size-constrained string.
|
|
||||||
///
|
|
||||||
/// 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]
|
|
||||||
#[track_caller]
|
|
||||||
pub const fn new(s: &str) -> Result<Self, LengthError> {
|
|
||||||
let len = s.len();
|
|
||||||
if len > N {
|
|
||||||
return Err(LengthError {
|
|
||||||
remaining: N,
|
|
||||||
count: len,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut buf = [0x00; N];
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let src = s.as_ptr();
|
|
||||||
let dst = buf.as_mut_ptr();
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unsafely constructs a new, size-constrained string.
|
|
||||||
///
|
|
||||||
/// See also [`new`](Self::new) for a safe alternative to this constructor.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// If the internal buffer cannot contain the entirety of `s`, then the call to this constructor will result in undefined behaviour.
|
|
||||||
#[inline]
|
|
||||||
#[track_caller]
|
|
||||||
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();
|
|
||||||
|
|
||||||
copy_nonoverlapping(src, dst, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SAFETY: `s` 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]
|
|
||||||
#[track_caller]
|
|
||||||
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]
|
|
||||||
#[track_caller]
|
|
||||||
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() as *const [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]
|
|
||||||
#[track_caller]
|
|
||||||
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.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.len() == 0x0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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_char_boundary(&self, index: usize) -> bool {
|
|
||||||
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 as *const [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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<const N: usize> Debug for String<N> {
|
|
||||||
#[inline]
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
Debug::fmt(self.as_str(), f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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]
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
Display::fmt(self.as_str(), f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
let mut buf = [0x00; N];
|
|
||||||
let mut len = 0x0;
|
|
||||||
|
|
||||||
for c in iter {
|
|
||||||
let rem = N - len;
|
|
||||||
let req = c.len_utf8();
|
|
||||||
|
|
||||||
if rem < req { break }
|
|
||||||
|
|
||||||
let start = len;
|
|
||||||
let end = start + req;
|
|
||||||
|
|
||||||
c.encode_utf8(&mut buf[start..end]);
|
|
||||||
|
|
||||||
len += req;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SAFETY: All octets are initialised and come from
|
|
||||||
// `char::encode_utf8`.
|
|
||||||
unsafe { Self::from_raw_parts(buf, len) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<const N: usize> FromStr for 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) {
|
|
||||||
self.as_str().hash(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I: SliceIndex<str>, const N: usize> Index<I> for String<N> {
|
|
||||||
type Output = I::Output;
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
#[track_caller]
|
|
||||||
fn index(&self, index: I) -> &Self::Output {
|
|
||||||
self.get(index).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I: SliceIndex<str>, const N: usize> IndexMut<I> for String<N> {
|
|
||||||
#[inline(always)]
|
|
||||||
#[track_caller]
|
|
||||||
fn index_mut(&mut self, index: I) -> &mut Self::Output {
|
|
||||||
self.get_mut(index).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<const N: usize> Ord for String<N> {
|
|
||||||
#[inline(always)]
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
|
||||||
(**self).cmp(&**other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<const N: usize, const M: usize> PartialEq<String<M>> for String<N> {
|
|
||||||
#[inline(always)]
|
|
||||||
fn eq(&self, other: &String<M>) -> bool {
|
|
||||||
**self == **other
|
|
||||||
}
|
|
||||||
|
|
||||||
#[expect(clippy::partialeq_ne_impl)]
|
|
||||||
#[inline(always)]
|
|
||||||
fn ne(&self, other: &String<M>) -> bool {
|
|
||||||
**self != **other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
|
|
||||||
impl<const N: usize> PartialEq<Cow<'_, str>> for String<N> {
|
|
||||||
#[inline(always)]
|
|
||||||
fn eq(&self, other: &Cow<str>) -> bool {
|
|
||||||
**self == **other
|
|
||||||
}
|
|
||||||
|
|
||||||
#[expect(clippy::partialeq_ne_impl)]
|
|
||||||
#[inline(always)]
|
|
||||||
fn ne(&self, other: &Cow<str>) -> bool {
|
|
||||||
**self != **other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<const N: usize> PartialEq<str> for String<N> {
|
|
||||||
#[inline(always)]
|
|
||||||
fn eq(&self, other: &str) -> bool {
|
|
||||||
**self == *other
|
|
||||||
}
|
|
||||||
|
|
||||||
#[expect(clippy::partialeq_ne_impl)]
|
|
||||||
#[inline(always)]
|
|
||||||
fn ne(&self, other: &str) -> bool {
|
|
||||||
**self == *other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<const N: usize> PartialEq<&str> for String<N> {
|
|
||||||
#[inline(always)]
|
|
||||||
fn eq(&self, other: &&str) -> bool {
|
|
||||||
**self == **other
|
|
||||||
}
|
|
||||||
|
|
||||||
#[expect(clippy::partialeq_ne_impl)]
|
|
||||||
#[inline(always)]
|
|
||||||
fn ne(&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 == **other
|
|
||||||
}
|
|
||||||
|
|
||||||
#[expect(clippy::partialeq_ne_impl)]
|
|
||||||
#[inline(always)]
|
|
||||||
fn ne(&self, other: &alloc::string::String) -> bool {
|
|
||||||
*self != **other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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).partial_cmp(other)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn lt(&self, other: &String<M>) -> bool {
|
|
||||||
**self < **other
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn le(&self, other: &String<M>) -> bool {
|
|
||||||
**self <= **other
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn gt(&self, other: &String<M>) -> bool {
|
|
||||||
**self > **other
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn ge(&self, other: &String<M>) -> bool {
|
|
||||||
**self >= **other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: This function is used by the `str` macro
|
|
||||||
// to circumvent itself using code which may be
|
|
||||||
// forbidden by the macro user's lints. While this
|
|
||||||
// function is sound, please do not call it direct-
|
|
||||||
// ly. It is not a breaking change if it is re-
|
|
||||||
// moved.
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
#[track_caller]
|
|
||||||
pub const fn __string<const N: usize>(s: &'static str) -> String<N> {
|
|
||||||
assert!(s.len() <= N, "cannot construct string from literal that is longer");
|
|
||||||
|
|
||||||
// SAFETY: `s` has been tested to not contain more
|
|
||||||
// than `N` octets.
|
|
||||||
unsafe { String::new_unchecked(s) }
|
|
||||||
}
|
|
|
@ -1,94 +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/>.
|
|
||||||
|
|
||||||
#![cfg(test)]
|
|
||||||
|
|
||||||
use core::cmp::Ordering;
|
|
||||||
use oct::string;
|
|
||||||
use oct::error::Utf8Error;
|
|
||||||
use oct::string::String;
|
|
||||||
use oct::vec::Vec;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_str_macro() {
|
|
||||||
let s0: String<0x3> = string!("Oct");
|
|
||||||
let s1: String<0x3> = string!("Oct");
|
|
||||||
|
|
||||||
assert_eq!(s0, s1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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> = string!("Hello there!");
|
|
||||||
let s1: String<0x12> = string!("MEIN_GRO\u{1E9E}_GOTT");
|
|
||||||
let s2: String<0x05> = string!("Hello");
|
|
||||||
|
|
||||||
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::copy_from_slice(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,373 +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 test;
|
|
||||||
|
|
||||||
use crate::vec::{clone_to_uninit_in_range, Vec};
|
|
||||||
|
|
||||||
use core::iter::{DoubleEndedIterator, ExactSizeIterator, FusedIterator};
|
|
||||||
use core::mem::MaybeUninit;
|
|
||||||
use core::ptr::drop_in_place;
|
|
||||||
use core::slice;
|
|
||||||
|
|
||||||
/// Owning iterator to a vector.
|
|
||||||
///
|
|
||||||
/// This type is exclusively used by the deconstruction of the [`Vec`] type, as per <code>[IntoIterator]::[into_iter](IntoIterator::into_iter)</code>.
|
|
||||||
#[must_use]
|
|
||||||
pub struct IntoIter<T, const N: usize> {
|
|
||||||
/// The cursor position in the buffer.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// This field may not be greater than [`isize::MAX`].
|
|
||||||
pos: usize,
|
|
||||||
|
|
||||||
/// The length; the count remaining of alive elements remaining in the buffer.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// This field may not be greater than [`isize::MAX`].
|
|
||||||
len: usize,
|
|
||||||
|
|
||||||
/// The internal buffer.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// We must **always** guarantee that all objects in the range `pos..pos + len` are initialised and alive.
|
|
||||||
/// One may therefore assume that interpreting these objects as such is valid.
|
|
||||||
buf: [MaybeUninit<T>; N],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> IntoIter<T, N> {
|
|
||||||
/// Constructs a new, owning iterator to a vector.
|
|
||||||
#[inline(always)]
|
|
||||||
#[track_caller]
|
|
||||||
pub(super) fn new(v: Vec<T, N>) -> Self {
|
|
||||||
let (buf, len) = v.into_raw_parts();
|
|
||||||
|
|
||||||
let pos = Default::default();
|
|
||||||
|
|
||||||
Self { pos, len, buf }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Incremenets the cursor position by a specified amount.
|
|
||||||
///
|
|
||||||
/// The caller is responsible for dropping the skipped elements.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The iterator `self` may not contain less than `count` elements.
|
|
||||||
#[inline(always)]
|
|
||||||
#[track_caller]
|
|
||||||
unsafe fn advance_by_unchecked(&mut self, count: usize) {
|
|
||||||
debug_assert!(count <= self.len);
|
|
||||||
|
|
||||||
// SAFETY: The caller guarantees that at least
|
|
||||||
// `count` element are remaining.
|
|
||||||
self.len = unsafe { self.len.unchecked_sub(count) };
|
|
||||||
|
|
||||||
// SAFETY: It is not invalid for us to go one-past-
|
|
||||||
// the-end or exceed `isize::MAX`; `len` guarantees
|
|
||||||
// that this counter will not be used as a pointer
|
|
||||||
// offset in such cases.
|
|
||||||
self.pos = unsafe { self.pos.unchecked_add(count) };
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Decrements the length counter by a specified amount.
|
|
||||||
///
|
|
||||||
/// The caller is responsible for dropping the skipped elements.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The iterator `self` may not contain less than `count` elements.
|
|
||||||
#[inline(always)]
|
|
||||||
#[track_caller]
|
|
||||||
unsafe fn advance_back_by_unchecked(&mut self, count: usize) {
|
|
||||||
debug_assert!(count <= self.len);
|
|
||||||
|
|
||||||
// SAFETY: The caller guarantees that at least
|
|
||||||
// `count` element are remaining.
|
|
||||||
self.len = unsafe { self.len.unchecked_sub(count) };
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets a pointer to the current element.
|
|
||||||
///
|
|
||||||
/// If the iterator `self` is currently empty, then the returned pointer will instead be dangling.
|
|
||||||
#[inline(always)]
|
|
||||||
fn as_ptr(&self) -> *const T {
|
|
||||||
let pos = self.pos;
|
|
||||||
|
|
||||||
// SAFETY: `MaybeUninit<T>` is transparent to `T`.
|
|
||||||
let base = self.buf.as_ptr() as *const T;
|
|
||||||
|
|
||||||
unsafe { base.add(pos) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets a mutable pointer to the current element.
|
|
||||||
///
|
|
||||||
/// If the iterator `self` is currently empty, then the returned pointer will instead be dangling.
|
|
||||||
#[inline(always)]
|
|
||||||
fn as_mut_ptr(&mut self) -> *mut T {
|
|
||||||
let pos = self.pos;
|
|
||||||
|
|
||||||
// SAFETY: `MaybeUninit<T>` is transparent to `T`.
|
|
||||||
let base = self.buf.as_mut_ptr() as *mut T;
|
|
||||||
|
|
||||||
unsafe { base.add(pos) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets a slice of the remaining elements.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn as_slice(&self) -> &[T] {
|
|
||||||
let len = self.len;
|
|
||||||
let ptr = self.as_ptr();
|
|
||||||
|
|
||||||
unsafe { slice::from_raw_parts(ptr, len) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets a mutable slice of the remaining elements.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn as_mut_slice(&mut self) -> &mut [T] {
|
|
||||||
let len = self.len;
|
|
||||||
let ptr = self.as_mut_ptr();
|
|
||||||
|
|
||||||
unsafe { slice::from_raw_parts_mut(ptr, len) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> AsMut<[T]> for IntoIter<T, N> {
|
|
||||||
#[inline(always)]
|
|
||||||
fn as_mut(&mut self) -> &mut [T] {
|
|
||||||
self.as_mut_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> AsRef<[T]> for IntoIter<T, N> {
|
|
||||||
#[inline(always)]
|
|
||||||
fn as_ref(&self) -> &[T] {
|
|
||||||
self.as_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Clone, const N: usize> Clone for IntoIter<T, N> {
|
|
||||||
#[inline]
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
let pos = self.pos;
|
|
||||||
let len = self.len;
|
|
||||||
let mut buf = [const { MaybeUninit::uninit() }; N];
|
|
||||||
|
|
||||||
// SAFETY: `MaybeUninit<T>` is transparent to `T`.
|
|
||||||
let src = self.buf.as_ptr() as *const T;
|
|
||||||
|
|
||||||
let dst = buf.as_mut_ptr() as *mut T;
|
|
||||||
|
|
||||||
let start = pos;
|
|
||||||
let end = pos + len;
|
|
||||||
|
|
||||||
// SAFETY: The range
|
|
||||||
//
|
|
||||||
// pos..pos + len
|
|
||||||
//
|
|
||||||
// defines in and of itself the bounds of valid
|
|
||||||
// elements.
|
|
||||||
unsafe { clone_to_uninit_in_range(src, dst, start..end) };
|
|
||||||
|
|
||||||
// SAFETY: The buffer has been initialised in the
|
|
||||||
// provided range - which does not extend beyond
|
|
||||||
// bounds.
|
|
||||||
Self { pos, len, buf }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> Default for IntoIter<T, N> {
|
|
||||||
#[inline(always)]
|
|
||||||
fn default() -> Self {
|
|
||||||
Vec::default().into_iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> DoubleEndedIterator for IntoIter<T, N> {
|
|
||||||
#[inline]
|
|
||||||
fn next_back(&mut self) -> Option<Self::Item> {
|
|
||||||
// Test whether the iterator is empty.
|
|
||||||
|
|
||||||
if self.len == 0x0 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take the next value.
|
|
||||||
|
|
||||||
// Get a pointer to the next item.
|
|
||||||
|
|
||||||
// SAFETY: `self.pos` is guaranteed to always be
|
|
||||||
// within bounds. `self.pos + self.len` is guaran-
|
|
||||||
// teed one-past-the-end index.
|
|
||||||
let index = self.pos + self.len - 0x1;
|
|
||||||
|
|
||||||
// SAFETY: `MaybeUninit<T>` is transparent to `T`.
|
|
||||||
let base = self.buf.as_ptr() as *const T;
|
|
||||||
|
|
||||||
let item = unsafe { base.add(index) };
|
|
||||||
|
|
||||||
// Read the item value.
|
|
||||||
|
|
||||||
let value = unsafe { item.read() };
|
|
||||||
|
|
||||||
// Update counters, **not** including the position.
|
|
||||||
|
|
||||||
// SAFETY: We have tested that at least one element
|
|
||||||
// remains.
|
|
||||||
unsafe { self.advance_back_by_unchecked(0x1) };
|
|
||||||
|
|
||||||
Some(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> Drop for IntoIter<T, N> {
|
|
||||||
#[inline(always)]
|
|
||||||
fn drop(&mut self) {
|
|
||||||
// Drop every element that hasn't been consumed.
|
|
||||||
|
|
||||||
let remaining = self.as_mut_slice();
|
|
||||||
unsafe { drop_in_place(remaining) };
|
|
||||||
|
|
||||||
// We do not need to ensure that `self` is in a
|
|
||||||
// valid state after this call to `drop`.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> ExactSizeIterator for IntoIter<T, N> {
|
|
||||||
#[inline(always)]
|
|
||||||
fn len(&self) -> usize {
|
|
||||||
self.len
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> FusedIterator for IntoIter<T, N> { }
|
|
||||||
|
|
||||||
impl<T, const N: usize> Iterator for IntoIter<T, N> {
|
|
||||||
type Item = T;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
// Test whether the iterator is empty.
|
|
||||||
|
|
||||||
if self.len == 0x0 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take the next value.
|
|
||||||
|
|
||||||
// Get a pointer to the next item.
|
|
||||||
|
|
||||||
// SAFETY: `self.pos` is guaranteed to always be
|
|
||||||
// within bounds.
|
|
||||||
let index = self.pos;
|
|
||||||
|
|
||||||
// SAFETY: `MaybeUninit<T>` is transparent to `T`.
|
|
||||||
let base = self.buf.as_ptr() as *const T;
|
|
||||||
|
|
||||||
let item = unsafe { base.add(index) };
|
|
||||||
|
|
||||||
// Read the item value.
|
|
||||||
|
|
||||||
// SAFETY: We guarantee that all items in the range
|
|
||||||
//
|
|
||||||
// self.pos..self.pos + self.len
|
|
||||||
//
|
|
||||||
// are alive (and initialised).
|
|
||||||
let value = unsafe { item.read() };
|
|
||||||
|
|
||||||
// Update counters.
|
|
||||||
|
|
||||||
// SAFETY: We have tested that at least one element
|
|
||||||
// remains.
|
|
||||||
unsafe { self.advance_by_unchecked(0x1) };
|
|
||||||
|
|
||||||
// Return the item value.
|
|
||||||
|
|
||||||
Some(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn nth(&mut self, n: usize) -> Option<Self::Item> {
|
|
||||||
// Test whether the iterator is empty.
|
|
||||||
|
|
||||||
if n >= self.len {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the indices of the involved items.
|
|
||||||
|
|
||||||
let drop_start = self.pos;
|
|
||||||
let drop_end = drop_start + n;
|
|
||||||
|
|
||||||
let index = drop_end;
|
|
||||||
|
|
||||||
// SAFETY: `MaybeUninit<T>` is transparent to `T`.
|
|
||||||
let base = self.buf.as_mut_ptr() as *mut T;
|
|
||||||
|
|
||||||
// Drop each skipped element.
|
|
||||||
|
|
||||||
for i in drop_start..drop_end {
|
|
||||||
let item = unsafe { base.add(i) };
|
|
||||||
|
|
||||||
// SAFETY: We guarantee that all items in the range
|
|
||||||
//
|
|
||||||
// self.pos..self.pos + self.len
|
|
||||||
//
|
|
||||||
// are alive (and initialised).
|
|
||||||
unsafe { drop_in_place(item) };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the final value.
|
|
||||||
|
|
||||||
let item = unsafe { base.add(index) };
|
|
||||||
let value = unsafe { item.read() };
|
|
||||||
|
|
||||||
// Update counters.
|
|
||||||
|
|
||||||
// SAFETY: This cannot overflow as `n` has been
|
|
||||||
// tested to be less than `self.len`, which itself
|
|
||||||
// cannot be greater than `isize::MAX`.
|
|
||||||
let count = unsafe { n.unchecked_add(0x1) };
|
|
||||||
|
|
||||||
// SAFETY: We have tested that there are at least
|
|
||||||
// `count` elements left.
|
|
||||||
unsafe { self.advance_by_unchecked(count) };
|
|
||||||
|
|
||||||
// Return the value.
|
|
||||||
|
|
||||||
Some(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
||||||
let len = self.len;
|
|
||||||
(len, Some(len))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn count(self) -> usize {
|
|
||||||
// NOTE: Elements are dropped automatically.
|
|
||||||
self.len
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn is_sorted(self) -> bool
|
|
||||||
where
|
|
||||||
T: PartialOrd,
|
|
||||||
{
|
|
||||||
self.as_slice().is_sorted()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn is_sorted_by<F: FnMut(&Self::Item, &Self::Item) -> bool>(self, compare: F) -> bool {
|
|
||||||
self.as_slice().is_sorted_by(compare)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,86 +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/>.
|
|
||||||
|
|
||||||
#![cfg(test)]
|
|
||||||
|
|
||||||
use oct::string;
|
|
||||||
use oct::string::String;
|
|
||||||
use oct::vec::Vec;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec_iter_clone() {
|
|
||||||
let data: String::<0xF> = string!("fran\u{00E7}aise");
|
|
||||||
|
|
||||||
assert_eq!(data.len(), 0xA);
|
|
||||||
assert_eq!(data.as_bytes(), b"fran\xC3\xA7aise");
|
|
||||||
|
|
||||||
let mut iter0 = data.into_bytes().into_iter();
|
|
||||||
|
|
||||||
assert_eq!(iter0.len(), 0xA);
|
|
||||||
assert_eq!(iter0.as_slice(), b"fran\xC3\xA7aise");
|
|
||||||
|
|
||||||
assert_eq!(iter0.nth(0x3), Some(b'n'));
|
|
||||||
|
|
||||||
assert_eq!(iter0.len(), 0x6);
|
|
||||||
assert_eq!(iter0.as_slice(), b"\xC3\xA7aise");
|
|
||||||
|
|
||||||
let mut iter1 = iter0.clone();
|
|
||||||
|
|
||||||
assert_eq!(iter1.len(), 0x6);
|
|
||||||
assert_eq!(iter1.as_slice(), b"\xC3\xA7aise");
|
|
||||||
|
|
||||||
assert_eq!(iter0.next(), Some(0xC3));
|
|
||||||
assert_eq!(iter1.next(), Some(0xC3));
|
|
||||||
assert_eq!(iter0.next(), Some(0xA7));
|
|
||||||
assert_eq!(iter1.next(), Some(0xA7));
|
|
||||||
assert_eq!(iter0.next(), Some(b'a'));
|
|
||||||
assert_eq!(iter1.next(), Some(b'a'));
|
|
||||||
assert_eq!(iter0.next(), Some(b'i'));
|
|
||||||
assert_eq!(iter1.next(), Some(b'i'));
|
|
||||||
assert_eq!(iter0.next(), Some(b's'));
|
|
||||||
assert_eq!(iter1.next(), Some(b's'));
|
|
||||||
assert_eq!(iter0.next(), Some(b'e'));
|
|
||||||
assert_eq!(iter1.next(), Some(b'e'));
|
|
||||||
assert_eq!(iter0.next(), None);
|
|
||||||
assert_eq!(iter1.next(), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec_iter_double_ended() {
|
|
||||||
let data = Vec::from([
|
|
||||||
'H', 'E', 'L', 'L', 'O', ' ', 'W', 'O',
|
|
||||||
'R', 'L', 'D',
|
|
||||||
]);
|
|
||||||
|
|
||||||
let mut data = data.into_iter();
|
|
||||||
|
|
||||||
assert_eq!(data.next(), Some('H'));
|
|
||||||
assert_eq!(data.next_back(), Some('D'));
|
|
||||||
assert_eq!(data.next(), Some('E'));
|
|
||||||
assert_eq!(data.next_back(), Some('L'));
|
|
||||||
assert_eq!(data.next(), Some('L'));
|
|
||||||
assert_eq!(data.next_back(), Some('R'));
|
|
||||||
assert_eq!(data.next(), Some('L'));
|
|
||||||
assert_eq!(data.next_back(), Some('O'));
|
|
||||||
assert_eq!(data.next(), Some('O'));
|
|
||||||
assert_eq!(data.next_back(), Some('W'));
|
|
||||||
assert_eq!(data.next(), Some(' '));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec_new() {
|
|
||||||
assert_eq!(
|
|
||||||
Vec::<u32, 0x6>::new([0x6]),
|
|
||||||
[0x6].as_slice(),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Vec::<u32, 0x6>::new([0x6; 0x6]),
|
|
||||||
[0x6, 0x6, 0x6, 0x6, 0x6, 0x6].as_slice(),
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,40 +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/>.
|
|
||||||
|
|
||||||
//! Vector container and iterators.
|
|
||||||
|
|
||||||
mod into_iter;
|
|
||||||
mod vec;
|
|
||||||
|
|
||||||
pub use into_iter::IntoIter;
|
|
||||||
pub use vec::Vec;
|
|
||||||
|
|
||||||
use core::ops::Range;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
unsafe fn clone_to_uninit_in_range<T: Clone>(src: *const T, dst: *mut T, range: Range<usize>) {
|
|
||||||
// SAFETY: The caller guarantees a valid range.
|
|
||||||
for i in range.start..range.end {
|
|
||||||
// SAFETY: We guarantee that all items in the range
|
|
||||||
//
|
|
||||||
// 0x0..self.len
|
|
||||||
//
|
|
||||||
// are alive (and initialised).
|
|
||||||
let src_item = unsafe { &*src.add(i) };
|
|
||||||
|
|
||||||
let dst_item = unsafe { dst.add(i) };
|
|
||||||
|
|
||||||
// Clone the item value.
|
|
||||||
|
|
||||||
let value = src_item.clone();
|
|
||||||
|
|
||||||
// Write the item value.
|
|
||||||
|
|
||||||
unsafe { dst_item.write(value) };
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,773 +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 test;
|
|
||||||
|
|
||||||
use crate::decode::{self, Decode, DecodeBorrowed};
|
|
||||||
use crate::encode::{self, Encode, SizedEncode};
|
|
||||||
use crate::error::{CollectionDecodeError, ItemDecodeError, LengthError};
|
|
||||||
use crate::vec::{clone_to_uninit_in_range, IntoIter};
|
|
||||||
|
|
||||||
use core::borrow::{Borrow, BorrowMut};
|
|
||||||
use core::cmp::Ordering;
|
|
||||||
use core::fmt::{self, Debug, Formatter};
|
|
||||||
use core::hash::{Hash, Hasher};
|
|
||||||
use core::mem::{offset_of, ManuallyDrop, MaybeUninit};
|
|
||||||
use core::ops::{Deref, DerefMut, Index, IndexMut};
|
|
||||||
use core::ptr::{copy_nonoverlapping, drop_in_place};
|
|
||||||
use core::slice::{self, SliceIndex};
|
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
use alloc::boxed::Box;
|
|
||||||
|
|
||||||
/// Vector container with maximum length.
|
|
||||||
///
|
|
||||||
/// This type is intended as a [sized-encodable](crate::encode::SizedEncode) and [decodable](crate::decode::Decode) alternative to slices and the standard library's [`Vec`](alloc::vec::Vec) (in cases where [arrays](array) may not be wanted).
|
|
||||||
///
|
|
||||||
/// Note that this type is immutable in the sense that it does **not** define methods like `push` and `pop`, in contrast to the standard library type.
|
|
||||||
///
|
|
||||||
/// See [`String`](crate::string::String) for an equivalent alternative to the standard library's [`String`](alloc::string::String).
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// All instances of this type with the same `T` and `N` also have the exact same layout:
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// use oct::vec::Vec;
|
|
||||||
///
|
|
||||||
/// 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(&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> {
|
|
||||||
len: usize,
|
|
||||||
buf: [MaybeUninit<T>; N],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> Vec<T, N> {
|
|
||||||
/// Constructs a new slice from existing data.
|
|
||||||
///
|
|
||||||
/// This constructor takes inherits an existing array and converts it to a vector.
|
|
||||||
/// If the provided array's length `M` is greater than `N`, then this function will panic at compile-time.
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
#[track_caller]
|
|
||||||
pub const fn new<const M: usize>(data: [T; M]) -> Self {
|
|
||||||
const { assert!(M <= N, "cannot construct vector from array that is longer") };
|
|
||||||
|
|
||||||
let data = ManuallyDrop::new(data);
|
|
||||||
|
|
||||||
let len = M;
|
|
||||||
|
|
||||||
let buf = if N == M {
|
|
||||||
// Reuse the existing buffer.
|
|
||||||
|
|
||||||
// SAFETY: `ManuallyDrop<[T; N]>` and
|
|
||||||
// `[MaybeUninit<T>; N]` are both transparent to
|
|
||||||
// `[T; N]`. `data` can also be forgotten as its
|
|
||||||
// constructor is supressed. Also remember that
|
|
||||||
// `N` and `M` have been tested to be equal.
|
|
||||||
let ptr = &raw const data as *const [MaybeUninit<T>; N];
|
|
||||||
|
|
||||||
unsafe { ptr.read() }
|
|
||||||
} else {
|
|
||||||
// Reallocate the buffer to `N` elements.
|
|
||||||
|
|
||||||
// SAFETY: `ManuallyDrop<T>` is transparent to `T`.
|
|
||||||
let data = unsafe { &*(&raw const data as *const [T; M]) };
|
|
||||||
|
|
||||||
let mut buf = [const { MaybeUninit::uninit() }; N];
|
|
||||||
|
|
||||||
let src = data.as_ptr();
|
|
||||||
let dst = buf.as_mut_ptr() as *mut T;
|
|
||||||
|
|
||||||
unsafe { copy_nonoverlapping(src, dst, len) };
|
|
||||||
|
|
||||||
buf
|
|
||||||
};
|
|
||||||
|
|
||||||
// SAFETY: We have checked that the length is with-
|
|
||||||
// in bounds.
|
|
||||||
unsafe { Self::from_raw_parts(buf, len) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Copies elements from a slice into a new vector.
|
|
||||||
///
|
|
||||||
/// This constructor copies the raw representation of `data` into a new allocation of `N` elements.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// If `self` cannot contain the entirety of `data`, then this method will return an error.
|
|
||||||
#[inline]
|
|
||||||
#[track_caller]
|
|
||||||
pub const fn copy_from_slice(data: &[T]) -> Result<Self, LengthError>
|
|
||||||
where
|
|
||||||
T: Copy,
|
|
||||||
{
|
|
||||||
let len = data.len();
|
|
||||||
|
|
||||||
if len > N {
|
|
||||||
return Err(LengthError {
|
|
||||||
remaining: N,
|
|
||||||
count: len,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// SAFETY: We have checked that the length is with-
|
|
||||||
// in bounds.
|
|
||||||
let this = unsafe { Self::copy_from_slice_unchecked(data) };
|
|
||||||
Ok(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a new slice from existing data without checking bounds.
|
|
||||||
///
|
|
||||||
/// For a safe version of this constructor, see [`copy_from_slice`](Self::copy_from_slice).
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The entirety of `data` must be able to fit into an array of `N` elements.
|
|
||||||
#[inline(always)]
|
|
||||||
#[track_caller]
|
|
||||||
pub const unsafe fn copy_from_slice_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() as *mut T;
|
|
||||||
|
|
||||||
// SAFETY: `T` implements `Copy`.
|
|
||||||
copy_nonoverlapping(src, dst, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SAFETY: The relevant elements have been ini-
|
|
||||||
// tialised, and `len` is not greater than `N` - as
|
|
||||||
// guaranteed by the caller.
|
|
||||||
unsafe { Self::from_raw_parts(buf, len) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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]
|
|
||||||
#[track_caller]
|
|
||||||
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 }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates a new vector referencing the elements of `self`.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `len` may not be greater than `N`.
|
|
||||||
#[inline]
|
|
||||||
#[must_use]
|
|
||||||
const unsafe fn each_ptr(data: *mut [MaybeUninit<T>; N], len: usize) -> [MaybeUninit<*mut T>; N] {
|
|
||||||
// SAFETY: Note that this function does not take
|
|
||||||
// any `&_` reference, so the caller can safely
|
|
||||||
// reinterpret the returned array as `&mut _` muta-
|
|
||||||
// ble references.
|
|
||||||
|
|
||||||
debug_assert!(len <= N);
|
|
||||||
|
|
||||||
let mut buf = [const { MaybeUninit::uninit() }; N];
|
|
||||||
|
|
||||||
let src_base = data as *mut T;
|
|
||||||
let dst_base = buf.as_mut_ptr();
|
|
||||||
|
|
||||||
let mut i = 0x0;
|
|
||||||
while i < len {
|
|
||||||
// SAFETY: `i` will always be less than `self.len`
|
|
||||||
// and thereby within bounds as that variable is
|
|
||||||
// also always less than `N`.
|
|
||||||
let slot = unsafe { &mut *dst_base.add(i) };
|
|
||||||
|
|
||||||
let value = unsafe { src_base.add(i) };
|
|
||||||
|
|
||||||
slot.write(value);
|
|
||||||
|
|
||||||
i += 0x1;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates a new vector referencing the elements of `self`.
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
pub const fn each_ref(&self) -> Vec<&T, N> {
|
|
||||||
let buf = (&raw const self.buf).cast_mut();
|
|
||||||
|
|
||||||
let len = self.len;
|
|
||||||
let buf = unsafe { Self::each_ptr(buf, len) };
|
|
||||||
|
|
||||||
// SAFETY: every relavent pointer (i.e. the first
|
|
||||||
// `len`) have been initialised as valid refer-
|
|
||||||
// ences. The destructor of the original `buf` does
|
|
||||||
// also not show any substantial effects.
|
|
||||||
let buf = unsafe { (&raw const buf as *const [MaybeUninit<&T>; N]).read() };
|
|
||||||
|
|
||||||
unsafe { Vec::from_raw_parts(buf, len) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates a new vector mutably-referencing the elements of `self`.
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
pub const fn each_mut(&mut self) -> Vec<&mut T, N> {
|
|
||||||
let len = self.len;
|
|
||||||
let buf = unsafe { Self::each_ptr(&raw mut self.buf, len) };
|
|
||||||
|
|
||||||
// SAFETY: every relavent pointer (i.e. the first
|
|
||||||
// `len`) have been initialised as valid refer-
|
|
||||||
// ences. The destructor of the original `buf` does
|
|
||||||
// also not show any substantial effects.
|
|
||||||
let buf = unsafe { (&raw const buf as *const [MaybeUninit<&mut T>; N]).read() };
|
|
||||||
|
|
||||||
unsafe { Vec::from_raw_parts(buf, len) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unsafely sets the length of the vector.
|
|
||||||
///
|
|
||||||
/// The provided length is **not** tested in any way.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The new length `len` may not be larger than `N`.
|
|
||||||
///
|
|
||||||
/// It is only valid to enlarge vectors if `T` supports being in a purely uninitialised state
|
|
||||||
/// Such is permitted by e.g. [`MaybeUninit`].
|
|
||||||
#[inline(always)]
|
|
||||||
#[track_caller]
|
|
||||||
pub const unsafe fn set_len(&mut self, len: usize) {
|
|
||||||
debug_assert!(len <= N, "cannot set length past bounds");
|
|
||||||
|
|
||||||
// SAFETY: The caller guarantees that `len` is not
|
|
||||||
// freaky.
|
|
||||||
self.len = len
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the length of the vector.
|
|
||||||
///
|
|
||||||
/// 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 {
|
|
||||||
self.len
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if the vector is empty, i.e. no elements are recorded.
|
|
||||||
///
|
|
||||||
/// Note that the internal buffer may still contain objects that have been "shadowed" by setting a smaller length with [`len`](Self::len).
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
pub const fn is_empty(&self) -> bool {
|
|
||||||
self.len() == 0x0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 {
|
|
||||||
// SAFETY: `MaybeUninit<T>` is transparent to `T`.
|
|
||||||
self.buf.as_ptr() as *const T
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 {
|
|
||||||
// SAFETY: `MaybeUninit<T>` is transparent to `T`.
|
|
||||||
self.buf.as_mut_ptr() as *mut T
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 len = self.len();
|
|
||||||
let ptr = self.as_ptr();
|
|
||||||
|
|
||||||
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 len = self.len();
|
|
||||||
let ptr = self.as_mut_ptr();
|
|
||||||
|
|
||||||
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 len = self.len;
|
|
||||||
|
|
||||||
let this = ManuallyDrop::new(self);
|
|
||||||
|
|
||||||
// FIXME(const-hack): We can't just use drop glue.
|
|
||||||
let buf = {
|
|
||||||
// SAFETY: `ManuallyDrop<T>` is transparent to `T`.
|
|
||||||
let base = &raw const this as *const Self;
|
|
||||||
|
|
||||||
let off = offset_of!(Self, buf);
|
|
||||||
let ptr = unsafe { base.byte_add(off) as *const [MaybeUninit<T>; N] };
|
|
||||||
|
|
||||||
// SAFETY: `this` will not be dropped with its own
|
|
||||||
// destructor, so we can safely move out of it.
|
|
||||||
unsafe { ptr.read() }
|
|
||||||
};
|
|
||||||
|
|
||||||
(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")))]
|
|
||||||
#[inline]
|
|
||||||
#[must_use]
|
|
||||||
#[track_caller]
|
|
||||||
pub fn into_boxed_slice(self) -> Box<[T]> {
|
|
||||||
let (buf, len) = self.into_raw_parts();
|
|
||||||
|
|
||||||
let mut boxed = alloc::vec::Vec::with_capacity(len).into_boxed_slice();
|
|
||||||
|
|
||||||
// SAFETY: `MaybeUninit<T>` is transparent to `T`.
|
|
||||||
let src = buf.as_ptr() as *const T;
|
|
||||||
|
|
||||||
let dst = boxed.as_mut_ptr();
|
|
||||||
|
|
||||||
// SAFETY: `boxed` has been allocated with at least
|
|
||||||
// `len` elements.
|
|
||||||
unsafe { copy_nonoverlapping(src, dst, len) };
|
|
||||||
|
|
||||||
boxed
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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]
|
|
||||||
#[track_caller]
|
|
||||||
pub fn into_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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> AsRef<[T]> for Vec<T, N> {
|
|
||||||
#[inline(always)]
|
|
||||||
fn as_ref(&self) -> &[T] {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> Borrow<[T]> for Vec<T, N> {
|
|
||||||
#[inline(always)]
|
|
||||||
fn borrow(&self) -> &[T] {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> BorrowMut<[T]> for Vec<T, N> {
|
|
||||||
#[inline(always)]
|
|
||||||
fn borrow_mut(&mut self) -> &mut [T] {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Clone, const N: usize> Clone for Vec<T, N> {
|
|
||||||
#[inline]
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
let len = self.len;
|
|
||||||
let mut buf = [const { MaybeUninit::uninit() }; N];
|
|
||||||
|
|
||||||
// SAFETY: `MaybeUninit<T>` is transparent to `T`.
|
|
||||||
let src = self.buf.as_ptr() as *const T;
|
|
||||||
|
|
||||||
let dst = buf.as_mut_ptr() as *mut T;
|
|
||||||
|
|
||||||
// SAFETY: The range
|
|
||||||
//
|
|
||||||
// 0x0..len
|
|
||||||
//
|
|
||||||
// defines in and of itself the bounds of valid
|
|
||||||
// elements.
|
|
||||||
unsafe { clone_to_uninit_in_range(src, dst, 0x0..len) };
|
|
||||||
|
|
||||||
// SAFETY: The buffer has been initialised in the
|
|
||||||
// provided range - which does not extend beyond
|
|
||||||
// bounds.
|
|
||||||
unsafe { Self::from_raw_parts(buf, len) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Debug, const N: usize> Debug for Vec<T, N> {
|
|
||||||
#[inline]
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
Debug::fmt(self.as_slice(), f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 Ok(len) = Decode::decode(input);
|
|
||||||
|
|
||||||
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 {
|
|
||||||
let buf = [const { MaybeUninit::uninit() }; N];
|
|
||||||
|
|
||||||
// SAFETY: The resulting vector is zero lengthed
|
|
||||||
// and does therefore not expose any uninitialised
|
|
||||||
// objects.
|
|
||||||
unsafe { Self::from_raw_parts(buf, Default::default()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
// Drop every element that is currently alive.
|
|
||||||
|
|
||||||
let remaining = self.as_mut_slice() as *mut [T];
|
|
||||||
|
|
||||||
// SAFETY: Mutable references always point to alive
|
|
||||||
// and initialised objects.
|
|
||||||
unsafe { drop_in_place(remaining) };
|
|
||||||
|
|
||||||
// We do not need to ensure that `self` is in a
|
|
||||||
// valid state after this call to `drop`.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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).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 {
|
|
||||||
Self::new(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> FromIterator<T> for Vec<T, N> {
|
|
||||||
#[inline]
|
|
||||||
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
|
|
||||||
let mut len = 0x0;
|
|
||||||
let mut buf = [const { MaybeUninit::<T>::uninit() }; N];
|
|
||||||
|
|
||||||
for (slot, value) in buf.iter_mut().zip(iter) {
|
|
||||||
slot.write(value);
|
|
||||||
|
|
||||||
len += 0x1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drop the remaining elements.
|
|
||||||
|
|
||||||
Self { len, buf }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Hash, const N: usize> Hash for Vec<T, N> {
|
|
||||||
#[inline(always)]
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
(**self).hash(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, I: SliceIndex<[T]>, const N: usize> Index<I> for Vec<T, N> {
|
|
||||||
type Output = I::Output;
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
#[track_caller]
|
|
||||||
fn index(&self, index: I) -> &Self::Output {
|
|
||||||
Index::index(&**self, index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, I: SliceIndex<[T]>, const N: usize> IndexMut<I> for Vec<T, N> {
|
|
||||||
#[inline(always)]
|
|
||||||
#[track_caller]
|
|
||||||
fn index_mut(&mut self, index: I) -> &mut Self::Output {
|
|
||||||
IndexMut::index_mut(&mut **self, index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
IntoIter::new(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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).cmp(&**other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 == **other
|
|
||||||
}
|
|
||||||
|
|
||||||
#[expect(clippy::partialeq_ne_impl)]
|
|
||||||
#[inline(always)]
|
|
||||||
fn ne(&self, other: &Vec<U, M>) -> bool {
|
|
||||||
**self != **other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
#[expect(clippy::partialeq_ne_impl)]
|
|
||||||
#[inline(always)]
|
|
||||||
fn ne(&self, other: &[U; M]) -> bool {
|
|
||||||
**self != *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
|
|
||||||
}
|
|
||||||
|
|
||||||
#[expect(clippy::partialeq_ne_impl)]
|
|
||||||
#[inline(always)]
|
|
||||||
fn ne(&self, other: &[U]) -> bool {
|
|
||||||
**self != *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
|
|
||||||
}
|
|
||||||
|
|
||||||
#[expect(clippy::partialeq_ne_impl)]
|
|
||||||
#[inline(always)]
|
|
||||||
fn ne(&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 == **other
|
|
||||||
}
|
|
||||||
|
|
||||||
#[expect(clippy::partialeq_ne_impl)]
|
|
||||||
#[inline(always)]
|
|
||||||
fn ne(&self, other: &alloc::vec::Vec<U>) -> bool {
|
|
||||||
**self != **other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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).partial_cmp(&**other)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn lt(&self, other: &Vec<T, M>) -> bool {
|
|
||||||
**self < **other
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn le(&self, other: &Vec<T, M>) -> bool {
|
|
||||||
**self <= **other
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn gt(&self, other: &Vec<T, M>) -> bool {
|
|
||||||
**self > **other
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn ge(&self, other: &Vec<T, M>) -> bool {
|
|
||||||
**self >= **other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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::copy_from_slice(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_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 == **other
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,95 +0,0 @@
|
||||||
// Copyright 2024-str2025 Gabriel Bjørnager Jensen.
|
|
||||||
//
|
|
||||||
// This file is part of Oct.
|
|
||||||
//
|
|
||||||
// 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/>.
|
|
||||||
|
|
||||||
#![cfg(test)]
|
|
||||||
|
|
||||||
use oct::vec;
|
|
||||||
use oct::vec::Vec;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec_copy_from_slice() {
|
|
||||||
const DATA: &[i64] = &[
|
|
||||||
-0x67,
|
|
||||||
-0x51,
|
|
||||||
0x07,
|
|
||||||
0x0D,
|
|
||||||
0x14,
|
|
||||||
0x1A,
|
|
||||||
0x1F,
|
|
||||||
0x24,
|
|
||||||
0x2A,
|
|
||||||
0x2F,
|
|
||||||
0x37,
|
|
||||||
];
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Vec::<_, 0xB0>::copy_from_slice(DATA).as_deref(),
|
|
||||||
Ok([-0x67, -0x51, 0x7, 0xD, 0x14, 0x1A, 0x1F, 0x24, 0x2A, 0x2F, 0x37].as_slice()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec_from_array() {
|
|
||||||
const DATA: [i64; 0x6] = [
|
|
||||||
-0x1E,
|
|
||||||
0x0D,
|
|
||||||
0x0F,
|
|
||||||
0x12,
|
|
||||||
0x18,
|
|
||||||
0x1C,
|
|
||||||
];
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Vec::<_, 0x6>::new(DATA),
|
|
||||||
[-0x1E, 0xD, 0xF, 0x12, 0x18, 0x1C],
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Vec::<_, 0x60>::new(DATA),
|
|
||||||
[-0x1E, 0xD, 0xF, 0x12, 0x18, 0x1C],
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Vec::<_, 0x6>::from(DATA),
|
|
||||||
[-0x1E, 0xD, 0xF, 0x12, 0x18, 0x1C],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec_from_iter() {
|
|
||||||
let f = |x: u32| -> u32 {
|
|
||||||
let x = f64::from(x);
|
|
||||||
|
|
||||||
let y = x.sin().powi(0x2) * 1000.0;
|
|
||||||
|
|
||||||
y as u32
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut v = alloc::vec::Vec::new();
|
|
||||||
|
|
||||||
for x in 0x0..0x8 {
|
|
||||||
v.push(f(x));
|
|
||||||
}
|
|
||||||
|
|
||||||
let v: Vec<_, 0x10> = v.into_iter().collect();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
v,
|
|
||||||
[0, 708, 826, 19, 572, 919, 78, 431],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_vec_macro() {
|
|
||||||
let v0: Vec<u8, 0x4> = vec![0xEF; 0x3];
|
|
||||||
let v1: Vec<u8, 0x4> = vec![0xEF, 0xEF, 0xEF];
|
|
||||||
|
|
||||||
assert_eq!(v0, v1);
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue