Implement 'Default' for 'vec::IntoIter'; Mark 'String::is_char_boundary' with 'const'; Update lints; Clean up code; Bump MSRV to '1.86' for 'oct'; Bump Rust edition to '2024' for 'oct' and 'oct-macros' (and 'oct-benchmarks'); Fix '<vec::IntoIter as Iterator>::nth' not considering the current position; Optimise 'Iterator', 'DoubleEndedIterator', and 'ExactSizeIterator' implementations for 'vec::IntoIter'; Fix '<vec::IntoIter as Iterator>::size_hint' unsafely underflowing the size; Fix '<vec::IntoIter as Iterator>::nth' allowing out-of-bounds reads; Fix bounds on 'Arc' include in '/oct/src/decode/decode/mod.rs'; Implement 'PartialEq<Cow<str>>' for 'String'; Bring back 'vec' macro; Update syntax for 'string' macro; Unmark 'vec::IntoIter::{as_slice, as_mut_slice}' with 'const'; Optimise 'PartialEq', 'Eq', and 'PartialOrd' implementations; Rewrite 'Clone' implementations for 'Vec' and 'vec::IntoIter'; Update tests; Unimplement 'PartialEq<&mut [u8]>' for 'Slot'; Rewrite 'Vec::into_boxed_slice'; Rename 'Vec::into_alloc_vec' to 'into_vec';

This commit is contained in:
Gabriel Bjørnager Jensen 2025-02-22 21:38:43 +01:00
parent c19051dc1c
commit 50be4ca4bb
28 changed files with 810 additions and 269 deletions

View file

@ -3,6 +3,30 @@
This is the changelog of [Oct](https://crates.io/crates/oct/).
See `README.md` for more information.
## 0.19.0
* Implement `Default` for `vec::IntoIter`
* Mark `String::is_char_boundary` with `const`
* Update lints
* Clean up code
* Bump MSRV to `1.86` for `oct`
* Bump Rust edition to `2024` for `oct` and `oct-macros` (and `oct-benchmarks`)
* Fix `<vec::IntoIter as Iterator>::nth` not considering the current position
* Optimise `Iterator`, `DoubleEndedIterator`, and `ExactSizeIterator` implementations for `vec::IntoIter`
* Fix `<vec::IntoIter as Iterator>::size_hint` unsafely underflowing the size
* Fix `<vec::IntoIter as Iterator>::nth` allowing out-of-bounds reads
* Fix bounds on `Arc` include in `/oct/src/decode/decode/mod.rs`
* Implement `PartialEq<Cow<str>>` for `String`
* Bring back `vec` macro
* Update syntax for `string` macro
* Unmark `vec::IntoIter::{as_slice, as_mut_slice}` with `const`
* Optimise `PartialEq`, `Eq`, and `PartialOrd` implementations
* Rewrite `Clone` implementations for `Vec` and `vec::IntoIter`
* Update tests
* Unimplement `PartialEq<&mut [u8]>` for `Slot`
* Rewrite `Vec::into_boxed_slice`
* Rename `Vec::into_alloc_vec` to `into_vec`
## 0.18.0
* Clean up code

View file

@ -3,7 +3,7 @@ members = ["oct", "oct-benchmarks", "oct-macros"]
resolver = "2"
[workspace.package]
version = "0.18.0"
version = "0.19.0"
authors = ["Gabriel Bjørnager Jensen"]
readme = "README.md"
repository = "https://gitlab.com/bjoernager/oct/"
@ -34,6 +34,7 @@ cloned_instead_of_copied = "warn"
collection_is_never_read = "warn"
dbg_macro = "warn"
debug_assert_with_mut_call = "warn"
default_constructed_unit_structs = "forbid"
deref_by_slicing = "warn"
derive_partial_eq_without_eq = "warn"
derived_hash_with_manual_eq = "forbid"
@ -60,6 +61,7 @@ from_iter_instead_of_collect = "warn"
future_not_send = "forbid"
if_not_else = "warn"
if_then_some_else_none = "warn"
ignored_unit_patterns = "forbid"
impl_trait_in_params = "warn"
implicit_clone = "warn"
imprecise_flops = "forbid"
@ -68,6 +70,7 @@ index_refutable_slice = "warn"
inefficient_to_string = "warn"
infinite_loop = "forbid"
into_iter_without_iter = "warn"
invalid_atomic_ordering = "forbid"
invalid_upcast_comparisons = "warn"
iter_filter_is_ok = "warn"
iter_filter_is_some = "warn"
@ -76,6 +79,7 @@ iter_on_empty_collections = "warn"
iter_on_single_items = "warn"
iter_with_drain = "warn"
iter_without_into_iter = "warn"
let_unit_value = "forbid"
macro_use_imports = "warn"
manual_assert = "warn"
manual_c_str_literals = "warn"
@ -92,6 +96,7 @@ mismatching_type_param_order = "warn"
missing_errors_doc = "forbid"
missing_transmute_annotations = "forbid"
mixed_read_write_in_expression = "forbid"
must_use_unit = "forbid"
mut_mut = "warn"
mutex_atomic = "forbid"
mutex_integer = "forbid"
@ -107,11 +112,11 @@ non_ascii_literal = "forbid"
nonstandard_macro_braces = "forbid"
option_as_ref_cloned = "warn"
option_if_let_else = "warn"
option_map_unit_fn = "forbid"
option_option = "warn"
or_fun_call = "forbid"
path_buf_push_overwrite = "warn"
pattern_type_mismatch = "forbid"
ptr_as_ptr = "forbid"
ptr_cast_constness = "forbid"
pub_underscore_fields = "forbid"
range_plus_one = "warn"
@ -126,6 +131,8 @@ ref_as_ptr = "forbid"
ref_binding_to_reference = "warn"
ref_option_ref = "warn"
rest_pat_in_fully_bound_structs = "warn"
result_map_unit_fn = "forbid"
result_unit_err = "forbid"
return_self_not_must_use = "forbid"
same_functions_in_if_condition = "warn"
same_name_method = "forbid"
@ -146,6 +153,10 @@ transmute_ptr_to_ptr = "forbid"
type_repetition_in_bounds = "forbid"
uninhabited_references = "forbid"
uninlined_format_args = "forbid"
unit_arg = "forbid"
unit_cmp = "forbid"
unit_hash = "forbid"
unit_return_expecting_ord = "forbid"
unnecessary_box_returns = "forbid"
unnecessary_join = "warn"
unnecessary_self_imports = "forbid"
@ -155,6 +166,7 @@ unnested_or_patterns = "warn"
unused_async = "warn"
unused_peekable = "warn"
unused_rounding = "warn"
unused_unit = "forbid"
use_self = "forbid"
used_underscore_binding = "warn"
useless_let_if_seq = "warn"

View file

@ -1 +1,2 @@
avoid-breaking-exported-api = false
allow-private-module-inception = true
avoid-breaking-exported-api = false

View file

@ -22,7 +22,7 @@
[package]
name = "oct-benchmarks"
edition = "2021"
edition = "2024"
description = "Oct benchmarks."
license = "MIT"
@ -32,7 +32,7 @@ readme.workspace = true
repository.workspace = true
[dependencies]
oct = { path = "../oct", version = "0.18", features = ["proc-macro"]}
oct = { path = "../oct", version = "0.19", features = ["proc-macro"]}
bincode = "1.3.0"
rand = "0.8.0"

View file

@ -8,7 +8,7 @@
[package]
name = "oct-macros"
edition = "2021"
edition = "2024"
description = "Octonary transcoder. Procedural macros."
documentation = "https://docs.rs/oct-macros/"
homepage = "https://crates.io/crates/oct-macros/"

View file

@ -47,7 +47,7 @@ where
enum_body(data, repr)
}
Data::Union(..) => panic!("unions cannot derive `{trait_name}`"),
Data::Union(..) => panic!("untagged unions cannot derive `{trait_name}`"),
};
let generic_params = &input.generics.params;
@ -64,4 +64,4 @@ where
};
output
}
}

