initial commit

This commit is contained in:
Geoffroy Couprie 2017-05-04 21:31:37 +02:00
commit f15987b34a
5 changed files with 309 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
target/
**/*.rs.bk
Cargo.lock

25
.travis.yml Normal file
View File

@ -0,0 +1,25 @@
language: rust
cache: cargo
rust:
- nightly
- beta
- stable
before_script:
- cargo install cargo-travis --force
- export PATH=$HOME/.cargo/bin:$PATH;
script:
- cargo build
- cargo test
after_success:
- cargo coveralls
dist: trusty
#sudo: false

6
Cargo.toml Normal file
View File

@ -0,0 +1,6 @@
[package]
name = "circular"
version = "0.1.0"
authors = ["Geoffroy Couprie <geo.couprie@gmail.com>"]
[dependencies]

5
README.md Normal file
View File

@ -0,0 +1,5 @@
# Circular
Circular is a stream abstraction designed for use with nom. It can expose the
available data, a mutable slice of the available space, and it separates
reading data from actually consuming it from the buffer.

270
src/lib.rs Normal file
View File

@ -0,0 +1,270 @@
use std::{cmp, ptr};
use std::io::{self,Write,Read};
use std::iter::repeat;
#[derive(Debug,PartialEq,Clone)]
pub struct Buffer {
memory: Vec<u8>,
capacity: usize,
position: usize,
end: usize
}
impl Buffer {
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
}
}
pub fn from_slice(sl: &[u8]) -> Buffer {
Buffer {
memory: Vec::from(sl),
capacity: sl.len(),
position: 0,
end: sl.len()
}
}
pub fn grow(&mut self, new_size: usize) -> bool {
if self.capacity >= new_size {
return false;
}
self.memory.resize(new_size, 0);
self.capacity = new_size;
true
}
pub fn available_data(&self) -> usize {
self.end - self.position
}
pub fn available_space(&self) -> usize {
self.capacity - self.end
}
pub fn capacity(&self) -> usize {
self.capacity
}
pub fn empty(&self) -> bool {
self.position == self.end
}
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
}
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
}
pub fn reset(&mut self) {
self.position = 0;
self.end = 0;
}
pub fn data(&self) -> &[u8] {
&self.memory[self.position..self.end]
}
pub fn space(&mut self) -> &mut[u8] {
&mut self.memory[self.end..self.capacity]
}
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;
}
}
}
pub fn delete_slice(&mut self, start: usize, length: usize) -> Option<usize> {
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())
}
pub fn replace_slice(&mut self, data: &[u8], start: usize, length: usize) -> Option<usize> {
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())
}
pub fn insert_slice(&mut self, data: &[u8], start: usize) -> Option<usize> {
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())
}
}
impl Write for Buffer {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match self.space().write(buf) {
Ok(size) => { self.fill(size); Ok(size) },
err => err
}
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl Read for Buffer {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
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)
}
}
#[cfg(test)]
mod tests {
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);
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.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"[..]);
}
#[test]
fn delete() {
let mut b = Buffer::with_capacity(10);
let res = 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(5, 2), None);
assert_eq!(b.delete_slice(4, 2), None);
}
#[test]
fn replace() {
let mut b = Buffer::with_capacity(10);
let res = 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"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"123"[..], 2, 2), Some(8));
assert_eq!(b.available_data(), 8);
assert_eq!(b.available_space(), 2);
assert_eq!(b.data(), &b"ab123Zgh"[..]);
}
}