use core::{fmt::Debug, ops::Index};
use alloc::vec::Vec;
use crate::FixedCompactStrings;
pub struct FixedCompactBytestrings {
pub(crate) data: Vec<u8>,
pub(crate) starts: Vec<usize>,
}
impl FixedCompactBytestrings {
#[must_use]
pub const fn new() -> Self {
Self {
data: Vec::new(),
starts: Vec::new(),
}
}
#[must_use]
pub fn with_capacity(data_capacity: usize, capacity_meta: usize) -> Self {
Self {
data: Vec::with_capacity(data_capacity),
starts: Vec::with_capacity(capacity_meta),
}
}
pub fn push<S>(&mut self, bytestring: S)
where
S: AsRef<[u8]>,
{
let bytestr = bytestring.as_ref();
self.starts.push(self.data.len());
self.data.extend_from_slice(bytestr);
}
#[must_use]
pub fn get(&self, index: usize) -> Option<&[u8]> {
let &start = self.starts.get(index)?;
let &next = self
.starts
.get(index.checked_add(1)?)
.unwrap_or(&self.data.len());
if cfg!(feature = "no_unsafe") {
self.data.get(start..next)
} else {
unsafe { Some(self.data.get_unchecked(start..next)) }
}
}
#[must_use]
#[cfg(not(feature = "no_unsafe"))]
pub unsafe fn get_unchecked(&self, index: usize) -> &[u8] {
let start = *self.starts.get_unchecked(index);
let next = *self.starts.get(index + 1).unwrap_or(&self.data.len());
self.data.get_unchecked(start..next)
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.starts.len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
#[must_use]
pub fn capacity(&self) -> usize {
self.data.capacity()
}
#[inline]
#[must_use]
pub fn capacity_meta(&self) -> usize {
self.starts.capacity()
}
pub fn clear(&mut self) {
self.data.clear();
self.starts.clear();
}
#[inline]
pub fn shrink_to_fit(&mut self) {
self.data.shrink_to_fit();
}
#[inline]
pub fn shrink_meta_to_fit(&mut self) {
self.starts.shrink_to_fit();
}
#[inline]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.data.shrink_to(min_capacity);
}
#[inline]
pub fn shrink_meta_to(&mut self, min_capacity: usize) {
self.starts.shrink_to(min_capacity);
}
#[track_caller]
pub fn remove(&mut self, index: usize) {
#[cold]
#[inline(never)]
#[track_caller]
fn assert_failed(index: usize, len: usize) -> ! {
panic!("removal index (is {index}) should be < len (is {len})");
}
let len = self.len();
if index >= len {
assert_failed(index, len);
}
let inner_len = self.data.len();
let start = self.starts.remove(index);
let next = *self.starts.get(index).unwrap_or(&inner_len);
let len = next - start;
for s in self.starts.iter_mut().skip(index) {
*s -= len;
}
if cfg!(feature = "no_unsafe") {
self.data.copy_within(start + len..inner_len, start);
} else {
unsafe {
let ptr = self.data.as_mut_ptr().add(start);
core::ptr::copy(ptr.add(len), ptr, inner_len - start - len);
self.data.set_len(inner_len - len);
}
}
}
#[inline]
pub fn iter(&self) -> Iter<'_> {
Iter::new(self)
}
}
impl Clone for FixedCompactBytestrings {
fn clone(&self) -> Self {
let mut data = Vec::with_capacity(self.starts.windows(2).map(|s| s[1] - s[0]).sum());
let mut meta = Vec::with_capacity(self.starts.len());
for bytes in self {
meta.push(bytes.len());
data.extend_from_slice(bytes);
}
Self { data, starts: meta }
}
}
impl PartialEq for FixedCompactBytestrings {
fn eq(&self, other: &Self) -> bool {
let len = self.len();
if len != other.len() {
return false;
}
for idx in 0..len {
if self[idx] != other[idx] {
return false;
}
}
true
}
}
impl Debug for FixedCompactBytestrings {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
impl<S> Extend<S> for FixedCompactBytestrings
where
S: AsRef<[u8]>,
{
#[inline]
fn extend<I: IntoIterator<Item = S>>(&mut self, iter: I) {
for s in iter {
self.push(&s);
}
}
}
impl Index<usize> for FixedCompactBytestrings {
type Output = [u8];
#[inline]
fn index(&self, index: usize) -> &Self::Output {
self.get(index).unwrap()
}
}
#[must_use = "Iterators are lazy and do nothing unless consumed"]
pub struct Iter<'a> {
data: &'a [u8],
starts: core::slice::Iter<'a, usize>,
}
impl<'a> Iter<'a> {
#[inline]
pub fn new(inner: &'a FixedCompactBytestrings) -> Self {
Self {
data: &inner.data,
starts: inner.starts.iter(),
}
}
}
impl<'a> Iterator for Iter<'a> {
type Item = &'a [u8];
fn next(&mut self) -> Option<Self::Item> {
let &start = self.starts.next()?;
let &end = self.starts.clone().next().unwrap_or(&self.data.len());
if cfg!(feature = "no_unsafe") {
self.data.get(start..end)
} else {
unsafe { Some(self.data.get_unchecked(start..end)) }
}
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
let &start = self.starts.nth(n)?;
let &end = self.starts.clone().next().unwrap_or(&self.data.len());
if cfg!(feature = "no_unsafe") {
self.data.get(start..end)
} else {
unsafe { Some(self.data.get_unchecked(start..end)) }
}
}
#[inline]
fn count(self) -> usize
where
Self: Sized,
{
self.len()
}
#[inline]
fn last(mut self) -> Option<Self::Item>
where
Self: Sized,
{
self.next_back()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.starts.size_hint()
}
}
impl<'a> DoubleEndedIterator for Iter<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
let &start = self.starts.next_back()?;
let end = self.data.len();
let out = if cfg!(feature = "no_unsafe") {
self.data.get(start..end)
} else {
unsafe { Some(self.data.get_unchecked(start..end)) }
};
self.data = &self.data[..start];
out
}
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
let mut fork = self.starts.clone();
let &start = self.starts.nth_back(n)?;
let end = if n == 0 {
self.data.len()
} else {
*fork.nth_back(n - 1)?
};
let out = if cfg!(feature = "no_unsafe") {
self.data.get(start..end)
} else {
unsafe { Some(self.data.get_unchecked(start..end)) }
};
self.data = &self.data[..start];
out
}
}
impl ExactSizeIterator for Iter<'_> {
#[inline]
fn len(&self) -> usize {
self.starts.len()
}
}
impl<'a> IntoIterator for &'a FixedCompactBytestrings {
type Item = &'a [u8];
type IntoIter = Iter<'a>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<S> FromIterator<S> for FixedCompactBytestrings
where
S: AsRef<[u8]>,
{
fn from_iter<I: IntoIterator<Item = S>>(iter: I) -> Self {
let iter = iter.into_iter();
let meta_capacity = match iter.size_hint() {
(a, Some(b)) if a == b => a,
_ => 0,
};
let mut out = FixedCompactBytestrings::with_capacity(0, meta_capacity);
for s in iter {
out.push(s);
}
out
}
}
impl<S, I> From<I> for FixedCompactBytestrings
where
S: AsRef<[u8]>,
I: IntoIterator<Item = S>,
{
#[inline]
fn from(value: I) -> Self {
FromIterator::from_iter(value)
}
}
impl From<FixedCompactStrings> for FixedCompactBytestrings {
fn from(value: FixedCompactStrings) -> Self {
value.0
}
}
#[cfg(test)]
mod tests {
use crate::FixedCompactBytestrings;
#[test]
fn exact_size_iterator() {
let mut cmpbytes = FixedCompactBytestrings::new();
cmpbytes.push(b"One");
cmpbytes.push(b"Two");
cmpbytes.push(b"Three");
let mut iter = cmpbytes.iter();
assert_eq!(iter.len(), 3);
let _ = iter.next();
assert_eq!(iter.len(), 2);
let _ = iter.next();
assert_eq!(iter.len(), 1);
let _ = iter.next();
assert_eq!(iter.len(), 0);
let _ = iter.next();
assert_eq!(iter.len(), 0);
}
#[test]
fn double_ended_iterator() {
let mut cmpbytes = FixedCompactBytestrings::new();
cmpbytes.push(b"One");
cmpbytes.push(b"Two");
cmpbytes.push(b"Three");
cmpbytes.push(b"Four");
let mut iter = cmpbytes.iter();
assert_eq!(iter.next(), Some(b"One".as_slice()));
assert_eq!(iter.next_back(), Some(b"Four".as_slice()));
assert_eq!(iter.next(), Some(b"Two".as_slice()));
assert_eq!(iter.next_back(), Some(b"Three".as_slice()));
assert_eq!(iter.next(), None);
assert_eq!(iter.next_back(), None);
}
}