orphanage 0.5.6

Random collection of stuff that is still searching for a home.
Documentation
use std::{cmp, collections::VecDeque, fmt};

use tokio::io::ReadBuf;

use bytes::{Buf, Bytes};

use rand::{RngExt, distr::uniform::SampleRange};

// Note: We're shutting up clippy here because it says that we should be using
// MaybeUninit, which it is correct about.  However, the rand crate maintainers
// think that filling MaybeUninit is bad and that application should be forced
// to double-initialize buffers, which is obviously wrong.  But it's their
// crate, so we're doing it this way instead.
#[allow(clippy::uninit_vec)]
#[must_use]
pub fn random(len: usize) -> Vec<u8> {
  let mut buf = Vec::with_capacity(len);

  // SAFETY: Presumably with_capacity() works as documented.
  unsafe {
    buf.set_len(len);
  }

  rand::rng().fill(&mut buf[..]);

  buf
}


pub fn random_size<R>(range: R) -> Vec<u8>
where
  R: SampleRange<usize>
{
  let sz = rand::rng().random_range::<usize, R>(range);
  random(sz)
}


/// Hex dump byte-by-byte, but rle code repeated
pub struct ByteReps<'a>(pub &'a [u8]);

struct RepeatedByte {
  byte: u8,
  count: usize
}

impl fmt::Debug for RepeatedByte {
  fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result {
    match self.count {
      1 => write!(fmtr, "0x{:02x}", self.byte),
      _ => write!(fmtr, "0x{:02x}*{}", self.byte, self.count)
    }
  }
}

impl fmt::Debug for ByteReps<'_> {
  fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result {
    let mut list = fmtr.debug_list();
    let &[mut byte, ref rest @ ..] = self.0 else {
      return list.finish();
    };
    let mut count: usize = 1;
    for &this in rest {
      if byte == this {
        count += 1;
        continue;
      }

      list.entry(&RepeatedByte { byte, count });
      count = 1;
      byte = this;
    }

    list.entry(&RepeatedByte { byte, count });
    list.finish()
  }
}

impl fmt::Display for ByteReps<'_> {
  fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result {
    <Self as fmt::Debug>::fmt(self, fmtr)
  }
}


/// Copy buffers from a `VecDeque<Bytes>` into a `&mut [u8]`.
///
/// Returns the total number of bytes that have been copied
pub fn read_from_bufq(q: &mut VecDeque<Bytes>, dst: &mut [u8]) -> usize {
  let mut offs = 0;

  loop {
    // Calculate how much application buffer space remains
    let rem = dst.len() - offs;
    if rem == 0 {
      // Filled application buffer -- break out of loop
      break;
    }

    // Take next buffer off queue
    let Some(mut n) = q.pop_front() else {
      // Queue is empty
      break;
    };

    // Calculate number of bytes to copy
    let ncopy = cmp::min(rem, n.len());
    if ncopy < n.len() {
      // Only part of the buffer taken off the queue fits in the (remaining)
      // application buffer.
      //
      // Copy what fits, advance the buffer in the node and put it back onto
      // the queue.

      dst[offs..offs + ncopy].copy_from_slice(&n[..ncopy]);

      // Advance queue buffer before returning it
      n.advance(ncopy);
      offs += ncopy;

      // Put the remaining buffer back onto the queue
      q.push_front(n);

      break;
    }

    // The current buffer taken off the queue fits entirely within the
    // application buffer.
    dst[offs..offs + n.len()].copy_from_slice(&n);
    offs += n.len();
  }

  offs
}


/// Copy buffers from a `VecDeque<Bytes>` into a `ReadBuf`.
pub fn readbuf_from_bufq(q: &mut VecDeque<Bytes>, dst: &mut ReadBuf<'_>) {
  while dst.remaining() > 0
    && let Some(mut n) = q.pop_front()
  {
    // Determine number of bytes to copy
    let ncopy = cmp::min(dst.remaining(), n.len());
    if ncopy < n.len() {
      // Only part of the buffer taken off the queue fits in the (remaining)
      // application buffer.
      //
      // Copy what fits, advance the buffer in the node and put it back onto
      // the queue.

      dst.put_slice(&n[..ncopy]);

      // Advance queue buffer before returning it
      n.advance(ncopy);

      // Put the remaining buffer back onto the queue
      q.push_front(n);

      break;
    }

    // The current buffer taken off the queue fits entirely within the
    // application buffer.
    dst.put_slice(&n);
  }
}


#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn hex_rle() {
    let buf = [0];
    assert_eq!(ByteReps(&buf).to_string(), "[0x00]");

    let buf = [0, 0];
    assert_eq!(ByteReps(&buf).to_string(), "[0x00*2]");

    let buf = [0, 1];
    assert_eq!(ByteReps(&buf).to_string(), "[0x00, 0x01]");

    let buf = [0, 0, 1, 1];
    assert_eq!(ByteReps(&buf).to_string(), "[0x00*2, 0x01*2]");

    let buf = [0, 0, 1, 1, 0, 0];
    assert_eq!(ByteReps(&buf).to_string(), "[0x00*2, 0x01*2, 0x00*2]");
  }
}


