Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature std compatibility #13

Draft
wants to merge 28 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
d44385d
WIP: make len atomic, add and use methods using atomics
cehteh Sep 17, 2024
2076db3
Merge branch 'rust-cv:main' into main
cehteh Sep 17, 2024
c9d5388
rename methods in _exact/_strict, add 'atomic_append' feature, add 's…
cehteh Sep 17, 2024
ecc9554
add push_atomic()
cehteh Sep 17, 2024
993cad0
WIP: reserve/shrink API's
cehteh Sep 23, 2024
4bcab1f
FIX: the reseve functions need saturating add as well
cehteh Sep 23, 2024
decaeed
FIX: Miri, unaligned access
cehteh Sep 23, 2024
58d68a1
add: extend_from_slice()
cehteh Sep 23, 2024
00be0d8
add: extend_from_slice_atomic()
cehteh Sep 23, 2024
b34e0bd
DOC: fixing doc for reserve_cold()
cehteh Jan 20, 2025
9713321
Make HeaderVec being a NonNull<> instead a raw pointer
cehteh Jan 20, 2025
e2b5f0d
ADD: spare_capacity_mut() and set_len()
cehteh Jan 10, 2025
3fe8098
add support for zero length HeaderVec
cehteh Jan 18, 2025
4cde654
add a `std` feature flag
cehteh Jan 20, 2025
9f18d10
rename and export start_ptr()/mut to as_ptr()/mut
cehteh Jan 22, 2025
47af90f
FIX: Handle the case when resize_cold() would be a no-op
cehteh Jan 22, 2025
c2026c2
WIP: introduce WeakFixupFn, start removing Option<*const ()>
cehteh Jan 23, 2025
ca899af
CHANGE: modify push() using WeakFixupFn, add push_with_weakfix()
cehteh Jan 23, 2025
9775e59
CHANGE: WeakFixupFn for extend_from_slice()
cehteh Jan 23, 2025
71f42a1
refactor: put HeaderVecWeak into its own module
cehteh Jan 23, 2025
0fd6d9a
ADD: a lot `impl From`'s
cehteh Jan 23, 2025
30430e6
Make `extemd_from_slice()` generic over AsRef<[T]>
cehteh Jan 23, 2025
f00e8ad
FIX: enable From Cow/Vec/Box only when std is enabled
cehteh Jan 23, 2025
20a54c7
ADD: missing From<&str> for HeaderVec<.., u8>
cehteh Jan 23, 2025
09d6827
Add HeaderVec::from_header_slice(), simplify the From impl
cehteh Jan 24, 2025
d8c717e
ADD: truncate() and clear()
cehteh Jan 30, 2025
efc96c0
CHANGE: make the `std` feature default (breaks no_std)
cehteh Jan 30, 2025
a421e3f
ADD: `drain()` method and `Drain` iterator
cehteh Jan 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,9 @@ keywords = ["header", "heap", "vec", "vector", "graph"]
categories = ["no-std"]
license = "MIT"
readme = "README.md"

[features]
default = ["std", "atomic_append"]
std = []
atomic_append = []

10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,13 @@
Allows one to store a header struct and a vector all inline in the same memory on the heap and share weak versions for minimizing random lookups in data structures

If you use this without creating a weak ptr, it is safe. It is unsafe to create a weak pointer because you now have aliasing.

## Features

* `std`
Enables API's that requires stdlib features. Provides more compatibility to `Vec`.
This feature is enabled by default.
* `atomic_append`
Enables the `atomic_push` API's that allows extending a `HeaderVec` with interior
mutability from a single thread by a immutable handle.
This feature is enabled by default.
242 changes: 242 additions & 0 deletions src/drain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
#![cfg(feature = "std")]

use core::{
any::type_name,

Check warning on line 4 in src/drain.rs

View workflow job for this annotation

GitHub Actions / no-std

unused import: `any::type_name`
fmt,
mem::{self},
ptr::{self, NonNull},
};

use std::{iter::FusedIterator, mem::ManuallyDrop, slice};

Check failure on line 10 in src/drain.rs

View workflow job for this annotation

GitHub Actions / no-std

can't find crate for `std`

use crate::HeaderVec;

/// A draining iterator for `HeaderVec<H, T>`.
///
/// This `struct` is created by [`HeaderVec::drain`].
/// See its documentation for more.
///
/// # Feature compatibility
///
/// The `drain()` API and [`Drain`] iterator are only available when the `std` feature is
/// enabled.
///
/// # Example
///
/// ```
/// # use header_vec::HeaderVec;
/// let mut hv: HeaderVec<(), _> = HeaderVec::from([0, 1, 2]);
/// let iter: header_vec::Drain<'_, _, _> = hv.drain(..);
/// ```
pub struct Drain<'a, H, T> {
/// Index of tail to preserve
pub(super) tail_start: usize,
/// Length of tail
pub(super) tail_len: usize,
/// Current remaining range to remove
pub(super) iter: slice::Iter<'a, T>,
pub(super) vec: NonNull<HeaderVec<H, T>>,
}

