use core::{fmt::Debug, ops::Index};
use alloc::vec::Vec;
use crate::{metadata::Metadata, CompactStrings};
pub struct CompactBytestrings {
pub(crate) data: Vec<u8>,
pub(crate) meta: Vec<Metadata>,
}
impl CompactBytestrings {
#[must_use]
pub const fn new() -> Self {
Self {
data: Vec::new(),
meta: Vec::new(),
}
}
#[must_use]
pub fn with_capacity(data_capacity: usize, capacity_meta: usize) -> Self {
Self {
data: Vec::with_capacity(data_capacity),
meta: Vec::with_capacity(capacity_meta),
}
}
pub fn push<S>(&mut self, bytestring: S)
where
S: AsRef<[u8]>,
{
let bytestr = bytestring.as_ref();
self.meta
.push(Metadata::new(self.data.len(), bytestr.len()));
self.data.extend_from_slice(bytestr);
}
#[must_use]
pub fn get(&self, index: usize) -> Option<&[u8]> {
let (start, len) = self.meta.get(index)?.as_tuple();
if cfg!(feature = "no_unsafe") {
self.data.get(start..start + len)
} else {
unsafe { Some(self.data.get_unchecked(start..start + len)) }
}
}
#[must_use]
#[cfg(not(feature = "no_unsafe"))]
pub unsafe fn get_unchecked(&self, index: usize) -> &[u8] {
let (start, len) = self.meta.get_unchecked(index).as_tuple();
self.data.get_unchecked(start..start + len)
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.meta.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.meta.capacity()
}
pub fn clear(&mut self) {
self.data.clear();
self.meta.clear();
}
#[inline]
pub fn shrink_to_fit(&mut self) {
self.data.shrink_to_fit();
}
#[inline]
pub fn shrink_meta_to_fit(&mut self) {
self.meta.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.meta.shrink_to(min_capacity);
}
#[track_caller]
pub fn ignore(&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);
}
self.meta.remove(index);
}
#[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 (start, len) = self.meta.remove(index).as_tuple();
let inner_len = self.data.len();
for meta in self.meta.iter_mut().skip(index) {
meta.start -= start;
}
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 CompactBytestrings {
fn clone(&self) -> Self {
let mut data = Vec::with_capacity(self.meta.iter().map(|m| m.len).sum());
let mut meta = Vec::with_capacity(self.meta.len());
for bytes in self {
meta.push(Metadata {
start: data.len(),
len: bytes.len(),
});
data.extend_from_slice(bytes);
}
Self { data, meta }
}
}
impl PartialEq for CompactBytestrings {
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 CompactBytestrings {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
impl<S> Extend<S> for CompactBytestrings
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 CompactBytestrings {
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],
iter: core::slice::Iter<'a, Metadata>,
}
impl<'a> Iter<'a> {
#[inline]
pub fn new(inner: &'a CompactBytestrings) -> Self {
Self {
data: &inner.data,
iter: inner.meta.iter(),
}
}
}
impl<'a> Iterator for Iter<'a> {
type Item = &'a [u8];
fn next(&mut self) -> Option<Self::Item> {
let (start, len) = self.iter.next()?.as_tuple();
if cfg!(feature = "no_unsafe") {
self.data.get(start..start + len)
} else {
unsafe { Some(self.data.get_unchecked(start..start + len)) }
}
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
let (start, len) = self.iter.nth(n)?.as_tuple();
if cfg!(feature = "no_unsafe") {
self.data.get(start..start + len)
} else {
unsafe { Some(self.data.get_unchecked(start..start + len)) }
}
}
#[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.iter.size_hint()
}
}
impl<'a> DoubleEndedIterator for Iter<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
let (start, len) = self.iter.next_back()?.as_tuple();
if cfg!(feature = "no_unsafe") {
self.data.get(start..start + len)
} else {
unsafe { Some(self.data.get_unchecked(start..start + len)) }
}
}
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
let (start, len) = self.iter.nth_back(n)?.as_tuple();
if cfg!(feature = "no_unsafe") {
self.data.get(start..start + len)
} else {
unsafe { Some(self.data.get_unchecked(start..start + len)) }
}
}
}
impl ExactSizeIterator for Iter<'_> {
#[inline]
fn len(&self) -> usize {
self.iter.len()
}
}
impl<'a> IntoIterator for &'a CompactBytestrings {
type Item = &'a [u8];
type IntoIter = Iter<'a>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<S> FromIterator<S> for CompactBytestrings
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 = CompactBytestrings::with_capacity(0, meta_capacity);
for s in iter {
out.push(s);
}
out
}
}
impl<S, I> From<I> for CompactBytestrings
where
S: AsRef<[u8]>,
I: IntoIterator<Item = S>,
{
#[inline]
fn from(value: I) -> Self {
FromIterator::from_iter(value)
}
}
impl From<CompactStrings> for CompactBytestrings {
fn from(value: CompactStrings) -> Self {
value.0
}
}
#[cfg(test)]
mod tests {
use crate::CompactBytestrings;
#[test]
fn exact_size_iterator() {
let mut cmpbytes = CompactBytestrings::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 = CompactBytestrings::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);
}
}