#[cfg(test)]
mod read_from_bufq_tests {
  use super::{Bytes, VecDeque, read_from_bufq};

  #[test]
  fn empty() {
    let mut q = VecDeque::new();

    let mut buf = [0u8; 4];

    let copied = read_from_bufq(&mut q, &mut buf[..]);
    assert_eq!(copied, 0);
  }

  #[test]
  fn exact_fit() {
    let mut q = VecDeque::new();

    q.push_back(Bytes::from("foo"));
    q.push_back(Bytes::from("bar"));

    let mut buf = vec![0u8; 6];

    let copied = read_from_bufq(&mut q, &mut buf[..]);
    assert_eq!(copied, 6);
    assert_eq!(buf, b"foobar");
  }

  #[test]
  fn partial() {
    let mut q = VecDeque::new();

    q.push_back(Bytes::from("foo"));
    q.push_back(Bytes::from("bar"));

    let mut buf = vec![0u8; 4];

    let copied = read_from_bufq(&mut q, &mut buf[..]);
    assert_eq!(copied, 4);
    assert_eq!(buf, b"foob");


    let copied = read_from_bufq(&mut q, &mut buf[..]);
    assert_eq!(copied, 2);
    assert_eq!(&buf[..copied], b"ar");
  }

  #[test]
  fn q_order() {
    let mut q = VecDeque::new();

    q.push_back(Bytes::from("foo"));
    q.push_back(Bytes::from("bar"));
    q.push_back(Bytes::from("baz"));

    let mut buf = vec![0u8; 2];

    let copied = read_from_bufq(&mut q, &mut buf[..]);
    assert_eq!(copied, 2);
    assert_eq!(buf, b"fo");

    let copied = read_from_bufq(&mut q, &mut buf[..]);
    assert_eq!(copied, 2);
    assert_eq!(buf, b"ob");

    let copied = read_from_bufq(&mut q, &mut buf[..]);
    assert_eq!(copied, 2);
    assert_eq!(buf, b"ar");

    let copied = read_from_bufq(&mut q, &mut buf[..]);
    assert_eq!(copied, 2);
    assert_eq!(buf, b"ba");

    let copied = read_from_bufq(&mut q, &mut buf[..]);
    assert_eq!(copied, 1);
    assert_eq!(&buf[..copied], b"z");
  }
}


#[cfg(test)]
mod readbuf_from_bufq_tests {
  use super::{Bytes, ReadBuf, VecDeque, readbuf_from_bufq};
  use std::mem::MaybeUninit;

  #[test]
  fn empty() {
    let mut q = VecDeque::new();

    let mut ubuf: [MaybeUninit<u8>; 4] = [MaybeUninit::uninit(); 4];
    let mut buf = ReadBuf::uninit(&mut ubuf);

    readbuf_from_bufq(&mut q, &mut buf);

    assert_eq!(buf.initialized(), &[]);
    assert_eq!(buf.filled(), &[]);
  }

  #[test]
  fn exact_fit() {
    let mut q = VecDeque::new();

    q.push_back(Bytes::from("foo"));
    q.push_back(Bytes::from("bar"));

    let mut ubuf: [MaybeUninit<u8>; 6] = [MaybeUninit::uninit(); 6];
    let mut buf = ReadBuf::uninit(&mut ubuf);

    readbuf_from_bufq(&mut q, &mut buf);
    assert_eq!(buf.initialized(), b"foobar");
    assert_eq!(buf.filled(), b"foobar");
  }

  #[test]
  fn partial() {
    let mut q = VecDeque::new();

    q.push_back(Bytes::from("foo"));
    q.push_back(Bytes::from("bar"));

    let mut ubuf: [MaybeUninit<u8>; 4] = [MaybeUninit::uninit(); 4];
    let mut buf = ReadBuf::uninit(&mut ubuf);

    readbuf_from_bufq(&mut q, &mut buf);
    assert_eq!(buf.filled(), b"foob");

    buf.clear();

    readbuf_from_bufq(&mut q, &mut buf);
    assert_eq!(buf.filled(), b"ar");
  }

  #[test]
  fn q_order() {
    let mut q = VecDeque::new();

    q.push_back(Bytes::from("foo"));
    q.push_back(Bytes::from("bar"));
    q.push_back(Bytes::from("baz"));

    let mut ubuf: [MaybeUninit<u8>; 2] = [MaybeUninit::uninit(); 2];
    let mut buf = ReadBuf::uninit(&mut ubuf);

    readbuf_from_bufq(&mut q, &mut buf);
    assert_eq!(buf.filled(), b"fo");

    buf.clear();

    readbuf_from_bufq(&mut q, &mut buf);
    assert_eq!(buf.filled(), b"ob");

    buf.clear();

    readbuf_from_bufq(&mut q, &mut buf);
    assert_eq!(buf.filled(), b"ar");

    buf.clear();

    readbuf_from_bufq(&mut q, &mut buf);
    assert_eq!(buf.filled(), b"ba");

    buf.clear();

    readbuf_from_bufq(&mut q, &mut buf);
    assert_eq!(buf.filled(), b"z");
  }
}

// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :