aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Geoffroy Couprie <geo.couprie@gmail.com>2017-05-04 21:31:37 +0200
committerLibravatar Geoffroy Couprie <geo.couprie@gmail.com>2017-05-04 21:31:37 +0200
commitf15987b34a554ee7db1df643cebe8be9e0317e38 (patch)
tree9892ae25e9020a11b7f42f8583df05f610b0db40
downloadcircular-f15987b34a554ee7db1df643cebe8be9e0317e38.tar.gz
circular-f15987b34a554ee7db1df643cebe8be9e0317e38.tar.bz2
circular-f15987b34a554ee7db1df643cebe8be9e0317e38.zip
initial commit
-rw-r--r--.gitignore3
-rw-r--r--.travis.yml25
-rw-r--r--Cargo.toml6
-rw-r--r--README.md5
-rw-r--r--src/lib.rs270
5 files changed, 309 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4308d82
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+target/
+**/*.rs.bk
+Cargo.lock
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..2f1177d
--- /dev/null
+++ b/.travis.yml
@@ -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
+
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..1cfafd1
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "circular"
+version = "0.1.0"
+authors = ["Geoffroy Couprie <geo.couprie@gmail.com>"]
+
+[dependencies]
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3b6ab91
--- /dev/null
+++ b/README.md
@@ -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.
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..ecfb057
--- /dev/null
+++ b/src/lib.rs
@@ -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"[..]);
+ }
+}