diff --git a/Cargo.toml b/Cargo.toml index ef3c8cc..f151682 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,4 @@ include = [ ] [dependencies] +bytes = "1.8.0" diff --git a/src/lib.rs b/src/lib.rs index 1b0e13d..9371ee3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,369 +47,421 @@ //! assert_eq!(b.data(), &b"cd"[..]); //! } //! -use std::{cmp, ptr}; -use std::io::{self,Write,Read}; +use std::io::{self, Read, Write}; use std::iter::repeat; +use std::{cmp, ptr}; + +extern crate bytes; +use bytes::BufMut; /// the Buffer contains the underlying memory and data positions /// /// In all cases, `0 ≤ position ≤ end ≤ capacity` should be true -#[derive(Debug,PartialEq,Clone)] +#[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 + /// 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)); - Buffer { - memory: v, - capacity: capacity, - position: 0, - end: 0 - } - } - - /// 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(data), - capacity: data.len(), - position: 0, - 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; + /// 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)); + Buffer { + memory: v, + capacity: capacity, + position: 0, + end: 0, + } } - self.memory.resize(new_size, 0); - self.capacity = new_size; - 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; - if self.position > self.capacity / 2 { - //trace!("consume shift: pos {}, end {}", self.position, self.end); - self.shift(); - } - cnt - } - - /// advances the position tracker - /// - /// This method is similar to `consume()` but will not move data - /// to the beginning of the buffer - pub fn consume_noshift(&mut self, count: usize) -> usize { - let cnt = cmp::min(count, self.available_data()); - self.position += cnt; - 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; - if self.available_space() < self.available_data() + cnt { - //trace!("fill shift: pos {}, end {}", self.position, self.end); - self.shift(); + /// 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(data), + capacity: data.len(), + position: 0, + end: data.len(), + } } - cnt - } + /// 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; + } - /// Get the current position - /// - /// # Examples - /// ``` - /// use circular::Buffer; - /// use std::io::{Read,Write}; - /// - /// let mut output = [0;5]; - /// - /// let mut b = Buffer::with_capacity(10); - /// - /// let res = b.write(&b"abcdefgh"[..]); - /// - /// b.read(&mut output); - /// - /// // Position must be 5 - /// assert_eq!(b.position(), 5); - /// assert_eq!(b.available_data(), 3); - /// ``` - pub fn position(&self) -> usize { - self.position - } + self.memory.resize(new_size, 0); + self.capacity = new_size; + true + } - /// 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 how much data can be read from the buffer + pub fn available_data(&self) -> usize { + self.end - self.position + } - /// returns a slice with all the available data - pub fn data(&self) -> &[u8] { - &self.memory[self.position..self.end] - } + /// returns how much free space is available to write to + pub fn available_space(&self) -> usize { + self.capacity - 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] - } + /// returns the underlying vector's size + pub fn capacity(&self) -> usize { + 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 { - let length = self.end - self.position; - ptr::copy( (&self.memory[self.position..self.end]).as_ptr(), (&mut self.memory[..length]).as_mut_ptr(), length); + /// 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; + if self.position > self.capacity / 2 { + //trace!("consume shift: pos {}, end {}", self.position, self.end); + self.shift(); + } + cnt + } + + /// advances the position tracker + /// + /// This method is similar to `consume()` but will not move data + /// to the beginning of the buffer + pub fn consume_noshift(&mut self, count: usize) -> usize { + let cnt = cmp::min(count, self.available_data()); + self.position += cnt; + 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; + if self.available_space() < self.available_data() + cnt { + //trace!("fill shift: pos {}, end {}", self.position, self.end); + self.shift(); + } + + cnt + } + + /// Get the current position + /// + /// # Examples + /// ``` + /// use circular::Buffer; + /// use std::io::{Read,Write}; + /// + /// let mut output = [0;5]; + /// + /// let mut b = Buffer::with_capacity(10); + /// + /// let res = b.write(&b"abcdefgh"[..]); + /// + /// b.read(&mut output); + /// + /// // Position must be 5 + /// assert_eq!(b.position(), 5); + /// assert_eq!(b.available_data(), 3); + /// ``` + pub fn position(&self) -> usize { + 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 = length; - } - } - } - - //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 + self.end = 0; } - unsafe { - let begin = self.position + start; - let next_end = self.end - length; - ptr::copy( - (&self.memory[begin+length..self.end]).as_ptr(), - (&mut self.memory[begin..next_end]).as_mut_ptr(), - self.end - (begin+length) - ); - self.end = next_end; - } - 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() || - self.position + start + data_len > self.capacity { - return None + /// returns a slice with all the available data + pub fn data(&self) -> &[u8] { + &self.memory[self.position..self.end] } - unsafe { - let begin = self.position + start; - let slice_end = begin + data_len; - // we reduced the data size - if data_len < length { - ptr::copy(data.as_ptr(), (&mut self.memory[begin..slice_end]).as_mut_ptr(), data_len); - - ptr::copy((&self.memory[start+length..self.end]).as_ptr(), (&mut self.memory[slice_end..]).as_mut_ptr(), self.end - (start + length)); - self.end = self.end - (length - data_len); - - // we put more data in the buffer - } else { - ptr::copy((&self.memory[start+length..self.end]).as_ptr(), (&mut self.memory[start+data_len..]).as_mut_ptr(), self.end - (start + length)); - ptr::copy(data.as_ptr(), (&mut self.memory[begin..slice_end]).as_mut_ptr(), data_len); - self.end = self.end + data_len - length; - } - } - 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() || - self.position + self.end + data_len > self.capacity { - return None + /// 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] } - unsafe { - let begin = self.position + start; - let slice_end = begin + data_len; - ptr::copy((&self.memory[start..self.end]).as_ptr(), (&mut self.memory[start+data_len..]).as_mut_ptr(), self.end - start); - ptr::copy(data.as_ptr(), (&mut self.memory[begin..slice_end]).as_mut_ptr(), data_len); - self.end = self.end + data_len; + /// 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 { + let length = self.end - self.position; + ptr::copy( + (&self.memory[self.position..self.end]).as_ptr(), + (&mut self.memory[..length]).as_mut_ptr(), + length, + ); + self.position = 0; + self.end = length; + } + } + } + + //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; + } + + unsafe { + let begin = self.position + start; + let next_end = self.end - length; + ptr::copy( + (&self.memory[begin + length..self.end]).as_ptr(), + (&mut self.memory[begin..next_end]).as_mut_ptr(), + self.end - (begin + length), + ); + self.end = next_end; + } + 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() + || self.position + start + data_len > self.capacity + { + return None; + } + + unsafe { + let begin = self.position + start; + let slice_end = begin + data_len; + // we reduced the data size + if data_len < length { + ptr::copy( + data.as_ptr(), + (&mut self.memory[begin..slice_end]).as_mut_ptr(), + data_len, + ); + + ptr::copy( + (&self.memory[start + length..self.end]).as_ptr(), + (&mut self.memory[slice_end..]).as_mut_ptr(), + self.end - (start + length), + ); + self.end = self.end - (length - data_len); + + // we put more data in the buffer + } else { + ptr::copy( + (&self.memory[start + length..self.end]).as_ptr(), + (&mut self.memory[start + data_len..]).as_mut_ptr(), + self.end - (start + length), + ); + ptr::copy( + data.as_ptr(), + (&mut self.memory[begin..slice_end]).as_mut_ptr(), + data_len, + ); + self.end = self.end + data_len - length; + } + } + 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() || self.position + self.end + data_len > self.capacity { + return None; + } + + unsafe { + let begin = self.position + start; + let slice_end = begin + data_len; + ptr::copy( + (&self.memory[start..self.end]).as_ptr(), + (&mut self.memory[start + data_len..]).as_mut_ptr(), + self.end - start, + ); + ptr::copy( + data.as_ptr(), + (&mut self.memory[begin..slice_end]).as_mut_ptr(), + data_len, + ); + self.end = self.end + data_len; + } + Some(self.available_data()) } - Some(self.available_data()) - } } impl Write for Buffer { - fn write(&mut self, buf: &[u8]) -> io::Result { - match self.space().write(buf) { - Ok(size) => { self.fill(size); Ok(size) }, - err => err + fn write(&mut self, buf: &[u8]) -> io::Result { + match self.space().write(buf) { + Ok(size) => { + self.fill(size); + Ok(size) + } + err => err, + } } - } - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } } impl Read for Buffer { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let len = cmp::min(self.available_data(), buf.len()); - unsafe { - ptr::copy((&self.memory[self.position..self.position+len]).as_ptr(), buf.as_mut_ptr(), len); - self.position += len; + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let len = cmp::min(self.available_data(), buf.len()); + unsafe { + ptr::copy( + (&self.memory[self.position..self.position + len]).as_ptr(), + buf.as_mut_ptr(), + len, + ); + self.position += len; + } + Ok(len) + } +} + +unsafe impl BufMut for Buffer { + fn remaining_mut(&self) -> usize { + self.available_space() + } + + unsafe fn advance_mut(&mut self, cnt: usize) { + self.fill(cnt); + } + + fn chunk_mut(&mut self) -> &mut bytes::buf::UninitSlice { + self.space().into() } - Ok(len) - } } #[cfg(test)] mod tests { - use super::*; - use std::io::Write; + use super::*; + use std::io::Write; - #[test] - fn fill_and_consume() { - 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); + #[test] + fn fill_and_consume() { + 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); - assert_eq!(b.data(), &b"abcd"[..]); + assert_eq!(b.data(), &b"abcd"[..]); - b.consume(2); - assert_eq!(b.available_data(), 2); - assert_eq!(b.available_space(), 6); - assert_eq!(b.data(), &b"cd"[..]); + b.consume(2); + assert_eq!(b.available_data(), 2); + assert_eq!(b.available_space(), 6); + assert_eq!(b.data(), &b"cd"[..]); - b.shift(); - assert_eq!(b.available_data(), 2); - assert_eq!(b.available_space(), 8); - assert_eq!(b.data(), &b"cd"[..]); + b.shift(); + assert_eq!(b.available_data(), 2); + assert_eq!(b.available_space(), 8); + assert_eq!(b.data(), &b"cd"[..]); - assert_eq!(b.write(&b"efghijklmnop"[..]).ok(), Some(8)); - assert_eq!(b.available_data(), 10); - assert_eq!(b.available_space(), 0); - assert_eq!(b.data(), &b"cdefghijkl"[..]); - b.shift(); - assert_eq!(b.available_data(), 10); - assert_eq!(b.available_space(), 0); - assert_eq!(b.data(), &b"cdefghijkl"[..]); - } + assert_eq!(b.write(&b"efghijklmnop"[..]).ok(), Some(8)); + assert_eq!(b.available_data(), 10); + assert_eq!(b.available_space(), 0); + assert_eq!(b.data(), &b"cdefghijkl"[..]); + b.shift(); + assert_eq!(b.available_data(), 10); + assert_eq!(b.available_space(), 0); + assert_eq!(b.data(), &b"cdefghijkl"[..]); + } - #[test] - fn delete() { - let mut b = Buffer::with_capacity(10); - let _ = b.write(&b"abcdefgh"[..]); - assert_eq!(b.available_data(), 8); - assert_eq!(b.available_space(), 2); + #[test] + fn delete() { + let mut b = Buffer::with_capacity(10); + let _ = b.write(&b"abcdefgh"[..]); + assert_eq!(b.available_data(), 8); + assert_eq!(b.available_space(), 2); - assert_eq!(b.delete_slice(2, 3), Some(5)); - assert_eq!(b.available_data(), 5); - assert_eq!(b.available_space(), 5); - assert_eq!(b.data(), &b"abfgh"[..]); + assert_eq!(b.delete_slice(2, 3), Some(5)); + assert_eq!(b.available_data(), 5); + assert_eq!(b.available_space(), 5); + assert_eq!(b.data(), &b"abfgh"[..]); - assert_eq!(b.delete_slice(5, 2), None); - assert_eq!(b.delete_slice(4, 2), None); - } + assert_eq!(b.delete_slice(5, 2), None); + assert_eq!(b.delete_slice(4, 2), None); + } - #[test] - fn replace() { - let mut b = Buffer::with_capacity(10); - let _ = b.write(&b"abcdefgh"[..]); - assert_eq!(b.available_data(), 8); - assert_eq!(b.available_space(), 2); + #[test] + fn replace() { + let mut b = Buffer::with_capacity(10); + let _ = b.write(&b"abcdefgh"[..]); + assert_eq!(b.available_data(), 8); + assert_eq!(b.available_space(), 2); - assert_eq!(b.replace_slice(&b"ABC"[..], 2, 3), Some(8)); - assert_eq!(b.available_data(), 8); - assert_eq!(b.available_space(), 2); - assert_eq!(b.data(), &b"abABCfgh"[..]); + assert_eq!(b.replace_slice(&b"ABC"[..], 2, 3), Some(8)); + assert_eq!(b.available_data(), 8); + assert_eq!(b.available_space(), 2); + assert_eq!(b.data(), &b"abABCfgh"[..]); - assert_eq!(b.replace_slice(&b"XYZ"[..], 8, 3), None); - assert_eq!(b.replace_slice(&b"XYZ"[..], 6, 3), None); + assert_eq!(b.replace_slice(&b"XYZ"[..], 8, 3), None); + assert_eq!(b.replace_slice(&b"XYZ"[..], 6, 3), None); - assert_eq!(b.replace_slice(&b"XYZ"[..], 2, 4), Some(7)); - assert_eq!(b.available_data(), 7); - assert_eq!(b.available_space(), 3); - assert_eq!(b.data(), &b"abXYZgh"[..]); + assert_eq!(b.replace_slice(&b"XYZ"[..], 2, 4), Some(7)); + assert_eq!(b.available_data(), 7); + assert_eq!(b.available_space(), 3); + assert_eq!(b.data(), &b"abXYZgh"[..]); - assert_eq!(b.replace_slice(&b"123"[..], 2, 2), Some(8)); - assert_eq!(b.available_data(), 8); - assert_eq!(b.available_space(), 2); - assert_eq!(b.data(), &b"ab123Zgh"[..]); - } + assert_eq!(b.replace_slice(&b"123"[..], 2, 2), Some(8)); + assert_eq!(b.available_data(), 8); + assert_eq!(b.available_space(), 2); + assert_eq!(b.data(), &b"ab123Zgh"[..]); + } - use std::str; - #[test] - fn set_position() { - let mut output = [0;5]; - let mut b = Buffer::with_capacity(10); - let _ = b.write(&b"abcdefgh"[..]); - let _ = b.read(&mut output); - assert_eq!(b.available_data(), 3); - println!("{:?}", b.position()); - } + use std::str; + #[test] + fn set_position() { + let mut output = [0; 5]; + let mut b = Buffer::with_capacity(10); + let _ = b.write(&b"abcdefgh"[..]); + let _ = b.read(&mut output); + assert_eq!(b.available_data(), 3); + println!("{:?}", b.position()); + } - #[test] - fn consume_without_shift() { - let mut b = Buffer::with_capacity(10); - let _ = b.write(&b"abcdefgh"[..]); - b.consume_noshift(6); - assert_eq!(b.position(), 6); - } + #[test] + fn consume_without_shift() { + let mut b = Buffer::with_capacity(10); + let _ = b.write(&b"abcdefgh"[..]); + b.consume_noshift(6); + assert_eq!(b.position(), 6); + } }