View file

@ -8,8 +8,8 @@
[package]
name = "oct"
edition = "2021"
rust-version = "1.83"
edition = "2024"
rust-version = "1.86"
description = "Octonary transcoder."
documentation = "https://docs.rs/oct/"
homepage = "https://crates.io/crates/oct/"
@ -25,6 +25,9 @@ categories.workspace = true
[package.metadata.docs.rs]
all-features = true
[dependencies]
oct-macros = { path = "../oct-macros", version = "0.19", optional = true}
[features]
default = ["alloc", "proc-macro", "std"]
@ -35,8 +38,5 @@ std = ["alloc"]
f128 = []
f16 = []
[dependencies]
oct-macros = { path = "../oct-macros", version = "0.18", optional = true}
[lints]
workspace = true

View file

@ -52,9 +52,11 @@ use {
alloc::collections::{BinaryHeap, LinkedList},
alloc::ffi::CString,
alloc::rc::Rc,
alloc::sync::Arc,
};
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
use alloc::sync::Arc;
#[cfg(any(feature = "f128", feature = "f16"))]
use crate::oct::encode::SizedEncode;
@ -130,7 +132,7 @@ impl<T: Decode, const N: usize> Decode for [T; N] {
// about it from this point on. `transmute` cannot
// be used here, and `transmute_unchecked` is re-
// served for the greedy rustc devs. >:(
let this = unsafe { buf.as_ptr().cast::<[T; N]>().read() };
let this = unsafe { (buf.as_ptr() as *const [T; N]).read() };
Ok(this)
}
}

View file

@ -197,20 +197,38 @@ impl Eq for Input<'_> { }
impl PartialEq for Input<'_> {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.as_slice() == other.as_slice()
*self.as_slice() == *other.as_slice()
}
#[expect(clippy::partialeq_ne_impl)]
#[inline(always)]
fn ne(&self, other: &Self) -> bool {
*self.as_slice() != *other.as_slice()
}
}
impl PartialEq<[u8]> for Input<'_> {
#[inline(always)]
fn eq(&self, other: &[u8]) -> bool {
self.as_slice() == other
*self.as_slice() == *other
}
#[expect(clippy::partialeq_ne_impl)]
#[inline(always)]
fn ne(&self, other: &[u8]) -> bool {
*self.as_slice() != *other
}
}
impl PartialEq<&[u8]> for Input<'_> {
#[inline(always)]
fn eq(&self, other: &&[u8]) -> bool {
self.as_slice() == *other
*self.as_slice() == **other
}
#[expect(clippy::partialeq_ne_impl)]
#[inline(always)]
fn ne(&self, other: &&[u8]) -> bool {
*self.as_slice() != **other
}
}

View file

@ -32,11 +32,13 @@
//! assert_eq!(<(u8, u8)>::decode(&mut stream).unwrap(), (0x54, 0x45));
//! ```
use crate::use_mod;
mod decode;
mod decode_borrowed;
mod input;
use_mod!(pub decode);
use_mod!(pub decode_borrowed);
use_mod!(pub input);
pub use decode::Decode;
pub use decode_borrowed::DecodeBorrowed;
pub use input::Input;
/// Implements [`Decode`] for the provided type.
///

View file