impl<H: fmt::Debug, T: fmt::Debug> fmt::Debug for Drain<'_, H, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct(&format!(

Check failure on line 43 in src/drain.rs

View workflow job for this annotation

GitHub Actions / no-std

cannot find macro `format` in this scope
"Drain<{}, {}>",
type_name::<H>(),
type_name::<T>()
))
.field("header", unsafe { self.vec.as_ref() })
.field("iter", &self.iter.as_slice())
.finish()
}
}

impl<H, T> Drain<'_, H, T> {
/// Returns the remaining items of this iterator as a slice.
///
/// # Examples
///
/// ```
/// # use header_vec::HeaderVec;
/// let mut hv: HeaderVec<(), _> = HeaderVec::from(['a', 'b', 'c']);
/// let mut drain = hv.drain(..);
/// assert_eq!(drain.as_slice(), &['a', 'b', 'c']);
/// let _ = drain.next().unwrap();
/// assert_eq!(drain.as_slice(), &['b', 'c']);
/// ```
#[must_use]
pub fn as_slice(&self) -> &[T] {
self.iter.as_slice()
}

/// Keep unyielded elements in the source `HeaderVec`.
///
/// # Examples
///
/// ```
/// # use header_vec::HeaderVec;
/// let mut hv: HeaderVec<(), _> = HeaderVec::from(['a', 'b', 'c']);
/// let mut drain = hv.drain(..);
///
/// assert_eq!(drain.next().unwrap(), 'a');
///
/// // This call keeps 'b' and 'c' in the vec.
/// drain.keep_rest();
///
/// // If we wouldn't call `keep_rest()`,
/// // `hv` would be empty.
/// assert_eq!(hv.as_slice(), ['b', 'c']);
/// ```
pub fn keep_rest(self) {
let mut this = ManuallyDrop::new(self);

unsafe {
let source_vec = this.vec.as_mut();

let start = source_vec.len();
let tail = this.tail_start;

let unyielded_len = this.iter.len();
let unyielded_ptr = this.iter.as_slice().as_ptr();

// ZSTs have no identity, so we don't need to move them around.
if std::mem::size_of::<T>() != 0 {
let start_ptr = source_vec.as_mut_ptr().add(start);

// memmove back unyielded elements
if unyielded_ptr != start_ptr {
let src = unyielded_ptr;
let dst = start_ptr;

ptr::copy(src, dst, unyielded_len);
}

// memmove back untouched tail
if tail != (start + unyielded_len) {
let src = source_vec.as_ptr().add(tail);
let dst = start_ptr.add(unyielded_len);
ptr::copy(src, dst, this.tail_len);
}
}

source_vec.set_len(start + unyielded_len + this.tail_len);
}
}
}

impl<H, T> AsRef<[T]> for Drain<'_, H, T> {
fn as_ref(&self) -> &[T] {
self.as_slice()
}
}

unsafe impl<H: Sync, T: Sync> Sync for Drain<'_, H, T> {}
unsafe impl<H: Send, T: Send> Send for Drain<'_, H, T> {}

impl<H, T> Iterator for Drain<'_, H, T> {
type Item = T;

#[inline]
fn next(&mut self) -> Option<T> {
self.iter
.next()
.map(|elt| unsafe { ptr::read(elt as *const _) })
}

fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}

impl<H, T> DoubleEndedIterator for Drain<'_, H, T> {
#[inline]
fn next_back(&mut self) -> Option<T> {
self.iter
.next_back()
.map(|elt| unsafe { ptr::read(elt as *const _) })
}
}

