#![allow(clippy::indexing_slicing)]
#![allow(clippy::unwrap_used)]
use crate::buf::Buffer;
use crate::iterators::LendingIterator;
use core::ops::{Index, IndexMut};
use core::str::Utf8Error;
use irox_bits::{Bits, BitsErrorKind, Error, MutBits, ReadFromBEBits, WriteToBEBits};
pub type StrBuf<const N: usize> = FixedU8Buf<N>;
#[derive(Clone)]
pub struct FixedU8Buf<const N: usize> {
buf: [u8; N],
len: usize,
}
impl<const N: usize> FixedU8Buf<N> {
pub const fn new() -> Self {
Self {
buf: [0u8; N],
len: 0,
}
}
pub fn take(self) -> [u8; N] {
self.buf
}
pub fn as_buf_default(&mut self) -> [u8; N] {
let out = core::mem::replace(&mut self.buf, [0u8; N]);
self.clear();
out
}
pub fn iter(&self) -> FixedU8BufIter<'_, N> {
FixedU8BufIter { buf: self, idx: 0 }
}
pub const fn len(&self) -> usize {
self.len
}
pub const fn is_empty(&self) -> bool {
self.len == 0
}
pub fn as_ref_used(&self) -> &[u8] {
self.as_ref()
}
pub fn as_mut_full(&mut self) -> &mut [u8] {
self.as_mut()
}
pub fn as_mut_used(&mut self) -> &mut [u8] {
&mut self.buf[0..self.len]
}
pub fn update_length(&mut self, new_len: usize) -> Result<(), Error> {
if new_len > N {
return Err(BitsErrorKind::OutOfMemory.into());
}
self.len = new_len;
Ok(())
}
pub fn push_char(&mut self, c: char) -> Result<(), Error> {
let mut buf = [0u8; 4];
let used = c.encode_utf8(&mut buf).len();
if self.len + used > N {
return Err(BitsErrorKind::OutOfMemory.into());
}
Ok(())
}
pub fn append(&mut self, buf: &[u8]) -> Result<(), Error> {
if self.len + buf.len() > N {
return Err(BitsErrorKind::OutOfMemory.into());
}
let start = self.len;
let end = start + buf.len();
self.buf[start..end].copy_from_slice(buf);
self.len = end;
Ok(())
}
pub fn as_str(&self) -> Result<&str, Utf8Error> {
core::str::from_utf8(self.as_ref_used())
}
pub fn as_str_mut(&mut self) -> Result<&mut str, Utf8Error> {
core::str::from_utf8_mut(self.as_mut_used())
}
pub fn as_f64(&self) -> Result<f64, core::num::ParseFloatError> {
let s = self.as_str().unwrap_or_default();
core::str::FromStr::from_str(s)
}
pub fn reverse(&mut self) {
let mut i = 0;
let mut j = self.len - 1;
while i < j {
self.buf.swap(i, j);
i += 1;
j -= 1;
}
}
pub fn from_slice(buf: &[u8]) -> Self {
let mut out = Self::new();
buf.iter().take(N).for_each(|b| {
let _ = out.push_back(*b);
});
out
}
}
impl<const N: usize> WriteToBEBits for FixedU8Buf<N> {
fn write_be_to<T: MutBits + ?Sized>(&self, bits: &mut T) -> Result<usize, Error> {
bits.write_be_u32_blob(self.as_ref_used())?;
Ok(self.len + 4)
}
}
impl<const N: usize> ReadFromBEBits for FixedU8Buf<N> {
fn read_from_be_bits<T: Bits>(inp: &mut T) -> Result<Self, Error> {
let mut out = Self::new();
inp.read_u32_blob_into(&mut out)?;
Ok(out)
}
}
impl<const N: usize> AsRef<[u8]> for FixedU8Buf<N> {
#[allow(clippy::indexing_slicing)]
fn as_ref(&self) -> &[u8] {
&self.buf[..self.len]
}
}
impl<const N: usize> AsMut<[u8]> for FixedU8Buf<N> {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.buf
}
}
impl<const N: usize> Default for FixedU8Buf<N> {
fn default() -> Self {
Self::new()
}
}
impl<const N: usize> core::fmt::Write for FixedU8Buf<N> {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
self.append(s.as_bytes()).map_err(|_| core::fmt::Error)?;
Ok(())
}
}
impl<const N: usize> Buffer<u8> for FixedU8Buf<N> {
fn get(&self, index: usize) -> Option<&u8> {
if index >= N || index >= self.len {
return None;
}
self.buf.get(index)
}
fn get_mut(&mut self, index: usize) -> Option<&mut u8> {
if index >= N || index >= self.len {
return None;
}
self.buf.get_mut(index)
}
fn capacity(&self) -> usize {
N
}
fn len(&self) -> usize {
self.len
}
fn clear(&mut self) {
self.len = 0
}
fn front(&self) -> Option<&u8> {
self.get(0)
}
fn front_mut(&mut self) -> Option<&mut u8> {
self.get_mut(0)
}
fn back(&self) -> Option<&u8> {
if N == 0 || self.len == 0 {
return None;
}
self.get(self.len - 1)
}
fn back_mut(&mut self) -> Option<&mut u8> {
if N == 0 || self.len == 0 {
return None;
}
self.get_mut(self.len - 1)
}
fn pop_front(&mut self) -> Option<u8> {
if N == 0 || self.len == 0 {
return None;
}
let out = self.buf[0];
for idx in 1..self.len {
self.buf[idx - 1] = self.buf[idx];
}
self.len -= 1;
Some(out)
}
fn pop_back(&mut self) -> Option<u8> {
if N == 0 || self.len == 0 {
return None;
}
let idx = self.len - 1;
self.len -= 1;
let val = self.buf[idx];
self.buf[idx] = 0;
Some(val)
}
fn push_front(&mut self, value: u8) -> Result<(), u8> {
if N == 0 || self.len == N {
return Err(value);
}
for idx in 0..self.len {
self.buf[idx + 1] = self.buf[idx];
}
self.buf[0] = value;
self.len += 1;
Ok(())
}
fn push_back(&mut self, value: u8) -> Result<(), u8> {
if N == 0 || self.len == N {
return Err(value);
}
self.buf[self.len] = value;
self.len += 1;
Ok(())
}
}
#[allow(clippy::panic)]
impl<const N: usize> Index<usize> for FixedU8Buf<N> {
type Output = u8;
fn index(&self, index: usize) -> &Self::Output {
assert!(index < self.len, "index {index} >= len {}", self.len);
let Some(val) = self.buf.get(index) else {
panic!("expected value at offset {index} but was empty!");
};
val
}
}
#[allow(clippy::panic)]
impl<const N: usize> IndexMut<usize> for FixedU8Buf<N> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
assert!(index < N, "index {index} >= capacity {N}");
if index >= self.len {
self.len = index + 1;
}
&mut self.buf[index]
}
}
pub struct FixedU8BufIter<'a, const N: usize> {
buf: &'a FixedU8Buf<N>,
idx: usize,
}
impl<'a, const N: usize> Iterator for FixedU8BufIter<'a, N> {
type Item = &'a u8;
fn next(&mut self) -> Option<Self::Item> {
if let Some(val) = self.buf.get(self.idx) {
self.idx += 1;
return Some(val);
}
None
}
}
impl<const N: usize> DoubleEndedIterator for FixedU8BufIter<'_, N> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.idx >= self.buf.len {
return None;
}
let idx = self.buf.len().saturating_sub(self.idx).saturating_sub(1);
self.idx += 1;
if let Some(val) = self.buf.get(idx) {
return Some(val);
}
None
}
}
impl<const N: usize> ExactSizeIterator for FixedU8BufIter<'_, N> {
fn len(&self) -> usize {
self.buf.len()
}
}
pub struct FixedU8BufIterMut<'a, const N: usize> {
buf: &'a mut FixedU8Buf<N>,
idx: usize,
}
impl<const N: usize> LendingIterator for FixedU8BufIterMut<'_, N> {
type Item<'b>
= &'b mut u8
where
Self: 'b;
fn next_ref(&mut self) -> Option<Self::Item<'_>> {
if let Some(val) = self.buf.get_mut(self.idx) {
self.idx += 1;
return Some(val);
}
None
}
}
impl<const N: usize> MutBits for &mut FixedU8Buf<N> {
fn write_u8(&mut self, val: u8) -> Result<(), Error> {
if self.push_back(val).is_err() {
return Err(BitsErrorKind::UnexpectedEof.into());
}
Ok(())
}
fn write_all_bytes(&mut self, val: &[u8]) -> Result<(), Error> {
self.append(val)
}
}
impl<const N: usize> MutBits for FixedU8Buf<N> {
fn write_u8(&mut self, val: u8) -> Result<(), Error> {
if self.push_back(val).is_err() {
return Err(BitsErrorKind::UnexpectedEof.into());
}
Ok(())
}
fn write_all_bytes(&mut self, val: &[u8]) -> Result<(), Error> {
self.append(val)
}
}
impl<const N: usize> Bits for FixedU8Buf<N> {
fn next_u8(&mut self) -> Result<Option<u8>, Error> {
Ok(self.pop_front())
}
}