diff --git a/src/lib.rs b/src/lib.rs index 8025be5..8e13fae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,74 @@ +//! Circular, a stream abstraction designed for use with nom +//! +//! Circular provides a `Buffer` type that wraps a `Vec` with a position +//! and end. Compared to a stream abstraction that would use `std::io::Read`, +//! it separates the reading and consuming phases. `Read` is designed to write +//! the data in a mutable slice and consume it from the stream as it does that. +//! +//! When used in streaming mode, nom will try to parse a slice, then tell you +//! how much it consumed. So you don't know how much data was actually used +//! until the parser returns. `Circular::Buffer` exposes a `data()` method +//! that gives an immutable slice of all the currently readable data, +//! and a `consume()` method to advance the position in the stream. +//! The `space()` and `fill()` methods are the write counterparts to those methods. +//! +//! ``` +//! extern crate circular; +//! +//! use circular::Buffer; +//! use std::io::Write; +//! +//! fn main() { +//! +//! // allocate a new Buffer +//! let mut b = Buffer::with_capacity(10); +//! assert_eq!(b.available_data(), 0); +//! assert_eq!(b.available_space(), 10); +//! +//! let res = b.write(&b"abcd"[..]); +//! assert_eq!(res.ok(), Some(4)); +//! assert_eq!(b.available_data(), 4); +//! assert_eq!(b.available_space(), 6); +//! +//! //the 4 bytes we wrote are immediately available and usable for parsing +//! assert_eq!(b.data(), &b"abcd"[..]); +//! +//! // this will advance the position from 0 to 2. it does not modify the underlying Vec +//! b.consume(2); +//! assert_eq!(b.available_data(), 2); +//! assert_eq!(b.available_space(), 6); +//! assert_eq!(b.data(), &b"cd"[..]); +//! +//! // shift moves the available data at the beginning of the buffer. +//! // the position is now 0 +//! b.shift(); +//! assert_eq!(b.available_data(), 2); +//! assert_eq!(b.available_space(), 8); +//! assert_eq!(b.data(), &b"cd"[..]); +//! } +//! use std::{cmp, ptr}; use std::io::{self,Write,Read}; use std::iter::repeat; +/// the Buffer contains the underlying memory and data positions +/// +/// In all cases, `0 ≤ position ≤ end ≤ capacity` should be true #[derive(Debug,PartialEq,Clone)] pub struct Buffer { + /// the Vec containing the data memory: Vec, + /// the current capacity of the Buffer capacity: usize, + /// the current beginning of the available data position: usize, + /// the current end of the available data + /// and beginning of the available space end: usize } impl Buffer { + /// allocates a new buffer of maximum size `capacity` pub fn with_capacity(capacity: usize) -> Buffer { let mut v = Vec::with_capacity(capacity); v.extend(repeat(0).take(capacity)); @@ -22,15 +80,21 @@ impl Buffer { } } - pub fn from_slice(sl: &[u8]) -> Buffer { + /// allocates a new buffer containing the slice `data` + /// + /// the buffer starts full, its available data size is exactly `data.len()` + pub fn from_slice(data: &[u8]) -> Buffer { Buffer { - memory: Vec::from(sl), - capacity: sl.len(), + memory: Vec::from(data), + capacity: data.len(), position: 0, - end: sl.len() + end: data.len() } } + /// increases the size of the buffer + /// + /// this does nothing if the buffer is already large enough pub fn grow(&mut self, new_size: usize) -> bool { if self.capacity >= new_size { return false; @@ -41,22 +105,31 @@ impl Buffer { true } + /// returns how much data can be read from the buffer pub fn available_data(&self) -> usize { self.end - self.position } + /// returns how much free space is available to write to pub fn available_space(&self) -> usize { self.capacity - self.end } + /// returns the underlying vector's size pub fn capacity(&self) -> usize { self.capacity } + /// returns true if there is no more data to read pub fn empty(&self) -> bool { self.position == self.end } + /// advances the position tracker + /// + /// if the position gets past the buffer's half, + /// this will call `shift()` to move the remaining data + /// to the beginning of the buffer pub fn consume(&mut self, count: usize) -> usize { let cnt = cmp::min(count, self.available_data()); self.position += cnt; @@ -67,6 +140,12 @@ impl Buffer { cnt } + /// after having written data to the buffer, use this function + /// to indicate how many bytes were written + /// + /// if there is not enough available space, this function can call + /// `shift()` to move the remaining data to the beginning of the + /// buffer pub fn fill(&mut self, count: usize) -> usize { let cnt = cmp::min(count, self.available_space()); self.end += cnt; @@ -101,19 +180,27 @@ impl Buffer { self.position } + /// moves the position and end trackers to the beginning + /// this function does not modify the data pub fn reset(&mut self) { self.position = 0; self.end = 0; } + /// returns a slice with all the available data pub fn data(&self) -> &[u8] { &self.memory[self.position..self.end] } + /// returns a mutable slice with all the available space to + /// write to pub fn space(&mut self) -> &mut[u8] { &mut self.memory[self.end..self.capacity] } + /// moves the data at the beginning of the buffer + /// + /// if the position was more than 0, it is now 0 pub fn shift(&mut self) { if self.position > 0 { unsafe { @@ -125,6 +212,8 @@ impl Buffer { } } + //FIXME: this should probably be rewritten, and tested extensively + #[doc(hidden)] pub fn delete_slice(&mut self, start: usize, length: usize) -> Option { if start + length >= self.available_data() { return None @@ -143,6 +232,8 @@ impl Buffer { Some(self.available_data()) } + //FIXME: this should probably be rewritten, and tested extensively + #[doc(hidden)] pub fn replace_slice(&mut self, data: &[u8], start: usize, length: usize) -> Option { let data_len = data.len(); if start + length > self.available_data() || @@ -170,6 +261,8 @@ impl Buffer { Some(self.available_data()) } + //FIXME: this should probably be rewritten, and tested extensively + #[doc(hidden)] pub fn insert_slice(&mut self, data: &[u8], start: usize) -> Option { let data_len = data.len(); if start > self.available_data() ||