impl<H, T> Drop for Drain<'_, H, T> {
fn drop(&mut self) {
/// Moves back the un-`Drain`ed elements to restore the original `Vec`.
struct DropGuard<'r, 'a, H, T>(&'r mut Drain<'a, H, T>);

impl<H, T> Drop for DropGuard<'_, '_, H, T> {
fn drop(&mut self) {
if self.0.tail_len > 0 {
unsafe {
let source_vec = self.0.vec.as_mut();
// memmove back untouched tail, update to new length
let start = source_vec.len();
let tail = self.0.tail_start;
if tail != start {
let src = source_vec.as_ptr().add(tail);
let dst = source_vec.as_mut_ptr().add(start);
ptr::copy(src, dst, self.0.tail_len);
}
source_vec.set_len(start + self.0.tail_len);
}
}
}
}

let iter = mem::take(&mut self.iter);
let drop_len = iter.len();

let mut vec = self.vec;

// unstable: if T::IS_ZST { instead we use size_of
if mem::size_of::<T>() == 0 {
// ZSTs have no identity, so we don't need to move them around, we only need to drop the correct amount.
// this can be achieved by manipulating the Vec length instead of moving values out from `iter`.
unsafe {
let vec = vec.as_mut();
let old_len = vec.len();
vec.set_len(old_len + drop_len + self.tail_len);
vec.truncate(old_len + self.tail_len);
}

return;
}

// ensure elements are moved back into their appropriate places, even when drop_in_place panics
let _guard = DropGuard(self);

if drop_len == 0 {
return;
}

// as_slice() must only be called when iter.len() is > 0 because
// it also gets touched by vec::Splice which may turn it into a dangling pointer
// which would make it and the vec pointer point to different allocations which would
// lead to invalid pointer arithmetic below.
let drop_ptr = iter.as_slice().as_ptr();

unsafe {
// drop_ptr comes from a slice::Iter which only gives us a &[T] but for drop_in_place
// a pointer with mutable provenance is necessary. Therefore we must reconstruct
// it from the original vec but also avoid creating a &mut to the front since that could
// invalidate raw pointers to it which some unsafe code might rely on.
let vec_ptr = vec.as_mut().as_mut_ptr();

// PLANNED: let drop_offset = drop_ptr.sub_ptr(vec_ptr); is in nightly
let drop_offset = usize::try_from(drop_ptr.offset_from(vec_ptr)).unwrap_unchecked();
let to_drop = ptr::slice_from_raw_parts_mut(vec_ptr.add(drop_offset), drop_len);
ptr::drop_in_place(to_drop);
}
}
}

impl<H, T> FusedIterator for Drain<'_, H, T> {}

// PLANNED: unstable features
// impl<H, T> ExactSizeIterator for Drain<'_, H, T> {
// fn is_empty(&self) -> bool {
// self.iter.is_empty()
// }
// }
//
// #[unstable(feature = "trusted_len", issue = "37572")]
// unsafe impl<H, T> TrustedLen for Drain<'_, H, T> {}
//
61 changes: 61 additions & 0 deletions src/future_slice.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//! This module re-implements a unstable slice functions, these should be removed once they
//! are stabilized. These is copy-pasted with slight modifications from std::slice for
//! functions that do not need language magic.

use std::ops;

Check failure on line 5 in src/future_slice.rs

View workflow job for this annotation

GitHub Actions / no-std

can't find crate for `std`

#[track_caller]
#[must_use]
pub(crate) fn range<R>(range: R, bounds: ops::RangeTo<usize>) -> ops::Range<usize>
where
R: ops::RangeBounds<usize>,
{
let len = bounds.end;

let start = match range.start_bound() {
ops::Bound::Included(&start) => start,
ops::Bound::Excluded(start) => start
.checked_add(1)
.unwrap_or_else(|| slice_start_index_overflow_fail()),
ops::Bound::Unbounded => 0,
};

let end = match range.end_bound() {
ops::Bound::Included(end) => end
.checked_add(1)
.unwrap_or_else(|| slice_end_index_overflow_fail()),
ops::Bound::Excluded(&end) => end,
ops::Bound::Unbounded => len,
};

if start > end {
slice_index_order_fail(start, end);
}
if end > len {
slice_end_index_len_fail(end, len);
}

ops::Range { start, end }
}

#[track_caller]
const fn slice_start_index_overflow_fail() -> ! {
panic!("attempted to index slice from after maximum usize");

Check failure on line 43 in src/future_slice.rs

View workflow job for this annotation

GitHub Actions / no-std

cannot find macro `panic` in this scope
}

#[track_caller]
const fn slice_end_index_overflow_fail() -> ! {
panic!("attempted to index slice up to maximum usize");

Check failure on line 48 in src/future_slice.rs

View workflow job for this annotation

GitHub Actions / no-std

cannot find macro `panic` in this scope
}

#[track_caller]
fn slice_index_order_fail(index: usize, end: usize) -> ! {
panic!("slice index start is larger than end, slice index starts at {index} but ends at {end}")

Check failure on line 53 in src/future_slice.rs

View workflow job for this annotation

GitHub Actions / no-std

cannot find macro `panic` in this scope
}

#[track_caller]
fn slice_end_index_len_fail(index: usize, len: usize) -> ! {
panic!(

Check failure on line 58 in src/future_slice.rs

View workflow job for this annotation

GitHub Actions / no-std

cannot find macro `panic` in this scope
"slice end index is out of range for slice, range end index {index} out of range for slice of length {len}"
)
}
Loading
Loading