@ -52,8 +52,6 @@ use {
alloc::boxed::Box,
alloc::collections::{BinaryHeap, LinkedList},
alloc::ffi::CString,
alloc::string::String,
alloc::vec::Vec,
alloc::rc::Rc,
};
@ -237,17 +235,17 @@ impl<T: Encode> Encode for Bound<T> {
fn encode(&self, output: &mut Output) -> Result<(), Self::Error> {
match *self {
Self::Included(ref bound) => {
let Ok(_) = 0x0u8.encode(output);
let Ok(()) = 0x0u8.encode(output);
bound.encode(output).map_err(EnumEncodeError::BadField)?;
}
Self::Excluded(ref bound) => {
let Ok(_) = 0x1u8.encode(output);
let Ok(()) = 0x1u8.encode(output);
bound.encode(output).map_err(EnumEncodeError::BadField)?;
}
Self::Unbounded => {
let Ok(_) = 0x2u8.encode(output);
let Ok(()) = 0x2u8.encode(output);
}
}
@ -481,7 +479,7 @@ impl Encode for isize {
let value = i16::try_from(*self)
.map_err(|_| IsizeEncodeError(*self))?;
let Ok(_) = value.encode(output);
let Ok(()) = value.encode(output);
Ok(())
}
}
@ -747,13 +745,13 @@ where
match *self {
Ok(ref v) => {
let Ok(_) = false.encode(output);
let Ok(()) = false.encode(output);
v.encode(output)?;
}
Err(ref e) => {
let Ok(_) = true.encode(output);
let Ok(()) = true.encode(output);
e.encode(output).map_err(Into::into)?;
}
@ -797,13 +795,13 @@ impl Encode for SocketAddr {
match *self {
Self::V4(ref addr) => {
let Ok(_) = 0x4u8.encode(output);
let Ok(_) = addr.encode(output);
let Ok(()) = 0x4u8.encode(output);
let Ok(()) = addr.encode(output);
}
Self::V6(ref addr) => {
let Ok(_) = 0x6u8.encode(output);
let Ok(_) = addr.encode(output);
let Ok(()) = 0x6u8.encode(output);
let Ok(()) = addr.encode(output);
}
}
@ -851,7 +849,7 @@ impl Encode for str {
#[cfg(feature = "alloc")]
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
impl Encode for String {
impl Encode for alloc::string::String {
type Error = <str as Encode>::Error;
/// See [`prim@str`].
@ -929,14 +927,14 @@ impl Encode for usize {
let value = u16::try_from(*self)
.map_err(|_| UsizeEncodeError(*self))?;
let Ok(_) = value.encode(output);
let Ok(()) = value.encode(output);
Ok(())
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
impl<T: Encode> Encode for Vec<T> {
impl<T: Encode> Encode for alloc::vec::Vec<T> {
type Error = <[T] as Encode>::Error;
#[inline(always)]

View file

@ -9,7 +9,6 @@
use core::time::Duration;
use oct::encode::{Encode, SizedEncode};
use oct::error::UsizeEncodeError;
use oct::string::String as OctString;
use std::time::{SystemTime, UNIX_EPOCH};
macro_rules! test {
@ -21,7 +20,7 @@ macro_rules! test {
)*
} => {{
$($({
let mut buf = ::std::vec![0x00; <$ty as ::oct::encode::SizedEncode>::MAX_ENCODED_SIZE];
let mut buf = ::std::vec![0x00; 0x100];
let mut output = ::oct::encode::Output::new(&mut buf);
@ -75,12 +74,12 @@ fn test_encode() {
]),
}
OctString<0x1> {
OctString::new("A").unwrap() => Ok(&[0x01, 0x00, 0x41]),
str {
"A" => Ok(&[0x01, 0x00, 0x41]),
}
OctString<0xA> {
OctString::new("l\u{00F8}gma\u{00F0}ur").unwrap() => Ok(&[
str {
"l\u{00F8}gma\u{00F0}ur" => Ok(&[
0x0A, 0x00, 0x6C, 0xC3, 0xB8, 0x67, 0x6D, 0x61,
0xC3, 0xB0, 0x75, 0x72,
])

View file

@ -30,8 +30,8 @@
//! let mut stream = Output::new(&mut buf);
//!
//! // Note: For serialising multiple characters, the
//! // `alloc::string::String` and `oct::str::
//! // String` types are usually preferred.
//! // `String` and `oct::str::String` types are usual-
//! // ly preferred.
//!
//! 'ل'.encode(&mut stream).unwrap();
//! 'ا'.encode(&mut stream).unwrap();
@ -48,11 +48,13 @@
//!
//! If the encoded type additionally implements [`SizedEncode`], then the maximum size of any encoding is guaranteed with the [`MAX_ENCODED_SIZE`](SizedEncode::MAX_ENCODED_SIZE) constant.
use crate::use_mod;
mod encode;
mod output;
mod sized_encode;
use_mod!(pub encode);
use_mod!(pub output);
use_mod!(pub sized_encode);
pub use encode::Encode;
pub use output::Output;
pub use sized_encode::SizedEncode;
/// Implements [`Encode`] for the provided type.
///

View file

@ -129,20 +129,38 @@ impl Eq for Output<'_> { }
impl PartialEq for Output<'_> {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.as_slice() == other.as_slice()
*self.as_slice() == *other.as_slice()
}
#[expect(clippy::partialeq_ne_impl)]
#[inline(always)]
fn ne(&self, other: &Self) -> bool {
*self.as_slice() != *other.as_slice()
}
}
impl PartialEq<[u8]> for Output<'_> {
#[inline(always)]
fn eq(&self, other: &[u8]) -> bool {
self.as_slice() == other
*self.as_slice() == *other
}
#[expect(clippy::partialeq_ne_impl)]
#[inline(always)]
fn ne(&self, other: &[u8]) -> bool {
*self.as_slice() != *other
}
}
impl PartialEq<&[u8]> for Output<'_> {
#[inline(always)]
fn eq(&self, other: &&[u8]) -> bool {
self.as_slice() == *other
*self.as_slice() == **other
}
#[expect(clippy::partialeq_ne_impl)]
#[inline(always)]
fn ne(&self, other: &&[u8]) -> bool {
*self.as_slice() != **other
}
}

View file

@ -56,7 +56,7 @@ use {
/// When using [`Encode`], the size of the resulting encoding cannot always be known beforehand.
/// This trait defines an upper bound for these sizes.
///
/// Note that whilst *technically* having a size limit, [`Vec`](alloc::vec::Vec), [`String`](alloc::string::String), etc. do not implement this trait.
/// Note that whilst *technically* having a size limit, [`alloc::vec::Vec`], [`alloc::string::String`], etc. do not implement this trait.
/// The general rule is that the size limit must be a substantial part of a type's design to constitute implementing this trait.
///
/// Also note that -- in practice -- this trait is **not** strictly enforceable.

View file

@ -11,25 +11,44 @@
//! This module defines the error types used by Oct.
//! All of these types define (at least conditionally) the [`Error`](core::error::Error) trait.
use crate::use_mod;
use_mod!(pub char_decode_error);
use_mod!(pub collection_decode_error);
use_mod!(pub collection_encode_error);
use_mod!(pub enum_decode_error);
use_mod!(pub enum_encode_error);
use_mod!(pub generic_decode_error);
use_mod!(pub generic_encode_error);
use_mod!(pub input_error);
use_mod!(pub isize_encode_error);
use_mod!(pub item_decode_error);
use_mod!(pub item_encode_error);
use_mod!(pub length_error);
use_mod!(pub non_zero_decode_error);
use_mod!(pub output_error);
use_mod!(pub ref_cell_encode_error);
use_mod!(pub usize_encode_error);
use_mod!(pub utf8_error);
mod char_decode_error;
mod collection_decode_error;
mod collection_encode_error;
mod enum_decode_error;
mod enum_encode_error;
mod generic_decode_error;
mod generic_encode_error;
mod input_error;
mod isize_encode_error;
mod item_decode_error;
mod item_encode_error;
mod length_error;
mod non_zero_decode_error;
mod output_error;
mod ref_cell_encode_error;
mod usize_encode_error;
mod utf8_error;
#[cfg(feature = "std")]
use_mod!(pub system_time_decode_error);
mod system_time_decode_error;
pub use char_decode_error::CharDecodeError;
pub use collection_decode_error::CollectionDecodeError;
pub use collection_encode_error::CollectionEncodeError;
pub use enum_decode_error::EnumDecodeError;
pub use enum_encode_error::EnumEncodeError;
pub use generic_decode_error::GenericDecodeError;
pub use generic_encode_error::GenericEncodeError;
pub use input_error::InputError;
pub use isize_encode_error::IsizeEncodeError;
pub use item_decode_error::ItemDecodeError;
pub use item_encode_error::ItemEncodeError;
pub use length_error::LengthError;
pub use non_zero_decode_error::NonZeroDecodeError;
pub use output_error::OutputError;
pub use ref_cell_encode_error::RefCellEncodeError;
pub use usize_encode_error::UsizeEncodeError;
pub use utf8_error::Utf8Error;
#[cfg(feature = "std")]
pub use system_time_decode_error::SystemTimeDecodeError;

View file

@ -235,16 +235,8 @@ extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
/// Includes a module and imports it contents.
///
/// The provided visibility denotes the visibility of **all** imported items.
macro_rules! use_mod {
($vis:vis $name:ident$(,)?) => {
mod $name;
$vis use $name::*;
};
}
pub(crate) use use_mod;
mod prim_discriminant;
mod prim_repr;
pub mod decode;
pub mod encode;
@ -255,24 +247,42 @@ pub mod vec;
#[cfg(feature = "alloc")]
pub mod slot;
use_mod!(pub prim_discriminant);
use_mod!(pub prim_repr);
pub use prim_discriminant::PrimDiscriminant;
pub use prim_repr::PrimRepr;
pub(crate) use prim_repr::SealedPrimRepr;
/// Directly constructs a [`Vec`](crate::vec::Vec) object.
///
/// 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_rules! vec {
[$value:literal; $len:expr] => {{
const { $crate::vec::__vec([$value; $len]) }
}};
[$($value:expr),+ $(,)?] => {{
const { $crate::vec::__vec([$($value,)*]) }
}};
[] => {{
const { $crate::vec::__vec([]) }
}}
}
/// Directly constructs a [`String`](crate::string::String) object.
///
/// This macro tests at compile-time whether the string literal can fit into the specified (or inferred) length.
/// 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, $len:expr) => {{
const { $crate::string::__str::<$len>($s) }
}};
($s:expr) => {{
const { $crate::string::__str($s) }
const { $crate::string::__string($s) }
}};
() => {{
const { $crate::string::__str("") }
const { $crate::string::__string("") }
}};
}

View file

@ -28,7 +28,7 @@ macro_rules! impl_prim_repr {
($ty:ty => $variant:ident) => {
impl ::oct::SealedPrimRepr for $ty { }
impl PrimRepr for $ty {
impl ::oct::PrimRepr for $ty {
#[inline(always)]
fn into_prim_discriminant(self) -> ::oct::PrimDiscriminant {
::oct::PrimDiscriminant::$variant(self)
@ -49,4 +49,4 @@ impl_prim_repr!(i16 => I16);
impl_prim_repr!(i32 => I32);
impl_prim_repr!(i64 => I64);
impl_prim_repr!(i128 => I128);
impl_prim_repr!(isize => Isize);
impl_prim_repr!(isize => Isize);

View file

@ -10,6 +10,6 @@
//!
//! *See the [`Slot`] type for more information.*
use crate::use_mod;
mod slot;
use_mod!(pub slot);
pub use slot::Slot;

View file

@ -56,8 +56,8 @@ use core::slice::{self, SliceIndex};
/// ```
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
pub struct Slot<T> {
buf: Box<[u8]>,
len: usize,
buf: Box<[u8]>,
_ty: PhantomData<fn() -> T>,
}
@ -100,8 +100,8 @@ impl<T> Slot<T> {
};
Self {
buf,
len,
buf,
_ty: PhantomData,
}
@ -310,7 +310,7 @@ impl<T: SizedEncode> Slot<T> {
impl<T> AsMut<[u8]> for Slot<T> {
#[inline(always)]
fn as_mut(&mut self) -> &mut [u8] {
self.as_mut_slice()
self
}
}
@ -319,7 +319,7 @@ impl<T> AsMut<[u8]> for Slot<T> {
impl<T> AsRef<[u8]> for Slot<T> {
#[inline(always)]
fn as_ref(&self) -> &[u8] {
self.as_slice()
self
}
}
@ -328,7 +328,7 @@ impl<T> AsRef<[u8]> for Slot<T> {
impl<T> Borrow<[u8]> for Slot<T> {
#[inline(always)]
fn borrow(&self) -> &[u8] {
self.as_slice()
self
}
}
@ -337,14 +337,14 @@ impl<T> Borrow<[u8]> for Slot<T> {
impl<T> BorrowMut<[u8]> for Slot<T> {
#[inline(always)]
fn borrow_mut(&mut self) -> &mut [u8] {
self.as_mut_slice()
self
}
}
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
impl<T> Debug for Slot<T> {
#[inline(always)]
fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{:?}", self.as_slice()) }
fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{:?}", &**self) }
}
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
@ -379,7 +379,7 @@ impl<T, I: SliceIndex<[u8]>> Index<I> for Slot<T> {
#[inline(always)]
fn index(&self, index: I) -> &Self::Output {
self.get(index).unwrap()
Index::index(&**self, index)
}
}
@ -387,7 +387,7 @@ impl<T, I: SliceIndex<[u8]>> Index<I> for Slot<T> {
impl<T, I: SliceIndex<[u8]>> IndexMut<I> for Slot<T> {
#[inline(always)]
fn index_mut(&mut self, index: I) -> &mut Self::Output {
self.get_mut(index).unwrap()
IndexMut::index_mut(&mut **self, index)
}
}
@ -395,7 +395,13 @@ impl<T, I: SliceIndex<[u8]>> IndexMut<I> for Slot<T> {
impl<T> PartialEq<[u8]> for Slot<T> {
#[inline(always)]
fn eq(&self, other: &[u8]) -> bool {
self.as_slice() == other
**self == *other
}
#[expect(clippy::partialeq_ne_impl)]
#[inline(always)]
fn ne(&self, other: &[u8]) -> bool {
**self != *other
}
}
@ -403,14 +409,12 @@ impl<T> PartialEq<[u8]> for Slot<T> {
impl<T> PartialEq<&[u8]> for Slot<T> {
#[inline(always)]
fn eq(&self, other: &&[u8]) -> bool {
self.as_slice() == *other
**self == **other
}
}
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
impl<T> PartialEq<&mut [u8]> for Slot<T> {
#[expect(clippy::partialeq_ne_impl)]
#[inline(always)]
fn eq(&self, other: &&mut [u8]) -> bool {
self.as_slice() == *other
fn ne(&self, other: &&[u8]) -> bool {
**self != **other
}
}

View file

@ -8,6 +8,6 @@
//! String container.
use crate::use_mod;
mod string;
use_mod!(pub string);
pub use string::{__string, String};

View file

@ -25,7 +25,10 @@ use core::slice::{self, SliceIndex};
use core::str::{self, FromStr};
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
use {
alloc::borrow::Cow,
alloc::boxed::Box,
};
#[cfg(feature = "std")]
use {
@ -94,7 +97,7 @@ impl<const N: usize> String<N> {
unsafe {
let src = s.as_ptr();
let dst = buf.as_mut_ptr().cast();
let dst = buf.as_mut_ptr();
copy_nonoverlapping(src, dst, len);
}
@ -122,7 +125,7 @@ impl<const N: usize> String<N> {
unsafe {
let src = s.as_ptr();
let dst = buf.as_mut_ptr().cast();
let dst = buf.as_mut_ptr();
copy_nonoverlapping(src, dst, len);
}
@ -181,7 +184,7 @@ impl<const N: usize> String<N> {
// SAFETY: We can safely transmute here as
// `MaybeUninit<u8>` is transparent to `u8` and
// we have initialised the remaining bytes.
let buf = unsafe { buf.as_ptr().cast::<[u8; N]>().read() };
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`.
@ -228,10 +231,7 @@ impl<const N: usize> String<N> {
/// 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 fn is_char_boundary(&self, index: usize) -> bool {
// TODO: Mark with `const` when `const_is_char_
// boundary` lands.
pub const fn is_char_boundary(&self, index: usize) -> bool {
self.as_str().is_char_boundary(index)
}
@ -329,7 +329,7 @@ impl<const N: usize> String<N> {
let (buf, len) = self.into_raw_parts();
// SAFETY: `MaybeUninit<u8>` is transparent to `u8`.
let buf = unsafe { (&raw const buf).cast::<[MaybeUninit<u8>; N]>().read() };
let buf = unsafe { (&raw const buf as *const [MaybeUninit<u8>; N]).read() };
unsafe { Vec::from_raw_parts(buf, len) }
@ -492,9 +492,9 @@ impl<const N: usize> FromIterator<char> for String<N> {
if rem < req { break }
let start = len;
let stop = start + req;
let end = start + req;
c.encode_utf8(&mut buf[start..stop]);
c.encode_utf8(&mut buf[start..end]);
len += req;
}
@ -542,28 +542,61 @@ impl<I: SliceIndex<str>, const N: usize> IndexMut<I> for String<N> {
impl<const N: usize> Ord for String<N> {
#[inline(always)]
fn cmp(&self, other: &Self) -> Ordering {
self.as_str().cmp(other.as_str())
(**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.as_str() == other.as_str()
**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.as_str() == other
**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
**self == **other
}
#[expect(clippy::partialeq_ne_impl)]
#[inline(always)]
fn ne(&self, other: &&str) -> bool {
**self != **other
}
}
@ -572,14 +605,40 @@ impl<const N: usize> PartialEq<&str> for String<N> {
impl<const N: usize> PartialEq<alloc::string::String> for String<N> {
#[inline(always)]
fn eq(&self, other: &alloc::string::String) -> bool {
self.as_str() == other.as_str()
*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.as_str().partial_cmp(other.as_str())
(**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
}
}
@ -671,7 +730,7 @@ impl<const N: usize> PartialEq<String<N>> for alloc::string::String {
#[inline(always)]
#[must_use]
#[track_caller]
pub const fn __str<const N: usize>(s: &'static str) -> String<N> {
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

View file

@ -14,7 +14,7 @@ use oct::vec::Vec;
#[test]
fn test_str_macro() {
let s0: String<0x3> = string!("Oct", 0x3);
let s0: String<0x3> = string!("Oct");
let s1: String<0x3> = string!("Oct");
assert_eq!(s0, s1);
@ -28,9 +28,9 @@ fn test_string() {
#[test]
fn test_string_size() {
let s0 = string!("Hello there!", 0x0C);
let s1 = string!("MEIN_GRO\u{1E9E}_GOTT", 0x12);
let s2 = string!("Hello", 0x05);
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));

View file

@ -9,6 +9,8 @@
#[cfg(test)]
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;
@ -16,50 +18,110 @@ use core::slice;
/// Owning iterator to a vector.
///
/// This type is exclusively used by the deconstruction of the [`Vec`](crate::vec::Vec) type.
/// When just borrowing such vectors, the standard library's <code>core::slice::{[Iter](core::slice::Iter), [IterMut](core::slice::IterMut)}</code> types are used instead.
/// 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> {
len: 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, size-constrained iterator.
/// Constructs a new, owning iterator to a vector.
#[inline(always)]
#[track_caller]
pub(super) const unsafe fn new(buf: [MaybeUninit<T>; N], len: usize) -> Self {
debug_assert!(len <= N, "cannot construct iterator longer than its capacity");
pub(super) fn new(v: Vec<T, N>) -> Self {
let (buf, len) = v.into_raw_parts();
Self { len, pos: 0x0, buf }
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 if this would happen.
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 slice of the remaining elements.
#[inline(always)]
pub const fn as_slice(&self) -> &[T] {
unsafe {
let ptr = self.buf
.as_ptr()
.add(self.pos)
.cast();
pub fn as_slice(&self) -> &[T] {
let pos = self.pos;
let len = self.len;
slice::from_raw_parts(ptr, self.len)
}
// SAFETY: `MaybeUninit<T>` is transparent to `T`.
let base = self.buf.as_ptr() as *const T;
let ptr = unsafe { base.add(pos) };
unsafe { slice::from_raw_parts(ptr, len) }
}
/// Gets a mutable slice of the remaining elements.
#[inline(always)]
pub const fn as_mut_slice(&mut self) -> &mut [T] {
unsafe {
let ptr = self.buf
.as_mut_ptr()
.add(self.pos)
.cast();
pub fn as_mut_slice(&mut self) -> &mut [T] {
let pos = self.pos;
let len = self.len;
slice::from_raw_parts_mut(ptr, self.len)
}
// SAFETY: `MaybeUninit<T>` is transparent to `T`.
let base = self.buf.as_mut_ptr() as *mut T;
let ptr = unsafe { base.add(pos) };
unsafe { slice::from_raw_parts_mut(ptr, len) }
}
}
@ -80,39 +142,135 @@ impl<T, const N: usize> AsRef<[T]> for IntoIter<T, N> {
impl<T: Clone, const N: usize> Clone for IntoIter<T, N> {
#[inline]
fn clone(&self) -> Self {
let mut buf = [const { MaybeUninit::<T>::uninit() }; N];
let pos = self.pos;
let len = self.len;
let mut buf = [const { MaybeUninit::uninit() }; N];
let Self { pos, len, .. } = *self;
// 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 stop = start + len;
let end = pos + len;
for i in start..stop {
unsafe {
let item = (&raw const *self.buf.get_unchecked(i)).cast();
// 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) };
let value = Clone::clone(&*item);
// SAFETY: The buffer has been initialised in the
// provided range - which does not extend beyond
// bounds.
Self { pos, len, buf }
}
}
buf.get_unchecked_mut(i).write(value);
}
}
Self { len, pos, 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;
let item = unsafe { self.buf.get_unchecked(index).assume_init_read() };
// SAFETY: `MaybeUninit<T>` is transparent to `T`.
let base = self.buf.as_ptr() as *const T;
self.len -= 0x1;
let item = unsafe { base.add(index) };
Some(item)
// 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, **not** including the position.
// SAFETY: We have tested that at least one element
// remains.
unsafe { self.advance_back_by_unchecked(0x1) };
Some(value)
}
#[inline]
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
// Test whether the iterator is empty.
if n >= self.len { return None };
// SAFETY: We have tested that there are at least
// `n + 1` elements left.
// Get the indeces of the involved items.
let end = self.pos + self.len;
let index = end - n;
let start = index + 0x1;
// SAFETY: `MaybeUninit<T>` is transparent to `T`.
let base = self.buf.as_mut_ptr() as *mut T;
// Drop each skipped element in **reverse** order.
for i in (start..end).rev() {
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) };
// 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, **not** including the position.
// 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_back_by_unchecked(count) };
// Return the value.
Some(value)
}
}
@ -121,7 +279,7 @@ impl<T, const N: usize> Drop for IntoIter<T, N> {
fn drop(&mut self) {
// Drop every element that hasn't been consumed.
let remaining = &raw mut *self.as_mut_slice();
let remaining = self.as_mut_slice();
unsafe { drop_in_place(remaining) };
// We do not need to ensure that `self` is in a
@ -129,7 +287,12 @@ impl<T, const N: usize> Drop for IntoIter<T, N> {
}
}
impl<T, const N: usize> ExactSizeIterator for IntoIter<T, N> { }
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> { }
@ -138,50 +301,119 @@ impl<T, const N: usize> Iterator for IntoIter<T, N> {
#[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;
let item = unsafe { self.buf.get_unchecked(index).assume_init_read() };
// SAFETY: `MaybeUninit<T>` is transparent to `T`.
let base = self.buf.as_ptr() as *const T;
self.len -= 0x1;
self.pos += 0x1;
let item = unsafe { base.add(index) };
Some(item)
// 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, index: usize) -> Option<Self::Item> {
if index > self.len { return None };
fn nth(&mut self, n: usize) -> Option<Self::Item> {
// Test whether the iterator is empty.
let skipped = unsafe {
let start = self.pos;
let stop = start + index - 0x1;
if n >= self.len { return None };
self.buf.get_unchecked_mut(start..stop)
};
// Get the indeces of the involved items.
let start = self.pos;
let end = start + n;
// SAFETY: `MaybeUninit<T>` is transparent to `T`.
let base = self.buf.as_mut_ptr() as *mut T;
// Drop each skipped element.
unsafe { drop_in_place(skipped) };
for i in start..end {
let item = unsafe { base.add(i) };
// Read the final element.
// SAFETY: We guarantee that all items in the range
//
// self.pos..self.pos + self.len
//
// are alive (and initialised).
unsafe { drop_in_place(item) };
}
// SAFETY: `index` has been tested to be within
// bounds, and the element at that position is also
// guaranteed to still be alive.
let item = unsafe { self.buf.get_unchecked(index).assume_init_read() };
// Read the final value.
self.len -= index;
self.pos += index;
let item = unsafe { base.add(end) };
Some(item)
// 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: 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 rem = unsafe { self.len.unchecked_sub(self.pos) };
let len = self.len;
(len, Some(len))
}
(rem, Some(rem))
#[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)
}
}

View file

@ -6,32 +6,46 @@
// can obtain one at:
// <https://mozilla.org/MPL/2.0/>.
use core::str::FromStr;
use oct::string;
use oct::string::String;
use oct::vec::Vec;
#[test]
fn test_vec_iter_clone() {
let data = String::<0x9>::from_str("fran\u{00E7}ais").unwrap();
let data: String::<0xF> = string!("fran\u{00E7}aise");
let mut data0 = data.into_bytes().into_iter();
assert_eq!(data.len(), 0xA);
assert_eq!(data.as_bytes(), b"fran\xC3\xA7aise");
let _ = data0.nth(0x4);
let mut iter0 = data.into_bytes().into_iter();
let mut data1 = data0.clone();
assert_eq!(iter0.len(), 0xA);
assert_eq!(iter0.as_slice(), b"fran\xC3\xA7aise");
assert_eq!(data0.next(), Some(0xC3));
assert_eq!(data1.next(), Some(0xC3));
assert_eq!(data0.next(), Some(0xA7));
assert_eq!(data1.next(), Some(0xA7));
assert_eq!(data0.next(), Some(b'a'));
assert_eq!(data1.next(), Some(b'a'));
assert_eq!(data0.next(), Some(b'i'));
assert_eq!(data1.next(), Some(b'i'));
assert_eq!(data0.next(), Some(b's'));
assert_eq!(data1.next(), Some(b's'));
assert_eq!(data0.next(), None);
assert_eq!(data1.next(), None);
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]

View file

@ -8,7 +8,33 @@
//! Vector container and iterators.
use crate::use_mod;
mod into_iter;
mod vec;
use_mod!(pub into_iter);
use_mod!(pub vec);
pub use into_iter::IntoIter;
pub use vec::{__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) };
}
}

View file

@ -12,7 +12,7 @@ mod test;
use crate::decode::{self, Decode, DecodeBorrowed};
use crate::encode::{self, Encode, SizedEncode};
use crate::error::{CollectionDecodeError, ItemDecodeError, LengthError};
use crate::vec::IntoIter;
use crate::vec::{clone_to_uninit_in_range, IntoIter};
use core::borrow::{Borrow, BorrowMut};
use core::cmp::Ordering;
@ -24,11 +24,7 @@ use core::ptr::{copy_nonoverlapping, drop_in_place, null, null_mut};
use core::slice::{self, SliceIndex};
#[cfg(feature = "alloc")]
use {
alloc::alloc::alloc,
alloc::boxed::Box,
core::alloc::Layout,
};
use alloc::boxed::Box;
/// Vector container with maximum length.
///
@ -370,24 +366,24 @@ impl<T, const N: usize> Vec<T, N> {
/// 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();
unsafe {
let layout = Layout::array::<T>(len).unwrap();
let ptr = alloc(layout).cast::<T>();
let mut boxed = alloc::vec::Vec::with_capacity(len).into_boxed_slice();
assert!(!ptr.is_null(), "allocation failed");
// SAFETY: `MaybeUninit<T>` is transparent to `T`.
let src = buf.as_ptr() as *const T;
copy_nonoverlapping(buf.as_ptr().cast(), ptr, len);
let dst = boxed.as_mut_ptr();
let slice = core::ptr::slice_from_raw_parts_mut(ptr, len);
Box::from_raw(slice)
// SAFETY: `boxed` has been allocated with at least
// `len` elements.
unsafe { copy_nonoverlapping(src, dst, len) };
// `self.buf` is dropped without destructors being
// run.
}
boxed
}
/// Converts the vector into a dynamically-allocated vector.
@ -397,7 +393,8 @@ impl<T, const N: usize> Vec<T, N> {
#[cfg_attr(doc, doc(cfg(feature = "alloc")))]
#[inline(always)]
#[must_use]
pub fn into_alloc_vec(self) -> alloc::vec::Vec<T> {
#[track_caller]
pub fn into_vec(self) -> alloc::vec::Vec<T> {
self.into_boxed_slice().into_vec()
}
}
@ -405,44 +402,54 @@ impl<T, const N: usize> Vec<T, N> {
impl<T, const N: usize> AsMut<[T]> for Vec<T, N> {
#[inline(always)]
fn as_mut(&mut self) -> &mut [T] {
self.as_mut_slice()
self
}
}
impl<T, const N: usize> AsRef<[T]> for Vec<T, N> {
#[inline(always)]
fn as_ref(&self) -> &[T] {
self.as_slice()
self
}
}
impl<T, const N: usize> Borrow<[T]> for Vec<T, N> {
#[inline(always)]
fn borrow(&self) -> &[T] {
self.as_slice()
self
}
}
impl<T, const N: usize> BorrowMut<[T]> for Vec<T, N> {
#[inline(always)]
fn borrow_mut(&mut self) -> &mut [T] {
self.as_mut_slice()
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];
unsafe {
for i in 0x0..self.len() {
let value = self.get_unchecked(i).clone();
buf.get_unchecked_mut(i).write(value);
}
// SAFETY: `MaybeUninit<T>` is transparent to `T`.
let src = self.buf.as_ptr() as *const T;
Self::from_raw_parts(buf, self.len())
}
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) }
}
}
@ -486,14 +493,12 @@ 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 {
unsafe {
let buf = [const { MaybeUninit::uninit() }; N];
let buf = [const { MaybeUninit::uninit() }; N];
// SAFETY: The resulting vector is zero lengthed
// and does therefore not expose any uninitialised
// objects.
Self::from_raw_parts(buf, Default::default())
}
// SAFETY: The resulting vector is zero lengthed
// and does therefore not expose any uninitialised
// objects.
unsafe { Self::from_raw_parts(buf, Default::default()) }
}
}
@ -518,7 +523,10 @@ impl<T, const N: usize> Drop for Vec<T, N> {
fn drop(&mut self) {
// Drop every element that is currently alive.
let remaining = &raw mut *self.as_mut_slice();
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
@ -531,7 +539,7 @@ impl<T: Encode, const N: usize> Encode for Vec<T, N> {
#[inline(always)]
fn encode(&self, output: &mut encode::Output) -> Result<(), Self::Error> {
self.as_slice().encode(output)
(**self).encode(output)
}
}
@ -561,6 +569,7 @@ impl<T, const N: usize> FromIterator<T> for Vec<T, N> {
for item in &mut buf {
let Some(value) = iter.next() else { break };
item.write(value);
len += 0x1;
@ -573,7 +582,7 @@ impl<T, const N: usize> FromIterator<T> for Vec<T, N> {
impl<T: Hash, const N: usize> Hash for Vec<T, N> {
#[inline(always)]
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_slice().hash(state)
(**self).hash(state)
}
}
@ -583,7 +592,7 @@ impl<T, I: SliceIndex<[T]>, const N: usize> Index<I> for Vec<T, N> {
#[inline(always)]
#[track_caller]
fn index(&self, index: I) -> &Self::Output {
Index::index(self.as_slice(), index)
Index::index(&**self, index)
}
}
@ -591,7 +600,7 @@ 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(self.as_mut_slice(), index)
IndexMut::index_mut(&mut **self, index)
}
}
@ -602,9 +611,7 @@ impl<T, const N: usize> IntoIterator for Vec<T, N> {
#[inline(always)]
fn into_iter(self) -> Self::IntoIter {
let (buf, len) = self.into_raw_parts();
unsafe { IntoIter::new(buf, len) }
IntoIter::new(self)
}
}
@ -633,35 +640,59 @@ impl<'a, T, const N: usize> IntoIterator for &'a mut Vec<T, N> {
impl<T: Ord, const N: usize> Ord for Vec<T, N> {
#[inline(always)]
fn cmp(&self, other: &Self) -> Ordering {
self.as_slice().cmp(other.as_slice())
(**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.as_slice() == other.as_slice()
**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.as_slice()
**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.as_slice() == other
**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
**self == **other
}
#[expect(clippy::partialeq_ne_impl)]
#[inline(always)]
fn ne(&self, other: &&[U]) -> bool {
**self != **other
}
}
@ -670,14 +701,40 @@ impl<T: PartialEq<U>, U, const N: usize> PartialEq<&[U]> for Vec<T, N> {
impl<T: PartialEq<U>, U, const N: usize> PartialEq<alloc::vec::Vec<U>> for Vec<T, N> {
#[inline(always)]
fn eq(&self, other: &alloc::vec::Vec<U>) -> bool {
self.as_slice() == other.as_slice()
**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.as_slice().partial_cmp(other.as_slice())
(**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
}
}
@ -708,7 +765,7 @@ impl<T, const N: usize> From<Vec<T, N>> for Box<[T]> {
impl<T, const N: usize> From<Vec<T, N>> for alloc::vec::Vec<T> {
#[inline(always)]
fn from(value: Vec<T, N>) -> Self {
value.into_alloc_vec()
value.into_vec()
}
}
@ -720,3 +777,37 @@ impl<T: PartialEq<U>, U, const N: usize> PartialEq<Vec<U, N>> for alloc::vec::Ve
self.as_slice() == other.as_slice()
}
}
// NOTE: This function is used by the `vec` macro
// to circumvent itself using code which may be
// forbidden by the macro user's lints. This func-
// tion is sound, but 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 __vec<T, const N: usize, const M: usize>(data: [T; M]) -> Vec<T, N> {
assert!(M <= N, "cannot construct vector from literal that is longer");
let data = ManuallyDrop::new(data);
let mut buf = [const { MaybeUninit::uninit() }; N];
let len = M;
unsafe {
let src = &raw const data as *const T;
let dst = buf.as_mut_ptr() as *mut T;
// SAFETY: The original array is not dropped after
// this move, and `len` is equal to the length `M`
// of the array. `MaybeUninit<T>` is also trans-
// parent to `T`.
copy_nonoverlapping(src, dst, len);
}
// SAFETY: `len` does not extend beyond the `M`
// elements moved from `data`.
unsafe { Vec::from_raw_parts(buf, len) }
}

View file

@ -8,6 +8,7 @@
// can obtain one at:
// <https://mozilla.org/MPL/2.0/>.
use oct::vec;
use oct::vec::Vec;
#[test]
@ -33,3 +34,12 @@ fn test_vec_from_iter() {
[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);
}