use crate::params::MAX_INPUT_PER_SYMBOL;
pub trait RangeOutput {
fn write_byte(&mut self, byte: u8);
#[inline]
fn write_run(&mut self, first: u8, fill: u8, count: u64) {
self.write_byte(first);
for _ in 0..count {
self.write_byte(fill);
}
}
}
impl RangeOutput for alloc_crate::vec::Vec<u8> {
#[inline(always)]
fn write_byte(&mut self, byte: u8) {
self.push(byte);
}
fn write_run(&mut self, first: u8, fill: u8, count: u64) {
self.push(first);
let count = usize::try_from(count).unwrap_or(usize::MAX);
self.resize(self.len().saturating_add(count), fill);
}
}
impl<O: RangeOutput + ?Sized> RangeOutput for &mut O {
#[inline(always)]
fn write_byte(&mut self, byte: u8) {
(**self).write_byte(byte);
}
#[inline]
fn write_run(&mut self, first: u8, fill: u8, count: u64) {
(**self).write_run(first, fill, count);
}
}
#[derive(Clone, Debug)]
pub struct Pending {
head: Option<u8>,
fill: u8,
run: u64,
tail: [u8; TAIL],
tail_start: usize,
tail_len: usize,
overflowed: bool,
}
const TAIL: usize = 2 * MAX_INPUT_PER_SYMBOL;
impl Default for Pending {
fn default() -> Self {
Self::new()
}
}
impl Pending {
pub const fn new() -> Self {
Self {
head: None,
fill: 0,
run: 0,
tail: [0; TAIL],
tail_start: 0,
tail_len: 0,
overflowed: false,
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.head.is_none() && self.run == 0 && self.tail_start == self.tail_len
}
pub fn len(&self) -> u64 {
u64::from(self.head.is_some()) + self.run + (self.tail_len - self.tail_start) as u64
}
pub fn overflowed(&self) -> bool {
self.overflowed
}
pub fn clear(&mut self) {
self.head = None;
self.run = 0;
self.tail_start = 0;
self.tail_len = 0;
self.overflowed = false;
}
#[inline]
fn push_tail(&mut self, byte: u8) {
if let Some(slot) = self.tail.get_mut(self.tail_len) {
*slot = byte;
self.tail_len += 1;
} else {
self.overflowed = true;
}
}
pub fn drain_into(&mut self, out: &mut [u8]) -> usize {
let mut n = 0;
if let Some(head) = self.head {
let Some(slot) = out.first_mut() else {
return 0;
};
*slot = head;
self.head = None;
n = 1;
}
if self.run != 0 {
let room = out.len() - n;
let take = usize::try_from(self.run).map_or(room, |run| run.min(room));
out[n..n + take].fill(self.fill);
self.run -= take as u64;
n += take;
if self.run != 0 {
return n;
}
}
let queued = &self.tail[self.tail_start..self.tail_len];
let take = queued.len().min(out.len() - n);
out[n..n + take].copy_from_slice(&queued[..take]);
self.tail_start += take;
if self.tail_start == self.tail_len {
self.tail_start = 0;
self.tail_len = 0;
}
n + take
}
}
pub struct Drain<'o, 'q> {
out: &'o mut [u8],
pos: usize,
end: usize,
queue: &'q mut Pending,
}
impl<'o, 'q> Drain<'o, 'q> {
pub fn new(out: &'o mut [u8], queue: &'q mut Pending) -> Self {
debug_assert!(queue.is_empty());
let end = out.len();
Self {
out,
pos: 0,
end,
queue,
}
}
#[inline]
pub fn written(&self) -> usize {
self.pos
}
#[inline]
pub fn blocked(&self) -> bool {
self.pos >= self.end
}
#[cold]
#[inline(never)]
fn queue_byte(&mut self, byte: u8) {
self.end = self.pos;
self.queue.push_tail(byte);
}
}
impl RangeOutput for Drain<'_, '_> {
#[inline(always)]
fn write_byte(&mut self, byte: u8) {
if self.pos < self.end {
if let Some(slot) = self.out.get_mut(self.pos) {
*slot = byte;
self.pos += 1;
return;
}
}
self.queue_byte(byte);
}
fn write_run(&mut self, first: u8, fill: u8, count: u64) {
if self.pos >= self.end {
if self.queue.is_empty() {
self.end = self.pos;
self.queue.head = Some(first);
self.queue.fill = fill;
self.queue.run = count;
return;
}
self.queue.push_tail(first);
for _ in 0..count.min(TAIL as u64 + 1) {
self.queue.push_tail(fill);
}
return;
}
self.write_byte(first);
let room = (self.end - self.pos) as u64;
let direct = count.min(room) as usize;
self.out[self.pos..self.pos + direct].fill(fill);
self.pos += direct;
let rest = count - direct as u64;
if rest != 0 {
self.end = self.pos;
self.queue.fill = fill;
self.queue.run = rest;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn drain_all(queue: &mut Pending) -> alloc_crate::vec::Vec<u8> {
let mut all = alloc_crate::vec::Vec::new();
let mut buf = [0u8; 3];
while !queue.is_empty() {
let n = queue.drain_into(&mut buf);
all.extend_from_slice(&buf[..n]);
}
all
}
#[test]
fn a_drain_keeps_order_across_the_slice_edge() {
for room in 0..12 {
let mut queue = Pending::new();
let mut out = alloc_crate::vec![0u8; room];
let mut sink = Drain::new(&mut out, &mut queue);
sink.write_byte(1);
sink.write_run(2, 0xFF, 5);
sink.write_byte(3);
sink.write_run(4, 0, 1);
let n = sink.written();
let mut got = out[..n].to_vec();
got.extend(drain_all(&mut queue));
assert_eq!(
got,
[1, 2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 3, 4, 0],
"room {room}"
);
}
}
#[test]
fn a_long_run_is_a_count_not_a_buffer() {
let mut queue = Pending::new();
let mut out = [0u8; 2];
let mut sink = Drain::new(&mut out, &mut queue);
sink.write_run(9, 0xFF, 1 << 40);
assert_eq!(sink.written(), 2);
assert_eq!(queue.len(), (1 << 40) - 1);
assert!(!queue.overflowed());
let mut vec = alloc_crate::vec::Vec::new();
vec.write_run(1, 2, 3);
assert_eq!(vec, [1, 2, 2, 2]);
}
}