#![cfg_attr(feature = "nightly", feature(test))]
#[cfg(feature = "nightly")]
extern crate test;
use std::io;
use std::error;
use std::fmt;
use std::boxed::Box;
use std::slice::from_raw_parts;
use std::slice::from_raw_parts_mut;
use std::ptr::copy_nonoverlapping;
pub const DEFAULT_CAPACITY: usize = 4096;
pub const DEFAULT_SIZE_MULTIPLIER: usize = 2;
#[derive(Debug)]
pub enum CircBufError {
BufEmpty,
BufFull,
Overflow,
}
impl fmt::Display for CircBufError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
CircBufError::BufEmpty => write!(f, "CircBuf is full"),
CircBufError::BufFull => write!(f, "CircBuf is empty"),
CircBufError::Overflow => write!(f, "Value would overflow uszie"),
}
}
}
impl error::Error for CircBufError {
fn description(&self) -> &str {
match *self {
CircBufError::BufEmpty => "CircBuf is full",
CircBufError::BufFull => "CircBuf is empty",
CircBufError::Overflow => "Value would overflow usize",
}
}
fn cause(&self) -> Option<&error::Error> {
None
}
}
#[derive(Debug)]
pub struct CircBuf {
buf: Box<[u8]>,
write_cursor: usize,
read_cursor: usize,
}
impl CircBuf {
pub fn new() -> Self {
CircBuf {
buf: Box::new([0; DEFAULT_CAPACITY]),
write_cursor: 0,
read_cursor: 0,
}
}
pub fn with_capacity(cap: usize) -> Result<Self, CircBufError> {
let capacity = match cap.checked_next_power_of_two() {
Some(capacity) => capacity,
None => return Err(CircBufError::Overflow),
};
Ok(CircBuf {
buf: vec![0; capacity].into_boxed_slice(),
write_cursor: 0,
read_cursor: 0,
})
}
pub fn cap(&self) -> usize {
self.buf.len() - 1
}
pub fn len(&self) -> usize {
if self.write_cursor < self.read_cursor {
return self.buf.len() - self.read_cursor + self.write_cursor;
}
self.write_cursor - self.read_cursor
}
pub fn avail(&self) -> usize {
self.cap() - self.len()
}
pub fn is_empty(&self) -> bool {
return self.len() == 0;
}
pub fn is_full(&self) -> bool {
return self.avail() == 0;
}
pub fn find_from_index(&self, val: u8, index: usize) -> Option<usize> {
if index >= self.len() {
return None;
}
if self.write_cursor < self.read_cursor {
if self.read_cursor + index < self.buf.len() {
for (i, b) in self.buf[self.read_cursor + index..].iter().enumerate() {
if *b == val {
return Some(i + index);
}
}
}
for (i, b) in self.buf[..self.write_cursor].iter().enumerate() {
if *b == val {
return Some(i + self.buf.len() - self.read_cursor);
}
}
None
} else {
for (i, b) in self.buf[self.read_cursor + index..self.write_cursor].iter().enumerate() {
if *b == val {
return Some(i + index);
}
}
None
}
}
pub fn find(&self, val: u8) -> Option<usize> {
self.find_from_index(val, 0)
}
pub fn peek(&self) -> Result<u8, CircBufError> {
if self.len() == 0 {
return Err(CircBufError::BufEmpty);
}
Ok(self.buf[self.read_cursor])
}
pub fn get(&mut self) -> Result<u8, CircBufError> {
if self.len() == 0 {
return Err(CircBufError::BufEmpty);
}
let val = self.buf[self.read_cursor];
self.advance_read(1);
Ok(val)
}
pub fn put(&mut self, val: u8) -> Result<(), CircBufError> {
if self.avail() == 0 {
return Err(CircBufError::BufFull);
}
self.buf[self.write_cursor] = val;
self.advance_write(1);
Ok(())
}
pub fn advance_read(&mut self, num: usize) {
self.read_cursor = (self.read_cursor + num) % self.buf.len();
}
pub fn advance_write(&mut self, num: usize) {
self.write_cursor = (self.write_cursor + num) % self.buf.len();
}
pub fn clear(&mut self) {
self.write_cursor = 0;
self.read_cursor = 0;
}
pub fn grow_with_factor(&mut self, factor: usize) -> Result<(), CircBufError> {
let cap = match self.buf.len().checked_mul(factor) {
Some(cap) => cap,
None => return Err(CircBufError::Overflow),
};
let cap_checked = match cap.checked_next_power_of_two() {
Some(cap) => cap,
None => return Err(CircBufError::Overflow),
};
let mut new_buf = vec![0; cap_checked].into_boxed_slice();
let mut bytes_written = 0;
if self.write_cursor < self.read_cursor {
let num_to_end = self.buf.len() - self.read_cursor;
unsafe { copy_nonoverlapping(&self.buf[self.read_cursor], &mut new_buf[0], num_to_end) }
bytes_written += num_to_end;
unsafe {
copy_nonoverlapping(&self.buf[0], &mut new_buf[bytes_written], self.write_cursor)
}
bytes_written += self.write_cursor;
} else {
let num_to_copy = self.write_cursor - self.read_cursor;
unsafe {
copy_nonoverlapping(&self.buf[self.read_cursor], &mut new_buf[0], num_to_copy)
}
bytes_written += num_to_copy;
}
self.buf = new_buf;
self.write_cursor = bytes_written;
self.read_cursor = 0;
Ok(())
}
pub fn grow(&mut self) -> Result<(), CircBufError> {
self.grow_with_factor(DEFAULT_SIZE_MULTIPLIER)
}
pub fn get_avail_upto_size(&mut self, size: usize) -> [&mut [u8]; 2] {
let first_buf;
let second_buf;
let min = if self.avail() < size {
self.avail()
} else {
size
};
if self.write_cursor >= self.read_cursor && min > self.buf.len() - self.write_cursor {
unsafe {
first_buf = from_raw_parts_mut(&mut self.buf[self.write_cursor],
self.buf.len() - self.write_cursor);
second_buf = from_raw_parts_mut(&mut self.buf[0],
min - (self.buf.len() - self.write_cursor));
}
} else {
unsafe {
first_buf = from_raw_parts_mut(&mut self.buf[self.write_cursor], min);
second_buf = from_raw_parts_mut(&mut self.buf[self.write_cursor], 0);
}
}
[first_buf, second_buf]
}
pub fn get_bytes_upto_size(&mut self, size: usize) -> [&[u8]; 2] {
let first_buf;
let second_buf;
let min = if self.len() < size { self.len() } else { size };
if self.write_cursor < self.read_cursor && min > self.buf.len() - self.read_cursor {
unsafe {
first_buf = from_raw_parts(&self.buf[self.read_cursor],
self.buf.len() - self.read_cursor);
second_buf = from_raw_parts(&self.buf[0],
min - (self.buf.len() - self.read_cursor));
}
} else {
unsafe {
first_buf = from_raw_parts(&self.buf[self.read_cursor], min);
second_buf = from_raw_parts(&self.buf[self.read_cursor], 0);
}
}
[first_buf, second_buf]
}
pub fn get_avail(&mut self) -> [&mut [u8]; 2] {
let avail = self.avail();
self.get_avail_upto_size(avail)
}
pub fn get_bytes(&mut self) -> [&[u8]; 2] {
let len = self.len();
self.get_bytes_upto_size(len)
}
}
impl io::Read for CircBuf {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let len = self.len();
if len == 0 {
return Ok(0);
}
let num_to_read = if len < buf.len() { len } else { buf.len() };
if self.write_cursor < self.read_cursor {
let num_to_end = self.buf.len() - self.read_cursor;
let min = if num_to_read < num_to_end {
num_to_read
} else {
num_to_end
};
unsafe { copy_nonoverlapping(&self.buf[self.read_cursor], &mut buf[0], min) };
if min != num_to_read {
unsafe { copy_nonoverlapping(&self.buf[0], &mut buf[min], num_to_read - min) };
}
} else {
unsafe { copy_nonoverlapping(&self.buf[self.read_cursor], &mut buf[0], num_to_read) };
}
self.advance_read(num_to_read);
Ok(num_to_read)
}
}
impl io::Write for CircBuf {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let avail = self.avail();
if avail == 0 {
return Ok(0);
}
let num_to_write = if avail < buf.len() { avail } else { buf.len() };
if self.write_cursor < self.read_cursor {
unsafe { copy_nonoverlapping(&buf[0], &mut self.buf[self.write_cursor], num_to_write) };
} else {
let num_to_end = self.buf.len() - self.write_cursor;
let min = if num_to_write < num_to_end {
num_to_write
} else {
num_to_end
};
unsafe { copy_nonoverlapping(&buf[0], &mut self.buf[self.write_cursor], min) };
if min != num_to_write {
unsafe { copy_nonoverlapping(&buf[min], &mut self.buf[0], num_to_write - min) };
}
}
self.advance_write(num_to_write);
Ok(num_to_write)
}
fn flush(&mut self) -> io::Result<()> {
self.clear();
Ok(())
}
}
#[cfg(test)]
mod tests {
extern crate vecio;
use std::env;
use std::error::Error;
use std::io::{Read, Write, Seek, SeekFrom};
use std::fs::OpenOptions;
use self::vecio::Rawv;
use super::{CircBuf, DEFAULT_CAPACITY, CircBufError};
#[cfg(feature = "nightly")]
use test::Bencher;
#[test]
fn create_circbuf() {
let c = CircBuf::new();
assert_eq!(c.cap(), DEFAULT_CAPACITY - 1);
assert_eq!(c.avail(), DEFAULT_CAPACITY - 1);
assert_eq!(c.len(), 0);
assert!(c.is_empty());
}
#[test]
fn create_circbuf_with_capacity() {
let c = CircBuf::with_capacity(49).unwrap();
assert_eq!(c.cap(), 64 - 1);
assert_eq!(c.avail(), 64 - 1);
assert_eq!(c.len(), 0);
assert!(c.is_empty());
}
#[test]
fn put_peek_and_get_bytes() {
let mut c = CircBuf::with_capacity(4).unwrap();
c.put(1).unwrap();
c.put(2).unwrap();
c.put(3).unwrap();
assert_eq!(c.put(4).unwrap_err().description(),
CircBufError::BufFull.description());
assert_eq!(c.avail(), 0);
assert_eq!(c.len(), 3);
assert!(c.is_full());
assert_eq!(c.peek().unwrap(), 1);
assert_eq!(c.get().unwrap(), 1);
assert_eq!(c.peek().unwrap(), 2);
assert_eq!(c.get().unwrap(), 2);
assert_eq!(c.peek().unwrap(), 3);
assert_eq!(c.get().unwrap(), 3);
assert_eq!(c.get().unwrap_err().description(),
CircBufError::BufEmpty.description());
assert_eq!(c.avail(), 3);
assert_eq!(c.len(), 0);
assert!(c.is_empty());
c.put(4).unwrap();
c.put(5).unwrap();
c.put(6).unwrap();
assert_eq!(c.peek().unwrap(), 4);
assert_eq!(c.get().unwrap(), 4);
assert_eq!(c.peek().unwrap(), 5);
assert_eq!(c.get().unwrap(), 5);
assert_eq!(c.peek().unwrap(), 6);
assert_eq!(c.get().unwrap(), 6);
}
#[test]
fn find_bytes() {
let mut c = CircBuf::with_capacity(8).unwrap();
c.put(7).unwrap();
c.put(6).unwrap();
c.put(5).unwrap();
c.put(4).unwrap();
c.put(3).unwrap();
c.put(2).unwrap();
c.put(1).unwrap();
assert_eq!(c.find(4).unwrap(), 3);
assert_eq!(c.find_from_index(3, 2).unwrap(), 4);
assert!(c.find_from_index(6, 2).is_none());
c.advance_read(4);
assert_eq!(c.find(1).unwrap(), 2);
assert!(c.find(5).is_none());
c.put(10).unwrap();
c.put(11).unwrap();
c.put(12).unwrap();
assert_eq!(c.find(12).unwrap(), 5);
assert_eq!(c.find_from_index(12, 4).unwrap(), 5);
}
#[test]
fn grow_buffer() {
let mut c = CircBuf::with_capacity(4).unwrap();
c.put(1).unwrap();
c.put(2).unwrap();
c.put(3).unwrap();
c.grow().unwrap();
assert!(c.cap() == 8 - 1);
assert!(c.avail() == 4);
assert!(c.len() == 3);
assert_eq!(c.get().unwrap(), 1);
assert_eq!(c.get().unwrap(), 2);
assert_eq!(c.get().unwrap(), 3);
c.advance_read(7);
c.advance_write(7);
c.put(1).unwrap();
c.put(2).unwrap();
c.put(3).unwrap();
c.grow().unwrap();
assert_eq!(c.get().unwrap(), 1);
assert_eq!(c.get().unwrap(), 2);
assert_eq!(c.get().unwrap(), 3);
}
#[test]
fn read_and_write_bytes() {
let mut c = CircBuf::with_capacity(8).unwrap();
assert_eq!(c.write(b"foo").unwrap(), 3);
assert_eq!(c.write(b"bar").unwrap(), 3);
assert_eq!(c.cap(), 7);
assert_eq!(c.avail(), 1);
assert_eq!(c.len(), 6);
let mut buf = [0; 6];
assert_eq!(c.read(&mut buf).unwrap(), 6);
assert!(b"foobar".iter().zip(buf.iter()).all(|(a, b)| a == b));
assert_eq!(c.cap(), 7);
assert_eq!(c.avail(), 7);
assert_eq!(c.len(), 0);
c.advance_read(7);
c.advance_write(7);
assert_eq!(c.write(b"foo").unwrap(), 3);
assert_eq!(c.write(b"bar").unwrap(), 3);
let mut buf = [0; 6];
assert_eq!(c.read(&mut buf).unwrap(), 6);
assert!(b"foobar".iter().zip(buf.iter()).all(|(a, b)| a == b));
}
#[test]
fn get_bytes_and_avail() {
let mut c = CircBuf::with_capacity(16).unwrap();
assert_eq!(c.write(b"funkytowns").unwrap(), 10);
assert_eq!(c.len(), 10);
assert_eq!(c.avail(), 5);
{
let bufs = c.get_avail_upto_size(4);
assert_eq!(bufs.len(), 2);
assert_eq!(bufs[0].len(), 4);
assert_eq!(bufs[1].len(), 0);
}
{
let bufs = c.get_bytes_upto_size(5);
assert_eq!(bufs.len(), 2);
assert_eq!(bufs[0].len(), 5);
assert_eq!(bufs[1].len(), 0);
assert!(b"funky".iter().zip(bufs[0].iter()).all(|(a, b)| a == b));
}
{
let bufs = c.get_avail_upto_size(10);
assert_eq!(bufs.len(), 2);
assert_eq!(bufs[0].len(), 5);
assert_eq!(bufs[1].len(), 0);
}
{
let bufs = c.get_bytes_upto_size(12);
assert_eq!(bufs.len(), 2);
assert_eq!(bufs[0].len(), 10);
assert_eq!(bufs[1].len(), 0);
assert!(b"funky".iter().zip(bufs[0].iter()).all(|(a, b)| a == b));
}
{
let bufs = c.get_avail();
assert_eq!(bufs.len(), 2);
assert_eq!(bufs[0].len(), 5);
assert_eq!(bufs[1].len(), 0);
}
{
let bufs = c.get_bytes();
assert_eq!(bufs.len(), 2);
assert_eq!(bufs[0].len(), 10);
assert_eq!(bufs[1].len(), 0);
assert!(b"funkytowns".iter().zip(bufs[0].iter()).all(|(a, b)| a == b));
}
c.advance_read(10);
assert_eq!(c.write(b"brickhouse").unwrap(), 10);
assert_eq!(c.len(), 10);
assert_eq!(c.avail(), 5);
{
let bufs = c.get_avail_upto_size(4);
assert_eq!(bufs.len(), 2);
assert_eq!(bufs[0].len(), 4);
assert_eq!(bufs[1].len(), 0);
}
{
let bufs = c.get_bytes_upto_size(8);
assert_eq!(bufs.len(), 2);
assert_eq!(bufs[0].len(), 6);
assert_eq!(bufs[1].len(), 2);
assert!(b"brickh".iter().zip(bufs[0].iter()).all(|(a, b)| a == b));
assert!(b"ou".iter().zip(bufs[1].iter()).all(|(a, b)| a == b));
}
{
let bufs = c.get_avail_upto_size(17);
assert_eq!(bufs.len(), 2);
assert_eq!(bufs[0].len(), 5);
assert_eq!(bufs[1].len(), 0);
}
{
let bufs = c.get_bytes_upto_size(12);
assert_eq!(bufs.len(), 2);
assert_eq!(bufs[0].len(), 6);
assert_eq!(bufs[1].len(), 4);
assert!(b"brickh".iter().zip(bufs[0].iter()).all(|(a, b)| a == b));
assert!(b"ouse".iter().zip(bufs[1].iter()).all(|(a, b)| a == b));
}
{
let bufs = c.get_avail();
assert_eq!(bufs.len(), 2);
assert_eq!(bufs[0].len(), 5);
assert_eq!(bufs[1].len(), 0);
}
{
let bufs = c.get_bytes();
assert_eq!(bufs.len(), 2);
assert_eq!(bufs[0].len(), 6);
assert_eq!(bufs[1].len(), 4);
assert!(b"brickh".iter().zip(bufs[0].iter()).all(|(a, b)| a == b));
assert!(b"ouse".iter().zip(bufs[1].iter()).all(|(a, b)| a == b));
}
}
#[test]
fn vecio() {
let mut c = CircBuf::with_capacity(16).unwrap();
let mut path = env::temp_dir();
path.push("circbuf-rs-test.txt");
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(path.to_str().unwrap())
.unwrap();
assert_eq!(file.write(b"foo\nbar\nbaz\n").unwrap(), 12);
file.seek(SeekFrom::Current(-12)).unwrap();
{
let mut bufs = c.get_avail();
assert_eq!(file.readv(&mut bufs).unwrap(), 12);
}
c.advance_write(12);
c.advance_read(12);
assert_eq!(c.write(b"fizzbuzz").unwrap(), 8);
let mut s = String::new();
assert_eq!(c.read_to_string(&mut s).unwrap(), 8);
assert_eq!(s, "fizzbuzz");
}
#[cfg(feature = "nightly")]
#[bench]
pub fn normal_read(b: &mut Bencher) {
let mut c = CircBuf::with_capacity(16).unwrap();
let mut path = env::temp_dir();
path.push("circbuf-rs-test.txt");
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(path.to_str().unwrap())
.unwrap();
assert_eq!(file.write(b"foo\nbar\nbaz\n").unwrap(), 12);
file.seek(SeekFrom::Current(-12)).unwrap();
c.advance_read(8);
c.advance_write(8);
let mut bufs = c.get_avail();
b.iter(|| {
file.read(&mut bufs[0]).unwrap();
file.read(&mut bufs[1]).unwrap();
file.seek(SeekFrom::Current(-12)).unwrap();
})
}
#[cfg(feature = "nightly")]
#[bench]
pub fn vector_read(b: &mut Bencher) {
let mut c = CircBuf::with_capacity(16).unwrap();
let mut path = env::temp_dir();
path.push("circbuf-rs-test.txt");
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(path.to_str().unwrap())
.unwrap();
assert_eq!(file.write(b"foo\nbar\nbaz\n").unwrap(), 12);
file.seek(SeekFrom::Current(-12)).unwrap();
c.advance_read(8);
c.advance_write(8);
let mut bufs = c.get_avail();
b.iter(|| {
file.readv(&mut bufs).unwrap();
file.seek(SeekFrom::Current(-12)).unwrap();
})
}
#[cfg(feature = "nightly")]
#[bench]
pub fn seek_base(b: &mut Bencher) {
let mut path = env::temp_dir();
path.push("circbuf-rs-test.txt");
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(path.to_str().unwrap())
.unwrap();
b.iter(|| {
file.seek(SeekFrom::Current(1)).unwrap();
})
}
}