#![allow(unused_variables)]
#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
#![allow(unused_parens)]
#![allow(unused_assignments)]
#![allow(unused_mut)]
#![allow(unused_imports)]
#![allow(dead_code)]
//! fixed strings with circular-queue backing
//extern crate std;
#[derive(Debug,Copy,Clone)]
pub struct cstr<const N : usize=32>
{
chrs: [u8;N],
front: u16,
len: u16,
} //cstr
impl<const N:usize> cstr<N>
{
/// create `cstr` from `&str` with silent truncation; panics if
/// N is greater than 65536
pub fn make(src:&str) -> cstr<N> {
if N > 65536 { panic!("cstr strings are limited to a maximum capacity of 65536");}
let mut m = cstr::<N>::new();
let length = core::cmp::min(N,src.len());
m.chrs[..length].copy_from_slice(&src.as_bytes()[..length]);
m.len = length as u16;
m
}//make
/// version of make that does not truncate. Also checks if N is
/// not greater than 65536 without panic.
pub fn try_make(src:&str) -> Option<cstr<N>> {
let length = src.len();
if length>N || N>65536 {return None;}
let mut m = cstr::new();
m.chrs[..].copy_from_slice(&src.as_bytes()[..length]);
m.len = length as u16;
Some(m)
}//try_make
/// version of make that returns a pair consisting of the made
/// `cstr` and the remainder `&str` that was truncated; panics if
/// N is greater than 65536
pub fn make_remainder(src:&str) -> (cstr<N>,&str) {
if N > 65536 { panic!("cstr strings are limited to a maximum capacity of 65536");}
let mut m = cstr::new();
let length = core::cmp::min(N,src.len());
m.chrs[..].copy_from_slice(&src.as_bytes()[..length]);
m.len = length as u16;
(m,&src[length..])
}//try_make
// make from a pair of str slices, does not truncate, and checks that
// N is not greater than 65536 without panic
pub fn from_pair(left:&str, right:&str) -> Option<cstr<N>> {
let (llen,rlen) = (left.len(), right.len());
if llen+rlen > N || N > 65536 { return None; }
let mut m = cstr::new();
m.len = (llen+rlen) as u16;
m.chrs[..llen].copy_from_slice(&left.as_bytes()[..llen]);
m.chrs[llen..].copy_from_slice(&right.as_bytes()[llen..]);
Some(m)
}//from_pair
/// checks if the underlying representation of the string is contiguous
/// (without wraparound).
#[inline(always)]
pub fn is_contiguous(&self) -> bool {
(self.front as usize + self.len as usize) < N
}
/// resets the internal representation of the cstr so that it is
/// represented contiguously, without wraparound. **Calling this function
/// has non-constant time cost both in terms of speed and memory** as
/// it requires a secondary buffer as well as copying.**
pub fn reset(&mut self) {
if self.front==0 {return;}
let mut mhrs = [0;N];
for i in 0..self.len as usize {
mhrs[i] = self.chrs[self.index(i)];
}
self.chrs = mhrs;
self.front = 0;
}//reset
/// pushes string to the end of the string, returns remainder
pub fn push_str<'t>(&mut self, src:&'t str) -> &'t str {
let srclen = src.len();
let slen = self.len as usize;
let bytes = &src.as_bytes();
let length = core::cmp::min(slen+srclen , N);
let remain = if N>(slen+srclen) {0} else {(srclen+slen)-N};
let mut i = 0;
while i<srclen && i+slen<N {
self.chrs[self.index(slen+i)] = bytes[i];
i += 1;
}//while
self.len += i as u16;
&src[srclen-remain..]
}//push_str
/// pushes string to the **front** of the string, returns remainder.
/// because of the circular-queue backing, this operation as the same
/// cost as pushing to the back of the string ([Self::push_str]).
pub fn push_front<'t>(&mut self, src:&'t str) -> &'t str {
let srclen = src.len();
let slen = self.len as usize;
let bytes = &src.as_bytes();
let length = core::cmp::min(slen+srclen , N);
let remain = if N>=(slen+srclen) {0} else {(srclen+slen)-N};
let mut i = 0;
while i<srclen && i+slen<N {
self.front = (self.front + (N as u16) -1) % (N as u16);
self.chrs[self.front as usize] = bytes[srclen-1-i];
i += 1;
}//while
self.len += i as u16;
&src[..remain]
}//push_front
/// pushes a single character to the end of the string, returning
/// true on success.
pub fn push_char(&mut self, c:char) -> bool {
let clen = c.len_utf8();
if self.len as usize + clen > N {return false;}
let mut buf = [0u8;4]; // char buffer
let bstr = c.encode_utf8(&mut buf);
self.push_str(bstr);
true
/*
let clen = c.len_utf8();
let slen = self.len;
if slen+clen >= N {return false;}
let mut buf = [0u8;4]; // char buffer
c.encode_utf8(&mut buf);
for i in 0..clen {
self.chrs[slen+i] = buf[i];
}
self.len = slen+clen
true
*/
}// push_char
/// remove and return last character in string, if it exists
pub fn pop_char(&mut self) -> Option<char> {
if self.len()==0 {return None;}
let (l,r) = self.to_strs();
let right = if r.len()>0 {r} else {l};
let (ci,lastchar) = right.char_indices().last().unwrap();
self.len = if r.len()>0 {(l.len() + ci) as u16} else {ci as u16};
Some(lastchar)
}//pop
/// remove and return first character in string, if it exists
pub fn pop_char_front(&mut self) -> Option<char> {
if self.len()==0 {return None;}
let (left,r) = self.to_strs();
let firstchar = left.chars().next().unwrap();
let clen = firstchar.len_utf8() as u16;
self.front = (self.front+clen) % (N as u16) ;
self.len -= clen;
Some(firstchar)
}//pop_char_front
/// right-truncates string up to *byte* position n. **Panics** if n is
/// not on a character boundary, similar to [String::truncate]. No effect
/// if n is greater than or equal to the length of the string.
pub fn truncate_bytes(&mut self, n: usize) {
if (n<self.len as usize) {
let (a,b) = self.to_strs();
if n<a.len() {
assert!(a.is_char_boundary(n));
}
else {
assert!(b.is_char_boundary(n-a.len()));
}
self.len = n as u16;
}
}
/// left-truncates string up to *byte* position n. **Panics** if n is
/// not on a character boundary, similar to [String::truncate]. No effect
///if n is greater than the length of the string.
pub fn truncate_left(&mut self, n: usize) {
if (n>0 && n<=self.len as usize) {
let (a,b) = self.to_strs();
if n<a.len() {
assert!(a.is_char_boundary(n));
}
else {
assert!(b.is_char_boundary(n-a.len()));
}
self.front = ((self.front as usize + n)%N) as u16;
self.len -= n as u16;
}
}//truncate_left
/// finds the position of first character that satisfies given predicate
pub fn find<P>(&self, predicate: P) -> Option<usize>
where P : Fn(char) -> bool
{
let (a,b) = self.to_strs();
if let Some(pos) = a.find(|x:char|predicate(x)) {
Some(pos)
}
else if let Some(pos) = b.find(|x:char|predicate(x)) {
Some(a.len() + pos)
}
else { None }
}//find
/// finds position of first matching substring
pub fn find_substr(&self, s:&str) -> Option<usize> {
let (a,b) = self.to_strs();
if let Some(pos) = a.find(s) {
Some(pos)
}
else if let Some(pos) = b.find(s) {
Some(a.len() + pos)
}
else { None }
}//find_substr
/// finds the position of last character that satisfies given predicate
pub fn rfind<P>(&self, predicate: P) -> Option<usize>
where P : Fn(char) -> bool
{
let (a,b) = self.to_strs();
if let Some(pos) = b.find(|x:char|predicate(x)) {
Some(a.len()+pos)
}
else if let Some(pos) = a.find(|x:char|predicate(x)) {
Some(pos)
}
else { None }
}//find
/// finds position of last matching substring
pub fn rfind_substr(&self, s:&str) -> Option<usize> {
let (a,b) = self.to_strs();
if let Some(pos) = b.find(s) {
Some(a.len()+pos)
}
else if let Some(pos) = a.find(s) {
Some(pos)
}
else { None }
}//find_substr
// **in-place** trimming of white spaces at the front of the string
pub fn trim_left(&mut self) {
let (a,b) = self.to_strs();
let offset;
if let Some(i) = a.find(|c:char|!c.is_whitespace()) {
offset = i;
}
else if let Some(k) = b.find(|c:char|!c.is_whitespace()) {
offset = a.len() + k;
}
else {
offset = a.len() + b.len();
}
self.front = ((self.front as usize + offset)%N) as u16;
self.len -= offset as u16;
}//trim_left
// **in-place** trimming of white spaces at the end of the string
pub fn trim_right(&mut self) {
let (a,b) = self.to_strs();
let offset;
if b.len()==0 {
if let Some(k) = a.rfind(|c:char|!c.is_whitespace()) {
offset = a.len() - k;
}
else {
offset = a.len();
}
}//contiguous
else if let Some(i) = b.rfind(|c:char|!c.is_whitespace()) {
offset = b.len() - i;
}
else if let Some(k) = a.rfind(|c:char|!c.is_whitespace()) {
offset = b.len() + (a.len() - k);
}
else {
offset = a.len() + b.len();
}
self.len -= offset as u16;
}//trim_right
// convenience
#[inline(always)]
fn endi(&self) -> usize { // index of last value plus 1
(self.front as usize + self.len as usize )%N
}// last
#[inline(always)]
fn index(&self, i:usize) -> usize {
(self.front as usize +i)%N
} // index of ith vale
///////// doesn't work on non-ascii strings, because of char boundaries
/// length of string in bytes
#[inline(always)]
pub fn len(&self) -> usize { self.len as usize }
/// construct new, empty string (same as `cstr::default`)
#[inline(always)]
pub fn new() -> Self {
Self::default()
}//new
/// returns a pair of string slices `(left,right)` which, if concactenated,
/// will yield an equivalent string underneath. In case of no wraparound,
/// the right str will be empty.
pub fn to_strs(&self) -> (&str,&str) {
let answer;
if self.len()==0 {answer = ("","");}
else if self.is_contiguous() {
answer = (core::str::from_utf8(&self.chrs[self.front as usize .. self.endi()]).unwrap(),
"")
}
else {
answer=(core::str::from_utf8(&self.chrs[self.front as usize .. ]).unwrap(),
core::str::from_utf8(&self.chrs[.. self.endi()]).unwrap())
}
answer
}//to_strs
}//main impl
impl<const N :usize> Default for cstr<N> {
fn default() -> Self {
cstr {
chrs: [0;N],
front: 0,
len:0,
}
}
}//impl default
/////////// need Eq, Ord, etc. and special iterator implementation