use core::fmt;
use std::cell::RefCell;
use std::fmt::{Debug, Formatter};
use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use std::path::Path;
use std::rc::Rc;
use std::vec::Vec;
use crate::error::Error;
#[derive(Clone)]
pub struct ByteVector {
storage: Rc<StorageType>,
}
impl ByteVector {
pub fn length(&self) -> usize {
self.storage.length()
}
pub fn read(&self, buf: &mut [u8], offset: usize, len: usize) -> Result<usize, Error> {
self.storage.read(buf, offset, len)
}
pub fn to_vec(&self) -> Result<Vec<u8>, Error> {
let mut vec = vec![0u8; self.length()];
self.read(&mut vec[..], 0, self.length()).map(|_res| vec)
}
pub fn take(&self, len: usize) -> Result<ByteVector, Error> {
ByteVector::view(&self.storage, 0, len).map(|storage| ByteVector { storage })
}
pub fn drop(&self, len: usize) -> Result<ByteVector, Error> {
let storage_len = self.length();
if len > storage_len {
return Err(Error::new(format!(
"Requested length of {len} bytes exceeds vector length of {vlen}",
len = len,
vlen = storage_len
)));
}
ByteVector::view(&self.storage, len, storage_len - len)
.map(|remainder| ByteVector { storage: remainder })
}
pub fn pad_left(&self, len: usize) -> Result<ByteVector, Error> {
#![allow(clippy::unknown_clippy_lints, clippy::comparison_chain)]
let storage_len = self.length();
if len < storage_len {
Err(Error::new(format!(
"Requested padded length of {len} bytes is smaller than vector length of {vlen}",
len = len,
vlen = storage_len
)))
} else if len == storage_len {
Ok((*self).clone())
} else {
Ok(append(&fill(0, len - storage_len), self))
}
}
pub fn pad_right(&self, len: usize) -> Result<ByteVector, Error> {
#![allow(clippy::unknown_clippy_lints, clippy::comparison_chain)]
let storage_len = self.length();
if len < storage_len {
Err(Error::new(format!(
"Requested padded length of {len} bytes is smaller than vector length of {vlen}",
len = len,
vlen = storage_len
)))
} else if len == storage_len {
Ok((*self).clone())
} else {
Ok(append(self, &fill(0, len - storage_len)))
}
}
fn view(
storage: &Rc<StorageType>,
offset: usize,
len: usize,
) -> Result<Rc<StorageType>, Error> {
let storage_len = storage.length();
if offset > storage_len {
return Err(Error::new(format!(
"Requested view offset of {off} bytes exceeds vector length of {vlen}",
off = offset,
vlen = storage_len
)));
}
if std::usize::MAX - offset < len {
return Err(Error::new(format!("Requested view offset of {off} and length {len} bytes would overflow maximum value of usize", off = offset, len = len)));
}
if offset + len > storage_len {
return Err(Error::new(format!("Requested view offset of {off} and length {len} bytes exceeds vector length of {vlen}", off = offset, len = len, vlen = storage_len)));
}
if len == storage_len {
return Ok((*storage).clone());
}
match **storage {
StorageType::Empty => Err(Error::new(
"Cannot create view for empty vector".to_string(),
)),
StorageType::DirectValue { .. } => {
Ok(Rc::new(StorageType::View {
vstorage: (*storage).clone(),
voffset: offset,
vlen: len,
}))
}
StorageType::Heap { .. } => {
Ok(Rc::new(StorageType::View {
vstorage: (*storage).clone(),
voffset: offset,
vlen: len,
}))
}
StorageType::Append {
ref lhs, ref rhs, ..
} => {
let lhs_len = lhs.length();
if offset + len < lhs_len {
ByteVector::view(&lhs, offset, len)
} else if offset >= lhs_len {
let rhs_offset = offset - lhs_len;
ByteVector::view(&rhs, rhs_offset, len)
} else {
let lhs_view_len = lhs_len - offset;
let rhs_view_len = len - lhs_view_len;
forcomp!({
lhs_view <- ByteVector::view(&lhs, offset, lhs_view_len);
rhs_view <- ByteVector::view(&rhs, 0, rhs_view_len);
} yield {
Rc::new(StorageType::Append { lhs: lhs_view, rhs: rhs_view, len: lhs_view_len + rhs_view_len })
})
}
}
StorageType::View {
ref vstorage,
ref voffset,
..
} => {
if std::usize::MAX - offset < *voffset {
return Err(Error::new(format!("Requested view offset of {off} plus storage offset {voff} would overflow maximum value of usize", off = offset, voff = *voffset)));
}
ByteVector::view(vstorage, *voffset + offset, len)
}
StorageType::File { .. } => {
Ok(Rc::new(StorageType::View {
vstorage: (*storage).clone(),
voffset: offset,
vlen: len,
}))
}
}
}
}
impl PartialEq for ByteVector {
fn eq(&self, other: &ByteVector) -> bool {
if self.length() != other.length() {
return false;
}
let len = self.length();
for i in 0..len {
let lhs = self.storage.unsafe_get(i);
let rhs = other.storage.unsafe_get(i);
if lhs != rhs {
return false;
}
}
true
}
}
impl Eq for ByteVector {}
const CHARS: &[u8] = b"0123456789abcdef";
impl Debug for ByteVector {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
let len = self.length();
let mut v = Vec::with_capacity(len * 2);
for i in 0..len {
let byte = self.storage.unsafe_get(i);
v.push(CHARS[(byte >> 4) as usize]);
v.push(CHARS[(byte & 0xf) as usize]);
}
unsafe {
let result = f.write_str(&String::from_utf8_unchecked(v));
if result.is_err() {
return result;
}
};
Ok(())
}
}
struct WrappedFile {
file: RefCell<File>,
path: String,
}
impl Debug for WrappedFile {
fn fmt(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> {
formatter.write_str(&self.path)
}
}
#[doc(hidden)]
pub const DIRECT_VALUE_SIZE_LIMIT: usize = 8;
#[derive(Debug)]
enum StorageType {
Empty,
DirectValue {
bytes: [u8; DIRECT_VALUE_SIZE_LIMIT],
length: usize,
},
Heap {
bytes: Vec<u8>,
},
Append {
lhs: Rc<StorageType>,
rhs: Rc<StorageType>,
len: usize,
},
View {
vstorage: Rc<StorageType>,
voffset: usize,
vlen: usize,
},
File {
file: WrappedFile,
length: usize,
},
}
impl StorageType {
fn length(&self) -> usize {
match *self {
StorageType::Empty => 0,
StorageType::DirectValue { ref length, .. } => *length,
StorageType::Heap { ref bytes } => bytes.len(),
StorageType::Append { ref len, .. } => *len,
StorageType::View { ref vlen, .. } => *vlen,
StorageType::File { ref length, .. } => *length,
}
}
fn read(&self, buf: &mut [u8], offset: usize, len: usize) -> Result<usize, Error> {
let storage_len = self.length();
if offset > storage_len {
return Err(Error::new(format!(
"Requested read offset of {off} bytes exceeds vector length of {vlen}",
off = offset,
vlen = storage_len
)));
}
if std::usize::MAX - offset < len {
return Err(Error::new(format!("Requested read offset of {off} and length {len} bytes would overflow maximum value of usize", off = offset, len = len)));
}
if offset + len > storage_len {
return Err(Error::new(format!("Requested read offset of {off} and length {len} bytes exceeds vector length of {vlen}", off = offset, len = len, vlen = storage_len)));
}
match *self {
StorageType::Empty => Err(Error::new("Cannot read from empty vector".to_string())),
StorageType::DirectValue {
ref bytes,
ref length,
} => {
let count = std::cmp::min(len, *length - offset);
copy_memory(&bytes[offset..offset + count], buf);
Ok(count)
}
StorageType::Heap { ref bytes } => {
let count = std::cmp::min(len, bytes.len() - offset);
copy_memory(&bytes[offset..offset + count], buf);
Ok(count)
}
StorageType::Append {
ref lhs, ref rhs, ..
} => {
let lhs_result = if offset < lhs.length() {
let lcount = std::cmp::min(lhs.length() - offset, len);
lhs.read(buf, offset, lcount)
} else {
Ok(0)
};
match lhs_result {
Ok(lhs_read_size) => {
let rhs_result = if lhs_read_size < len {
let roff = if lhs.length() < offset {
offset - lhs.length()
} else {
0
};
let rcount = len - lhs_read_size;
let dst = &mut buf[lhs_read_size..lhs_read_size + rcount];
rhs.read(dst, roff, rcount)
} else {
Ok(0)
};
rhs_result.map(|rhs_read_size| lhs_read_size + rhs_read_size)
}
Err(e) => Err(e),
}
}
StorageType::View {
ref vstorage,
ref voffset,
ref vlen,
} => {
if std::usize::MAX - offset < *voffset {
return Err(Error::new(format!("Requested read offset of {off} plus storage offset {voff} would overflow maximum value of usize", off = offset, voff = *voffset)));
}
let count = std::cmp::min(*vlen, len);
vstorage.read(buf, *voffset + offset, count)
}
StorageType::File {
ref file,
ref length,
} => {
let count = std::cmp::min(*length, len);
let f = &mut file.file.borrow_mut();
let read_result = f
.seek(SeekFrom::Start(offset as u64))
.and_then(|_newpos| f.read(&mut buf[0..count]))
.map_err(|io_err| Error::new(format!("Failed to read file: {}", io_err)));
read_result.and_then(|bytes_read| {
if bytes_read < count {
self.read(
&mut buf[bytes_read..len - bytes_read],
offset + bytes_read,
len - bytes_read,
)
.map(|size| size + bytes_read)
} else {
Ok(bytes_read)
}
})
}
}
}
fn unsafe_get(&self, index: usize) -> u8 {
let v: &mut [u8] = &mut [0];
let bytes_read = self.read(v, index, 1).unwrap();
if bytes_read != 1 {
panic!("Failed to read single byte");
}
v[0]
}
}
pub fn empty() -> ByteVector {
ByteVector {
storage: Rc::new(StorageType::Empty),
}
}
pub fn from_vec(bytes: Vec<u8>) -> ByteVector {
let storage = StorageType::Heap { bytes };
ByteVector {
storage: Rc::new(storage),
}
}
pub fn from_slice_copy(bytes: &[u8]) -> ByteVector {
let storage = if bytes.len() <= DIRECT_VALUE_SIZE_LIMIT {
let mut array = [0u8; DIRECT_VALUE_SIZE_LIMIT];
copy_memory(bytes, &mut array);
StorageType::DirectValue {
bytes: array,
length: bytes.len(),
}
} else {
StorageType::Heap {
bytes: bytes.to_owned(),
}
};
ByteVector {
storage: Rc::new(storage),
}
}
pub fn from_slice(bytes: [u8; DIRECT_VALUE_SIZE_LIMIT], length: usize) -> ByteVector {
ByteVector {
storage: Rc::new(StorageType::DirectValue { bytes, length }),
}
}
pub fn file(path: &Path) -> Result<ByteVector, Error> {
let result = forcomp!({
file <- File::open(path);
metadata <- path.metadata();
} yield {
ByteVector {
storage: Rc::new(StorageType::File {
file: WrappedFile {
file: RefCell::new(file),
path: format!("{}", path.display())
},
length: metadata.len() as usize
})
}
});
result.map_err(|io_err| Error::new(format!("Failed to open file: {}", io_err)))
}
pub fn append(lhs: &ByteVector, rhs: &ByteVector) -> ByteVector {
if lhs.length() == 0 && rhs.length() == 0 {
empty()
} else if lhs.length() == 0 {
ByteVector {
storage: rhs.storage.clone(),
}
} else if rhs.length() == 0 {
ByteVector {
storage: lhs.storage.clone(),
}
} else {
let storage = StorageType::Append {
lhs: lhs.storage.clone(),
rhs: rhs.storage.clone(),
len: lhs.storage.length() + rhs.storage.length(),
};
ByteVector {
storage: Rc::new(storage),
}
}
}
pub fn fill(value: u8, count: usize) -> ByteVector {
let storage = StorageType::Heap {
bytes: vec![value; count],
};
ByteVector {
storage: Rc::new(storage),
}
}
fn copy_memory(from: &[u8], mut to: &mut [u8]) -> usize {
use std::io::Write;
to.write(from).unwrap()
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
#[test]
fn byte_vector_macro_should_work() {
let bv1 = from_vec(vec![1, 2, 3, 4]);
let bv2 = byte_vector!(1, 2, 3, 4);
assert_eq!(bv1, bv2);
}
#[test]
fn clone_should_work() {
let bytes = vec![1, 2, 3, 4];
let lhs = from_slice_copy(&bytes);
let rhs = from_slice_copy(&bytes);
let bv1 = append(&lhs, &rhs);
let bv2 = bv1.clone();
assert_eq!(bv1, bv2);
}
#[test]
fn debug_string_should_be_formatted_correctly() {
assert_eq!("01020eff", format!("{:?}", byte_vector!(1, 2, 14, 255)))
}
#[test]
fn length_of_empty_vector_should_be_zero() {
assert_eq!(empty().length(), 0);
}
#[test]
fn length_of_heap_vector_should_be_correct() {
assert_eq!(byte_vector!(1, 2, 3, 4).length(), 4);
}
#[test]
fn append_should_work() {
let bytes = vec![1, 2, 3, 4];
let lhs = from_slice_copy(&bytes);
let rhs = from_slice_copy(&bytes);
let bv = append(&lhs, &rhs);
assert_eq!(bv.length(), 8);
let expected = byte_vector!(1, 2, 3, 4, 1, 2, 3, 4);
assert_eq!(bv, expected);
}
#[test]
fn big_appends_should_work() {
let small = from_vec(vec![1; DIRECT_VALUE_SIZE_LIMIT]);
let big = from_vec(vec![2; DIRECT_VALUE_SIZE_LIMIT + 1]);
let smallbig = append(&small, &big);
let mut smallbig_expected = vec![1; DIRECT_VALUE_SIZE_LIMIT];
smallbig_expected.extend(vec![2; DIRECT_VALUE_SIZE_LIMIT + 1]);
assert_eq!(smallbig, from_vec(smallbig_expected));
let bigsmall = append(&big, &small);
let mut bigsmall_expected = vec![2; DIRECT_VALUE_SIZE_LIMIT + 1];
bigsmall_expected.extend(vec![1; DIRECT_VALUE_SIZE_LIMIT]);
assert_eq!(bigsmall, from_vec(bigsmall_expected));
let bigbig = append(&big, &big);
let bigbig_expected = vec![2; DIRECT_VALUE_SIZE_LIMIT * 2 + 2];
assert_eq!(bigbig, from_vec(bigbig_expected));
}
#[test]
fn fill_should_work() {
let bv = fill(6u8, 4);
let expected = byte_vector!(6, 6, 6, 6);
assert_eq!(bv, expected);
}
#[test]
fn read_should_fail_if_offset_is_out_of_bounds() {
let bv = byte_vector!(1, 2, 3, 4);
let buf: &mut [u8] = &mut [0, 0];
assert!(bv.read(buf, 0, 2).is_ok());
assert!(bv.read(buf, 2, 2).is_ok());
assert!(bv.read(buf, 4, 1).is_err());
}
#[test]
fn read_should_work_for_heap_vector() {
let bv = byte_vector!(1, 2, 3, 4);
let buf: &mut [u8] = &mut [0, 0];
let result = bv.read(buf, 1, 2);
assert_eq!(result.unwrap(), 2);
assert_eq!(buf, [2, 3]);
}
#[test]
fn read_should_work_for_append_vector() {
let bytes = vec![1, 2, 3, 4];
let lhs = from_slice_copy(&bytes);
let rhs = from_slice_copy(&bytes);
let bv = append(&lhs, &rhs);
let buf: &mut [u8] = &mut [0, 0];
{
let result = bv.read(buf, 0, 2);
assert_eq!(result.unwrap(), 2);
assert_eq!(buf, [1, 2]);
}
{
let result = bv.read(buf, 5, 2);
assert_eq!(result.unwrap(), 2);
assert_eq!(buf, [2, 3]);
}
{
let result = bv.read(buf, 3, 2);
assert_eq!(result.unwrap(), 2);
assert_eq!(buf, [4, 1]);
}
}
#[test]
fn read_should_work_for_nested_views() {
let bv = byte_vector!(1, 2, 3, 4);
let view0 = bv.drop(1).unwrap();
let view1 = view0.drop(1).unwrap();
let buf: &mut [u8] = &mut [0, 0];
assert_eq!(view1.read(buf, 0, 2).unwrap(), 2);
assert_eq!(buf, [3, 4]);
}
#[test]
fn to_vec_should_work() {
let input = vec![1, 2, 3, 4];
let lhs = from_slice_copy(&input);
let rhs = from_slice_copy(&input);
let bv = append(&lhs, &rhs);
let result = bv.to_vec();
assert_eq!(result.unwrap(), vec!(1, 2, 3, 4, 1, 2, 3, 4));
}
#[test]
fn take_should_fail_if_length_is_invalid() {
let bv = byte_vector!(1, 2, 3, 4);
assert!(bv.take(2).is_ok());
assert!(bv.take(4).is_ok());
assert!(bv.take(5).is_err());
}
#[test]
fn take_should_work_for_heap_vector() {
let bv = byte_vector!(1, 2, 3, 4);
let result = bv.take(2);
assert_eq!(result.unwrap(), byte_vector!(1, 2));
}
#[test]
fn take_should_work_for_append_vector() {
let bytes = vec![1, 2, 3, 4];
let lhs = from_slice_copy(&bytes);
let rhs = from_slice_copy(&bytes);
let bv = append(&lhs, &rhs);
{
let result = bv.take(2);
assert_eq!(result.unwrap(), byte_vector!(1, 2));
}
{
let result = bv.take(6);
assert_eq!(result.unwrap(), byte_vector!(1, 2, 3, 4, 1, 2));
}
}
#[test]
fn drop_should_fail_if_length_is_invalid() {
let bv = byte_vector!(1, 2, 3, 4);
assert!(bv.drop(2).is_ok());
assert!(bv.drop(4).is_ok());
assert!(bv.drop(5).is_err());
}
#[test]
fn drop_should_work_for_heap_vector() {
let bv = byte_vector!(1, 2, 3, 4);
let result = bv.drop(2);
assert_eq!(result.unwrap(), byte_vector!(3, 4));
}
#[test]
fn drop_should_work_for_append_vector() {
let bytes = vec![1, 2, 3, 4];
let lhs = from_slice_copy(&bytes);
let rhs = from_slice_copy(&bytes);
let bv = append(&lhs, &rhs);
{
let result = bv.drop(2);
assert_eq!(result.unwrap(), byte_vector!(3, 4, 1, 2, 3, 4));
}
{
let result = bv.drop(6);
assert_eq!(result.unwrap(), byte_vector!(3, 4));
}
}
#[test]
fn pad_left_should_work() {
let bv = byte_vector!(1, 2, 3, 4);
assert_eq!(bv.pad_left(4).unwrap(), byte_vector!(1, 2, 3, 4));
assert_eq!(bv.pad_left(5).unwrap(), byte_vector!(0, 1, 2, 3, 4));
assert_eq!(bv.pad_left(6).unwrap(), byte_vector!(0, 0, 1, 2, 3, 4));
}
#[test]
fn pad_left_should_fail_if_length_is_invalid() {
let bv = byte_vector!(1, 2, 3, 4);
assert_eq!(
bv.pad_left(3).unwrap_err().message(),
"Requested padded length of 3 bytes is smaller than vector length of 4"
);
}
#[test]
fn pad_right_should_work() {
let bv = byte_vector!(1, 2, 3, 4);
assert_eq!(bv.pad_right(4).unwrap(), byte_vector!(1, 2, 3, 4));
assert_eq!(bv.pad_right(5).unwrap(), byte_vector!(1, 2, 3, 4, 0));
assert_eq!(bv.pad_right(6).unwrap(), byte_vector!(1, 2, 3, 4, 0, 0));
}
#[test]
fn pad_right_should_fail_if_length_is_invalid() {
let bv = byte_vector!(1, 2, 3, 4);
assert_eq!(
bv.pad_right(3).unwrap_err().message(),
"Requested padded length of 3 bytes is smaller than vector length of 4"
);
}
#[test]
fn file_should_work() {
use std::io::Write;
use std::path::Path;
let path = Path::new("/tmp/rcodec-test-file");
let contents = [1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let mut write_file = match fs::File::create(path) {
Err(why) => panic!("Couldn't create test file {:?}: {}", path.to_str(), why),
Ok(file) => file,
};
if let Err(why) = write_file.write_all(&contents) {
panic!("Couldn't write test file {:?}: {}", path.to_str(), why)
}
let bv_result = file(path);
assert!(bv_result.is_ok());
let bv = bv_result.unwrap();
assert_eq!(bv, byte_vector!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
let dropped = bv.drop(5);
assert!(dropped.is_ok());
assert_eq!(dropped.unwrap(), byte_vector!(6, 7, 8, 9, 10));
let _ignore = fs::remove_file(&path);
}
}