use std::fs;
use std::io::Read;
use std::path::Path;
#[derive(Debug, Clone)]
pub struct MemoryBuffer {
identifier: String,
data: Vec<u8>,
is_mmap: bool,
}
impl MemoryBuffer {
pub fn get_file<P: AsRef<Path>>(path: P) -> Result<Self, String> {
let path = path.as_ref();
let identifier = path.to_string_lossy().to_string();
let data =
fs::read(path).map_err(|e| format!("Cannot open file '{}': {}", identifier, e))?;
Ok(Self {
identifier,
data,
is_mmap: false,
})
}
pub fn get_file_mmap<P: AsRef<Path>>(path: P) -> Result<Self, String> {
let path = path.as_ref();
let identifier = path.to_string_lossy().to_string();
let data =
fs::read(path).map_err(|e| format!("Cannot mmap file '{}': {}", identifier, e))?;
Ok(Self {
identifier,
data,
is_mmap: true, })
}
pub fn get_mem_buffer(data: Vec<u8>, identifier: &str) -> Self {
Self {
identifier: identifier.to_string(),
data,
is_mmap: false,
}
}
pub fn get_mem_buffer_from_string(data: String, identifier: &str) -> Self {
Self {
identifier: identifier.to_string(),
data: data.into_bytes(),
is_mmap: false,
}
}
pub fn get_mem_buffer_copy(data: &[u8], identifier: &str) -> Self {
Self {
identifier: identifier.to_string(),
data: data.to_vec(),
is_mmap: false,
}
}
pub fn get_empty(identifier: &str) -> Self {
Self {
identifier: identifier.to_string(),
data: Vec::new(),
is_mmap: false,
}
}
pub fn get_buffer(&self) -> &[u8] {
&self.data
}
pub fn get_buffer_str(&self) -> Option<&str> {
std::str::from_utf8(&self.data).ok()
}
pub fn get_buffer_lossy(&self) -> String {
String::from_utf8_lossy(&self.data).to_string()
}
pub fn get_buffer_size(&self) -> usize {
self.data.len()
}
pub fn get_buffer_identifier(&self) -> &str {
&self.identifier
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn is_mmap(&self) -> bool {
self.is_mmap
}
pub fn get_slice(&self, offset: usize, length: usize) -> Option<&[u8]> {
if offset + length <= self.data.len() {
Some(&self.data[offset..offset + length])
} else {
None
}
}
pub fn into_bytes(self) -> Vec<u8> {
self.data
}
pub fn get_mut_buffer(&mut self) -> &mut Vec<u8> {
&mut self.data
}
pub fn starts_with(&self, prefix: &[u8]) -> bool {
self.data.starts_with(prefix)
}
pub fn ends_with(&self, suffix: &[u8]) -> bool {
self.data.ends_with(suffix)
}
}
impl AsRef<[u8]> for MemoryBuffer {
fn as_ref(&self) -> &[u8] {
&self.data
}
}
impl std::fmt::Display for MemoryBuffer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"MemoryBuffer({}, {} bytes)",
self.identifier,
self.data.len()
)
}
}
#[derive(Debug, Clone, Copy)]
pub struct MemoryBufferRef<'a> {
data: &'a [u8],
position: usize,
}
impl<'a> MemoryBufferRef<'a> {
pub fn new(data: &'a [u8]) -> Self {
Self { data, position: 0 }
}
pub fn from_buffer(buffer: &'a MemoryBuffer) -> Self {
Self {
data: buffer.get_buffer(),
position: 0,
}
}
pub fn tell(&self) -> usize {
self.position
}
pub fn seek(&mut self, pos: usize) -> Result<(), String> {
if pos <= self.data.len() {
self.position = pos;
Ok(())
} else {
Err(format!(
"Seek past end of buffer: {} > {}",
pos,
self.data.len()
))
}
}
pub fn read_u8(&mut self) -> Option<u8> {
if self.position < self.data.len() {
let byte = self.data[self.position];
self.position += 1;
Some(byte)
} else {
None
}
}
pub fn read_bytes(&mut self, n: usize) -> Option<&'a [u8]> {
if self.position + n <= self.data.len() {
let slice = &self.data[self.position..self.position + n];
self.position += n;
Some(slice)
} else {
None
}
}
pub fn peek_bytes(&self, n: usize) -> Option<&'a [u8]> {
if self.position + n <= self.data.len() {
Some(&self.data[self.position..self.position + n])
} else {
None
}
}
pub fn is_eof(&self) -> bool {
self.position >= self.data.len()
}
pub fn remaining(&self) -> usize {
self.data.len().saturating_sub(self.position)
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn rest(&self) -> &'a [u8] {
&self.data[self.position..]
}
}
#[derive(Debug, Clone)]
pub struct MemoryBufferOwner {
buffer: MemoryBuffer,
position: usize,
}
impl MemoryBufferOwner {
pub fn new(buffer: MemoryBuffer) -> Self {
Self {
buffer,
position: 0,
}
}
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, String> {
MemoryBuffer::get_file(path).map(Self::new)
}
pub fn tell(&self) -> usize {
self.position
}
pub fn seek(&mut self, pos: usize) -> Result<(), String> {
if pos <= self.buffer.get_buffer_size() {
self.position = pos;
Ok(())
} else {
Err("Seek past end of buffer".to_string())
}
}
pub fn read_u8(&mut self) -> Option<u8> {
if self.position < self.buffer.get_buffer_size() {
let byte = self.buffer.get_buffer()[self.position];
self.position += 1;
Some(byte)
} else {
None
}
}
pub fn read_bytes(&mut self, n: usize) -> Option<Vec<u8>> {
if self.position + n <= self.buffer.get_buffer_size() {
let slice = &self.buffer.get_buffer()[self.position..self.position + n];
self.position += n;
Some(slice.to_vec())
} else {
None
}
}
pub fn is_eof(&self) -> bool {
self.position >= self.buffer.get_buffer_size()
}
pub fn remaining(&self) -> usize {
self.buffer.get_buffer_size().saturating_sub(self.position)
}
pub fn buffer(&self) -> &MemoryBuffer {
&self.buffer
}
pub fn into_buffer(self) -> MemoryBuffer {
self.buffer
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
#[test]
fn test_get_mem_buffer() {
let buf = MemoryBuffer::get_mem_buffer(vec![1, 2, 3, 4], "test");
assert_eq!(buf.get_buffer(), &[1, 2, 3, 4]);
assert_eq!(buf.get_buffer_size(), 4);
assert_eq!(buf.get_buffer_identifier(), "test");
assert!(!buf.is_mmap());
assert!(!buf.is_empty());
assert!(buf.starts_with(&[1, 2]));
assert!(buf.ends_with(&[3, 4]));
}
#[test]
fn test_get_mem_buffer_copy() {
let data = vec![10, 20, 30];
let buf = MemoryBuffer::get_mem_buffer_copy(&data, "copy");
assert_eq!(buf.get_buffer(), &[10, 20, 30]);
}
#[test]
fn test_get_mem_buffer_from_string() {
let buf = MemoryBuffer::get_mem_buffer_from_string("hello".to_string(), "str");
assert_eq!(buf.get_buffer(), b"hello");
assert_eq!(buf.get_buffer_str(), Some("hello"));
}
#[test]
fn test_get_empty() {
let buf = MemoryBuffer::get_empty("empty");
assert!(buf.is_empty());
assert_eq!(buf.get_buffer_size(), 0);
}
#[test]
fn test_get_slice() {
let buf = MemoryBuffer::get_mem_buffer(vec![0, 1, 2, 3, 4, 5], "slice");
assert_eq!(buf.get_slice(1, 3), Some(&[1, 2, 3][..]));
assert_eq!(buf.get_slice(5, 2), None);
assert_eq!(buf.get_slice(0, 6), Some(&[0, 1, 2, 3, 4, 5][..]));
}
#[test]
fn test_into_bytes() {
let buf = MemoryBuffer::get_mem_buffer(vec![7, 8, 9], "owned");
let bytes = buf.into_bytes();
assert_eq!(bytes, vec![7, 8, 9]);
}
#[test]
fn test_get_buffer_str() {
let buf = MemoryBuffer::get_mem_buffer(b"utf8 text".to_vec(), "utf8");
assert_eq!(buf.get_buffer_str(), Some("utf8 text"));
}
#[test]
fn test_get_buffer_lossy() {
let buf = MemoryBuffer::get_mem_buffer(vec![0xFF, 0xFE, b'a'], "lossy");
assert!(buf.get_buffer_lossy().contains('a'));
}
#[test]
fn test_get_mut_buffer() {
let mut buf = MemoryBuffer::get_mem_buffer(vec![1, 2, 3], "mut");
buf.get_mut_buffer()[0] = 99;
assert_eq!(buf.get_buffer()[0], 99);
}
#[test]
fn test_display() {
let buf = MemoryBuffer::get_mem_buffer(vec![1, 2, 3, 4], "display");
let display = format!("{}", buf);
assert!(display.contains("display"));
assert!(display.contains("4 bytes"));
}
#[test]
fn test_as_ref() {
let buf = MemoryBuffer::get_mem_buffer(vec![10, 20, 30], "asref");
let slice: &[u8] = buf.as_ref();
assert_eq!(slice, &[10, 20, 30]);
}
#[test]
fn test_ref_new() {
let data = &[1, 2, 3, 4, 5];
let r = MemoryBufferRef::new(data);
assert_eq!(r.tell(), 0);
assert!(!r.is_eof());
assert_eq!(r.remaining(), 5);
}
#[test]
fn test_ref_read_byte() {
let data = &[10, 20, 30];
let mut r = MemoryBufferRef::new(data);
assert_eq!(r.read_u8(), Some(10));
assert_eq!(r.read_u8(), Some(20));
assert_eq!(r.read_u8(), Some(30));
assert_eq!(r.read_u8(), None);
assert!(r.is_eof());
}
#[test]
fn test_ref_read_bytes() {
let data = &[1, 2, 3, 4, 5];
let mut r = MemoryBufferRef::new(data);
assert_eq!(r.read_bytes(2), Some(&[1, 2][..]));
assert_eq!(r.read_bytes(3), Some(&[3, 4, 5][..]));
assert_eq!(r.read_bytes(1), None);
}
#[test]
fn test_ref_peek() {
let data = &[5, 6, 7, 8];
let r = MemoryBufferRef::new(data);
assert_eq!(r.peek_bytes(2), Some(&[5, 6][..]));
assert_eq!(r.tell(), 0); }
#[test]
fn test_ref_seek() {
let data = &[1, 2, 3, 4, 5];
let mut r = MemoryBufferRef::new(data);
assert!(r.seek(3).is_ok());
assert_eq!(r.tell(), 3);
assert_eq!(r.read_u8(), Some(4));
assert!(r.seek(10).is_err());
}
#[test]
fn test_ref_rest() {
let data = &[1, 2, 3, 4, 5];
let mut r = MemoryBufferRef::new(data);
r.read_bytes(2);
assert_eq!(r.rest(), &[3, 4, 5]);
}
#[test]
fn test_ref_from_buffer() {
let buf = MemoryBuffer::get_mem_buffer(vec![7, 8, 9], "ref_buf");
let r = MemoryBufferRef::from_buffer(&buf);
assert_eq!(r.len(), 3);
assert!(!r.is_empty());
}
#[test]
fn test_owner_read_byte() {
let buf = MemoryBuffer::get_mem_buffer(vec![40, 50, 60], "owner");
let mut owner = MemoryBufferOwner::new(buf);
assert_eq!(owner.read_u8(), Some(40));
assert_eq!(owner.remaining(), 2);
}
#[test]
fn test_owner_seek_read() {
let buf = MemoryBuffer::get_mem_buffer(vec![5, 10, 15, 20], "owner");
let mut owner = MemoryBufferOwner::new(buf);
assert!(owner.seek(2).is_ok());
assert_eq!(owner.read_u8(), Some(15));
}
#[test]
fn test_owner_into_buffer() {
let buf = MemoryBuffer::get_mem_buffer(vec![1, 2], "owned");
let owner = MemoryBufferOwner::new(buf);
let recovered = owner.into_buffer();
assert_eq!(recovered.get_buffer(), &[1, 2]);
}
#[test]
fn test_file_read() {
let tmp = std::env::temp_dir().join("mb_test_file.dat");
let mut f = fs::File::create(&tmp).unwrap();
f.write_all(b"hello world").unwrap();
f.flush().unwrap();
let buf = MemoryBuffer::get_file(&tmp).unwrap();
assert_eq!(buf.get_buffer(), b"hello world");
assert_eq!(buf.get_buffer_identifier(), tmp.to_string_lossy());
let _ = fs::remove_file(&tmp);
}
#[test]
fn test_file_not_found() {
let result = MemoryBuffer::get_file("/nonexistent/path/12345.dat");
assert!(result.is_err());
}
}