Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
37 changes: 37 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
Licenses for third party components used by this project can be found below.

================================================================================
The MIT License applies to:

* The code in `src/vendor/slice.rs` which is adapted from the Rust standard library.
(https://github.com/rust-lang/rust/blob/3ea2fbcb2a872b7e1fa3cada256f8e97f9e6636f/library/core/src/slice/index.rs)
Copyright (c) The Rust Project Contributors

* The code in `src/vendor/vec/drain.rs` which is adapted from the Rust standard library.
(https://github.com/rust-lang/rust/blob/3ea2fbcb2a872b7e1fa3cada256f8e97f9e6636f/library/alloc/src/vec/drain.rs)
Copyright (c) The Rust Project Contributors

* The code in `src/vendor/vec/splice.rs` which is adapted from the Rust standard library.
(https://github.com/rust-lang/rust/blob/3ea2fbcb2a872b7e1fa3cada256f8e97f9e6636f/library/alloc/src/vec/splice.rs)
Copyright (c) The Rust Project Contributors

The MIT License (MIT)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================================================
86 changes: 86 additions & 0 deletions src/dynamic.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use core::mem::{self, ManuallyDrop};
use core::ptr;
use std::ops::RangeBounds;

use super::EcoVec;

Expand Down Expand Up @@ -162,6 +163,26 @@ impl DynamicVec {
}
}

#[inline]
pub fn insert_slice(&mut self, index: usize, bytes: &[u8]) {
match self.variant_mut() {
VariantMut::Inline(inline) => {
if inline.insert_slice(index, bytes).is_err() {
let needed = inline.len() + bytes.len();
let mut eco = EcoVec::with_capacity(needed.next_power_of_two());
let (a, b) = inline.as_slice().split_at(index);
eco.extend_from_byte_slice(a);
eco.extend_from_byte_slice(bytes);
eco.extend_from_byte_slice(b);
*self = Self::from_eco(eco);
}
}
VariantMut::Spilled(spilled) => {
spilled.splice(index..index, bytes.iter().copied());
}
}
}

#[inline]
pub fn clear(&mut self) {
match self.variant_mut() {
Expand All @@ -177,6 +198,19 @@ impl DynamicVec {
VariantMut::Spilled(spilled) => spilled.truncate(target),
}
}

#[inline]
pub fn remove_range<R>(&mut self, range: R)
where
R: RangeBounds<usize>,
{
match self.variant_mut() {
VariantMut::Inline(inline) => inline.remove_range(range),
VariantMut::Spilled(spilled) => {
spilled.drain(range);
}
}
}
}

impl DynamicVec {
Expand Down Expand Up @@ -349,6 +383,37 @@ impl InlineVec {
}
}

#[inline]
pub fn insert_slice(&mut self, index: usize, bytes: &[u8]) -> Result<(), ()> {
let len = self.len();
assert!(index <= len, "index {index} out of range for slice of length {len}");

let grown = len + bytes.len();
let tail_len = len - index;
if grown <= LIMIT {
let ptr = self.buf.as_mut_ptr();
unsafe {
// Safety: Checked that `index <= len` and
// `len + bytes.len() == grown < LIMIT`.
core::ptr::copy(ptr.add(index), ptr.add(index + bytes.len()), tail_len);

// Safety: Checked that `index <= len` and
// `len + bytes.len() == grown < LIMIT`.
core::ptr::copy_nonoverlapping(
bytes.as_ptr(),
ptr.add(index),
bytes.len(),
);

// Safety: Checked that `grown < LIMIT`
self.set_len(grown);
}
Ok(())
} else {
Err(())
}
}

#[inline]
pub fn truncate(&mut self, target: usize) {
if target < self.len() {
Expand All @@ -359,4 +424,25 @@ impl InlineVec {
}
}
}

#[inline]
pub fn remove_range<R>(&mut self, range: R)
where
R: RangeBounds<usize>,
{
let len = self.len();
let range = crate::vendor::slice::range(range, ..len);

let tail_len = len - range.end;
let target = len - range.len();
let ptr = self.buf.as_mut_ptr();
unsafe {
// Safety: The range is in bounds
core::ptr::copy(ptr.add(range.end), ptr.add(range.start), tail_len);

// Safety: Checked that it's smaller than the current length,
// which cannot exceed LIMIT itself.
self.set_len(target);
}
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ pub mod string;
pub mod vec;

mod dynamic;
mod vendor;

pub use self::string::EcoString;
pub use self::vec::EcoVec;
Expand Down
20 changes: 20 additions & 0 deletions src/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,18 @@ impl EcoString {
self.0.extend_from_slice(string.as_bytes());
}

/// Insert the given character at the index.
pub fn insert(&mut self, index: usize, c: char) {
assert!(self.is_char_boundary(index));
self.0.insert_slice(index, c.encode_utf8(&mut [0; 4]).as_bytes());
}

/// Insert the given string slice at the index.
pub fn insert_str(&mut self, index: usize, string: &str) {
assert!(self.is_char_boundary(index));
self.0.insert_slice(index, string.as_bytes());
}

/// Remove the last character from the string.
#[inline]
pub fn pop(&mut self) -> Option<char> {
Expand Down Expand Up @@ -187,6 +199,14 @@ impl EcoString {
}
}

/// Remove the character at the index.
pub fn remove(&mut self, index: usize) -> char {
assert!(self.is_char_boundary(index));
let char = self[index..].chars().next().unwrap();
self.0.remove_range(index..index + char.len_utf8());
char
}

/// Replaces all matches of a string with another string.
///
/// This is a bit less general that [`str::replace`] because the `Pattern`
Expand Down
144 changes: 119 additions & 25 deletions src/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ use core::hash::{Hash, Hasher};
use core::marker::PhantomData;
use core::mem;
use core::ops::Deref;
use core::ops::{Range, RangeBounds};
use core::ptr::{self, NonNull};

#[cfg(not(feature = "std"))]
use alloc::vec::Vec;

use crate::sync::atomic::{self, AtomicUsize, Ordering::*};
use crate::vendor::slice;
use crate::vendor::vec::{Drain, Splice};

/// Create a new [`EcoVec`] with the given elements.
/// ```
Expand Down Expand Up @@ -420,22 +423,62 @@ impl<T: Clone> EcoVec<T> {
return;
}

let rest = self.len - target;
unsafe {
// Safety:
// - Since `target < len`, we maintain `len <= capacity`.
self.len = target;
// Safety: reference count is `1` and `target < len`.
self.truncate_unchecked(target);
}
}

// Safety:
// The reference count is `1` because of `make_unique`.
// - The pointer returned by `data_mut()` is valid for `capacity`
// writes.
// - We have the invariant `len <= capacity`.
// - Thus, `data_mut() + target` is valid for `len - target` writes.
ptr::drop_in_place(ptr::slice_from_raw_parts_mut(
self.data_mut().add(target),
rest,
));
/// Removes the subslice indicated by the given range from the vector,
/// returning a double-ended iterator over the removed subslice.
///
/// The vector will be cloned if its reference count is larger than 1.
pub fn drain<R>(&mut self, range: R) -> Drain<'_, T>
where
R: RangeBounds<usize>,
{
// Memory safety
//
// When the Drain is first created, it shortens the length of
// the source vector to make sure no uninitialized or moved-from elements
// are accessible at all if the Drain's destructor never gets to run.
//
// Drain will ptr::read out the values to remove.
// When finished, remaining tail of the vec is copied back to cover
// the hole, and the vector length is restored to the new length.
//
let len = self.len();
let Range { start, end } = slice::range(range, ..len);

self.make_unique();

unsafe {
// set self.vec length's to start, to be safe in case Drain is leaked
self.len = start;
let range_slice =
core::slice::from_raw_parts(self.ptr.as_ptr().add(start), end - start);
Drain {
tail_start: end,
tail_len: len - end,
iter: range_slice.iter(),
vec: NonNull::from(self),
}
}
}

/// Creates a splicing iterator that replaces the specified range in the vector
/// with the given `replace_with` iterator and yields the removed items.
///
/// The vector will be cloned if its reference count is larger than 1.
#[inline]
pub fn splice<R, I>(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoIter>
where
R: RangeBounds<usize>,
I: IntoIterator<Item = T>,
{
Splice {
drain: self.drain(range),
replace_with: replace_with.into_iter(),
}
}

Expand All @@ -448,16 +491,7 @@ impl<T: Clone> EcoVec<T> {
let mut target = capacity;

if additional > capacity - self.len {
// Reserve at least the `additional` capacity, but also at least
// double the capacity to ensure exponential growth and finally
// jump directly to a minimum capacity to prevent frequent
// reallocation for small vectors.
target = self
.len
.checked_add(additional)
.unwrap_or_else(|| capacity_overflow())
.max(2 * capacity)
.max(Self::min_cap());
target = Self::amortized_cap(self.len, additional, capacity);
}

if !self.is_unique() {
Expand Down Expand Up @@ -527,12 +561,58 @@ impl<T: Clone> EcoVec<T> {
}

impl<T> EcoVec<T> {
/// Forces the length of the vector to `new_len`.
///
/// # Safety
/// - `new_len` must be less than or equal to [`Self::capacity()`].
/// - The elements at `old_len..new_len` must be initialized.
pub unsafe fn set_len(&mut self, new_len: usize) {
self.len = new_len;
}

/// Returns a raw pointer to the vector's buffer, or a dangling raw pointer
/// valid for zero sized reads if the vector didn't allocate.
pub fn as_ptr(&self) -> *const T {
self.ptr.as_ptr()
}

/// Returns a raw mutable pointer to the vector's buffer, or a dangling
/// raw pointer valid for zero sized reads if the vector didn't allocate.
pub fn as_mut_ptr(&mut self) -> *mut T {
self.ptr.as_ptr()
}

/// # Safety
///
/// May only be called if:
/// - the reference count is `1`, and
/// - `target < len` (i.e., this methods shrinks, it doesn't grow).
pub(crate) unsafe fn truncate_unchecked(&mut self, target: usize) {
let rest = self.len - target;
unsafe {
// Safety:
// - Since `target < len`, we maintain `len <= capacity`.
self.len = target;

// Safety:
// The reference count is `1` because of `make_unique`.
// - The pointer returned by `data_mut()` is valid for `capacity`
// writes.
// - We have the invariant `len <= capacity`.
// - Thus, `data_mut() + target` is valid for `len - target` writes.
ptr::drop_in_place(ptr::slice_from_raw_parts_mut(
self.data_mut().add(target),
rest,
));
}
}

/// Grow the capacity to at least the `target` size.
///
/// May only be called if:
/// - the reference count is `1`, and
/// - `target > capacity` (i.e., this methods grows, it doesn't shrink).
unsafe fn grow(&mut self, mut target: usize) {
pub(crate) unsafe fn grow(&mut self, mut target: usize) {
debug_assert!(self.is_unique());
debug_assert!(target > self.capacity());

Expand Down Expand Up @@ -717,6 +797,20 @@ impl<T> EcoVec<T> {
1
}
}

/// Compute the new amortized capacity when growing the allocation.
///
/// Reserve at least the `additional` capacity, but also at least
/// double the capacity to ensure exponential growth and finally
/// jump directly to a minimum capacity to prevent frequent
/// reallocation for small vectors.
#[inline]
pub(crate) fn amortized_cap(len: usize, additional: usize, capacity: usize) -> usize {
len.checked_add(additional)
.unwrap_or_else(|| capacity_overflow())
.max(2 * capacity)
.max(Self::min_cap())
}
}

impl<T: Clone> EcoVec<T> {
Expand Down
5 changes: 5 additions & 0 deletions src/vendor/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//! This module contains code that has been adapted from Rust standard library
//! with minimal changes.

pub mod slice;
pub mod vec;
Loading
Loading