pub mod finite;
pub mod seg_walker;
#[cfg(test)]
mod tests;
mod trait_impls;
use std::{
fmt,
io::{self, Read},
str::Utf8Error,
string::FromUtf8Error,
};
use front_vec::FrontString;
use tracing::instrument;
use crate::pq_rc::PqRc;
type Len = usize;
pub struct CharList<Tail: CharListTail> {
data: PqRc<StringRepr<Tail>, Len>,
}
#[derive(Clone)]
struct StringRepr<Tail: CharListTail> {
front_string: FrontString,
tail: Tail,
}
pub trait CharListTail: Clone + Default {
type Err: fmt::Debug + fmt::Display;
fn next_char_list(&self) -> Result<Option<&CharList<Self>>, Self::Err>;
fn len(&self) -> Result<usize, Self::Err>;
fn is_empty(&self) -> Result<bool, Self::Err> {
Ok(self.len()? == 0)
}
}
impl<Tail: CharListTail> CharList<Tail> {
pub fn new() -> Self {
Self::with_capacity_and_tail(0, Default::default())
}
pub fn with_capacity(capacity: usize) -> Self {
Self::with_capacity_and_tail(capacity, Default::default())
}
pub fn from_utf8(vec: Vec<u8>) -> Result<Self, FromUtf8Error> {
let s = String::from_utf8(vec)?;
Ok(Self::from_string_and_tail(s, Default::default()))
}
pub fn from_utf8_lossy(bytes: &[u8]) -> Self {
let s: String = String::from_utf8_lossy(bytes).into();
Self::from_string_and_tail(s, Default::default())
}
pub fn new_with_tail(tail: Tail) -> Self {
Self::with_capacity_and_tail(0, tail)
}
#[instrument(skip(tail))]
pub fn with_capacity_and_tail(capacity: usize, tail: Tail) -> Self {
Self {
data: PqRc::new(
StringRepr {
front_string: FrontString::with_capacity(capacity),
tail,
},
0,
),
}
}
pub fn tail(&self) -> &Tail {
&self.data.tail
}
pub fn segment_as_str(&self) -> &str {
let slice: &str = self.backing_string().as_ref();
let seg_start = slice.len() - self.segment_len();
&slice[seg_start..]
}
pub fn segment_as_bytes(&self) -> &[u8] {
self.segment_as_str().as_bytes()
}
#[instrument(ret, skip(self), fields(repr = %self))]
pub fn len(&self) -> Result<usize, Tail::Err> {
Ok(self.segment_len() + self.tail().len()?)
}
pub fn partial_len(&self) -> usize {
self.partial_segments().map(|seg| seg.segment_len()).sum()
}
#[instrument(ret, skip(self))]
pub fn segment_len(&self) -> usize {
PqRc::priority(&self.data)
}
pub fn is_empty(&self) -> Result<bool, Tail::Err> {
if self.segment_len() != 0 {
return Ok(false);
}
let Some(tail) = self.tail().next_char_list()? else {
return Ok(true);
};
tail.len().map(|l| l == 0)
}
#[instrument]
pub fn cons(&self, ch: char) -> Self {
let mut buf = [0u8; 4];
self.cons_str(ch.encode_utf8(&mut buf))
}
#[instrument(ret, fields(s = s.as_ref()))]
pub fn cons_str(&self, s: impl AsRef<str>) -> Self {
let s = s.as_ref();
Self {
data: unsafe {
PqRc::mutate_or_clone_raising_prio(
&self.data,
|repr_mut| {
repr_mut.front_string.push_str_front(s);
s.len()
},
|_string_ref| {
let mut new_string = FrontString::from(self.segment_as_str());
new_string.push_str_front(s);
let repr = StringRepr {
front_string: new_string,
tail: self.tail().clone(),
};
(s.len(), repr)
},
)
},
}
}
pub fn cons_char_list(&self, prefix: &Self) -> Self {
let prefix_len = prefix.partial_len();
let pqrc = unsafe {
PqRc::mutate_or_clone_raising_prio(
&self.data,
|repr_mut| {
for seg in prefix.partial_segments() {
repr_mut
.front_string
.prepend_from_bytes_iter(seg.segment_as_bytes().iter().copied());
}
prefix_len
},
|_repr_ref| {
let mut new_string = FrontString::from(self.segment_as_str());
for seg in prefix.partial_segments() {
new_string.prepend_from_bytes_iter(seg.segment_as_bytes().iter().copied());
}
let repr = StringRepr {
front_string: new_string,
tail: self.tail().clone(),
};
(prefix_len, repr)
},
)
};
Self { data: pqrc }
}
#[track_caller]
pub fn car_cdr(&self) -> Result<Option<(char, Self)>, Tail::Err> {
let Some(first_char) = self.segment_as_str().chars().next() else {
return Ok(None);
};
let new_len = self.segment_len() - first_char.len_utf8();
let cdr = Self {
data: PqRc::clone_with_priority(&self.data, new_len),
};
Ok(Some((first_char, cdr)))
}
pub unsafe fn from_utf8_unchecked(bytes: &[u8]) -> Self {
let s = unsafe { std::str::from_utf8_unchecked(bytes) };
let s = s.to_owned();
Self::from_string_and_tail(s, Default::default())
}
pub fn backing_string(&self) -> &FrontString {
&PqRc::inner(&self.data).front_string
}
#[instrument(skip(s, tail))]
pub fn from_string_and_tail(s: impl Into<String>, tail: Tail) -> Self {
let front_string: FrontString = FrontString::from(s.into());
tracing::Span::current().record("s", front_string.to_string());
let priority = front_string.len();
Self {
data: PqRc::new(StringRepr { front_string, tail }, priority),
}
}
pub fn from_utf8_and_tail(bytes: &[u8], tail: Tail) -> Result<Self, Utf8Error> {
let s = std::str::from_utf8(bytes)?;
Ok(Self::from_string_and_tail(s, tail))
}
pub fn from_utf8_lossy_and_tail(bytes: &[u8], tail: Tail) -> Self {
Self::from_string_and_tail(String::from_utf8_lossy(bytes).into_owned(), tail)
}
pub fn from_io_readable(readable: &mut impl Read) -> io::Result<Self> {
let mut buf = String::new();
let _ = readable.read_to_string(&mut buf)?;
Ok(Self::from(buf))
}
pub fn prepend_from_bytes_iter(
&self,
it: impl ExactSizeIterator<Item = u8>,
) -> Result<Self, FromUtf8Error> {
let v = it.collect::<Vec<_>>();
let s = String::from_utf8(v)?;
Ok(self.cons_str(s))
}
#[must_use = "`CharList::reserving` returns a new `CharList`, it doesn't mutate the current one in place."]
#[allow(dead_code)]
fn reserving(&self, additional_bytes: usize) -> Self {
let pqrc = unsafe {
PqRc::mutate_or_clone_raising_prio(
&self.data,
|mut_s| {
mut_s.front_string.reserve_front(additional_bytes);
0
},
|ref_s| {
let mut s = ref_s.clone();
s.front_string.reserve_front(additional_bytes);
(0, s)
},
)
};
Self { data: pqrc }
}
}