use std::ops::Range;
use std::io::Cursor;
use std::fs::File;
use std::sync::Arc;
use std::path::{Path, PathBuf};
use byteorder::{
ReadBytesExt,
BigEndian,
LittleEndian
};
use memmap::Mmap;
use ron_uuid::UUID;
use {
Result,
Str,
Endianess,
};
#[derive(Clone, Debug)]
pub enum Region {
Undefined{
address_bits: usize,
name: Str,
uuid: UUID,
},
Buffer{
address_bits: usize,
name: Str,
uuid: UUID,
offset: u64,
buffer: Arc<[u8]>,
},
File{
address_bits: usize,
name: Str,
uuid: UUID,
offset: u64,
file: Arc<Mmap>,
path: Option<PathBuf>,
file_offset: u64,
},
}
impl Region {
pub fn undefined<S, U>(name: S, address_bits: usize, uuid: U) -> Region where S: Into<Str>, U: Into<Option<UUID>> {
Region::Undefined{
address_bits: address_bits,
name: name.into(),
uuid: uuid.into().unwrap_or_default(),
}
}
pub fn from_mmap<S, O, P, U>(name: S, address_bits: usize, mmap: Mmap, path: P, file_offset: O, offset: O, uuid: U) -> Region where S: Into<Str>, O: Into<Option<u64>>, P: Into<Option<PathBuf>>, U: Into<Option<UUID>> {
Region::File{
offset: offset.into().unwrap_or(0),
name: name.into(),
file: Arc::new(mmap),
address_bits: address_bits,
uuid: uuid.into().unwrap_or_default(),
path: path.into(),
file_offset: file_offset.into().unwrap_or(0),
}
}
pub fn from_file<S, O, P, U>(name: S, address_bits: usize, fd: File, path: P, offset: O, uuid: U) -> Result<Region> where S: Into<Str>, O: Into<Option<u64>>, P: Into<Option<PathBuf>>, U: Into<Option<UUID>> {
Ok(Region::File{
offset: offset.into().unwrap_or(0),
name: name.into(),
file: Arc::new(unsafe { Mmap::map(&fd)? }),
address_bits: address_bits,
uuid: uuid.into().unwrap_or_default(),
path: path.into(),
file_offset: 0,
})
}
pub fn from_buf<S, O, B, U>(name: S, address_bits: usize, buf: B, offset: O, uuid: U) -> Region where S: Into<Str>, O: Into<Option<u64>>, B: Into<Arc<[u8]>>, U: Into<Option<UUID>> {
Region::Buffer{
offset: offset.into().unwrap_or(0),
name: name.into(),
buffer: buf.into(),
address_bits: address_bits,
uuid: uuid.into().unwrap_or_default(),
}
}
pub fn file<'a>(&'a self) -> Option<(&'a Path, u64)> {
match self {
&Region::Undefined{ .. } => None,
&Region::File{ ref path, file_offset, .. } =>
path.as_ref().map(|x| (PathBuf::as_path(x), file_offset)),
&Region::Buffer{ .. } => None,
}
}
pub fn name<'a>(&'a self) -> &'a Str {
match self {
&Region::Undefined{ ref name,.. } => name,
&Region::File{ ref name,.. } => name,
&Region::Buffer{ ref name,.. } => name,
}
}
pub fn rename(&mut self, new: Str) {
match self {
&mut Region::Undefined{ ref mut name,.. } => { *name = new }
&mut Region::File{ ref mut name,.. } => { *name = new }
&mut Region::Buffer{ ref mut name,.. } => { *name = new }
}
}
pub fn uuid<'a>(&'a self) -> &'a UUID {
match self {
&Region::Undefined{ ref uuid,.. } => uuid,
&Region::File{ ref uuid,.. } => uuid,
&Region::Buffer{ ref uuid,.. } => uuid,
}
}
pub fn address_bits(&self) -> usize {
match self {
&Region::Undefined{ address_bits,.. } => address_bits,
&Region::File{ address_bits,.. } => address_bits,
&Region::Buffer{ address_bits,.. } => address_bits,
}
}
pub fn defined(&self) -> Range<u64> {
match self {
&Region::Undefined{ .. } => 0..0,
&Region::File{ offset, ref file,.. } =>
offset..offset + file.len() as u64,
&Region::Buffer{ offset, ref buffer,.. } =>
offset..offset + buffer.len() as u64,
}
}
pub fn read<'a>(&'a self, start: u64, len: usize) -> Result<&'a[u8]> {
if !self.in_range(start..start + len as u64) { return Err("Out of range".into()); }
if len == 0 || !self.is_defined(start..start + len as u64) {
return Err("Undefined".into());
}
match self {
&Region::Undefined{ .. } => unreachable!(),
&Region::File{ ref file, offset,.. } => {
let a = ((start - offset) as usize)..((start - offset) as usize + len);
Ok(&file[a])
}
&Region::Buffer{ ref buffer, offset,.. } => {
let a = ((start - offset) as usize)..((start - offset) as usize + len);
Ok(&buffer[a])
}
}
}
pub fn try_read(&self, address: u64, buf: &mut [u8]) -> Result<usize> {
use std::cmp;
if !self.in_range(address..address + 1) { return Err("Out of range".into()); }
if !self.is_defined(address..address + 1) { return Ok(0); }
match self {
&Region::Undefined{ .. } if buf.len() == 0 => Ok(0),
&Region::Undefined{ .. } => unreachable!(),
&Region::File{ ref file, offset,.. } => {
let o = offset as usize;
let l = cmp::min(buf.len(), file.len());
let a = (address as usize - o)..(address as usize + l - o);
buf[0..l].clone_from_slice(&file[a]);
Ok(l)
}
&Region::Buffer{ ref buffer, offset,.. } => {
let o = offset as usize;
let address = address as usize - o;
let l = cmp::min(buf.len(), buffer.len() - address);
let a = (address as usize)..(address as usize + l);
buf[0..l].clone_from_slice(&buffer[a]);
Ok(l)
}
}
}
pub fn read_integer(&self, address: u64, endianess: Endianess, bytes: usize) -> Result<u64> {
match bytes {
1 => {
Ok(self.read(address, 1)?[0] as u64)
}
2 => {
let buf = self.read(address, 2)?;
let mut cur = Cursor::new(buf);
match endianess {
Endianess::Little => Ok(cur.read_u16::<LittleEndian>()? as u64),
Endianess::Big => Ok(cur.read_u16::<BigEndian>()? as u64),
}
}
4 => {
let buf = self.read(address, 4)?;
let mut cur = Cursor::new(buf);
match endianess {
Endianess::Little => Ok(cur.read_u32::<LittleEndian>()? as u64),
Endianess::Big => Ok(cur.read_u32::<BigEndian>()? as u64),
}
}
8 => {
let buf = self.read(address, 8)?;
let mut cur = Cursor::new(buf);
match endianess {
Endianess::Little => cur.read_u64::<LittleEndian>().map_err(|e| e.into()),
Endianess::Big => cur.read_u64::<BigEndian>().map_err(|e| e.into()),
}
}
_ => Err(format!("reaidng a {} byte integer is unimplemented",bytes).into()),
}
}
pub fn in_range(&self, range: Range<u64>) -> bool {
match self {
&Region::Undefined{ address_bits,.. } => {
64 - range.start.leading_zeros() as usize <= address_bits &&
64 - range.end.saturating_sub(1).leading_zeros() as usize <= address_bits
}
&Region::Buffer{ address_bits,.. } => {
64 - range.start.leading_zeros() as usize <= address_bits &&
64 - range.end.saturating_sub(1).leading_zeros() as usize <= address_bits
}
&Region::File{ address_bits,.. } => {
64 - range.start.leading_zeros() as usize <= address_bits &&
64 - range.end.saturating_sub(1).leading_zeros() as usize <= address_bits
}
}
}
pub fn is_defined(&self, range: Range<u64>) -> bool {
match self {
&Region::Undefined{ .. } => false,
&Region::Buffer{ ref buffer, offset, address_bits,.. } => {
let end = if address_bits < 64 { 1 << address_bits } else { 0xFFFFFFFFFFFFFFFF };
let head = 0..offset;
let tail = offset + (buffer.len() as u64)..end;
let outside = head.end > range.start || tail.start < range.end;
!outside
}
&Region::File{ ref file, offset, address_bits,.. } => {
let end = if address_bits < 64 { 1 << address_bits } else { 0xFFFFFFFFFFFFFFFF };
let head = 0..offset;
let tail = offset + (file.len() as u64)..end;
let outside = head.end > range.start || tail.start < range.end;
!outside
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn construct() {
let b: Box<[u8]> = vec![1, 2, 3].into();
let l1 = Region::undefined("l1",6,None);
let l2 = Region::from_buf("l2",3,b,None,None);
assert_eq!(l1.defined(), 0..0);
assert_eq!(l2.defined(), 0..3);
assert_eq!(l1.address_bits(), 6);
assert_eq!(l1.name(), "l1");
assert_eq!(l2.address_bits(), 3);
assert_eq!(l2.name(), "l2");
}
#[test]
fn is_defined() {
let b: Box<[u8]> = vec![1, 2, 3].into();
let l1 = Region::from_buf("l2",16,b,5,None);
assert_eq!(l1.defined(), 5..8);
assert_eq!(l1.is_defined(0..1), false);
assert!(l1.try_read(0, &mut vec![0u8; 1]).is_ok());
assert_eq!(l1.is_defined(0..5), false);
assert!(l1.try_read(0, &mut vec![0u8; 4]).is_ok());
assert_eq!(l1.is_defined(3..7), false);
assert!(l1.try_read(3, &mut vec![0u8; 4]).is_ok());
assert_eq!(l1.is_defined(5..6), true);
assert!(l1.try_read(5, &mut vec![0u8; 1]).is_ok());
assert_eq!(l1.is_defined(5..8), true);
assert!(l1.try_read(5, &mut vec![0u8; 3]).is_ok());
assert_eq!(l1.is_defined(9..20), false);
assert!(l1.try_read(9, &mut vec![0u8; 10]).is_ok());
}
#[test]
fn in_range() {
let b: Box<[u8]> = vec![1, 2, 3].into();
let l1 = Region::from_buf("l2",16,b,0,None);
assert_eq!(l1.defined(), 0..3);
assert_eq!(l1.in_range(0..1), true);
assert!(l1.try_read(0, &mut vec![0u8; 1]).is_ok());
assert_eq!(l1.in_range(0x10000..0x10001), false);
assert_eq!(l1.in_range(3..0x1_00_00), true);
assert_eq!(l1.in_range(5..0x10001), false);
}
#[test]
fn read() {
let b: Box<[u8]> = vec![1, 2, 3].into();
let l1 = Region::from_buf("l2",3,b,None,None);
{
let buf = l1.read(0, 1).unwrap();
assert_eq!(buf, [1]);
}
{
let buf = l1.read(2, 1).unwrap();
assert_eq!(buf, [3]);
}
{
let buf = l1.read(1, 2).unwrap();
assert_eq!(buf, [2,3]);
}
}
#[test]
fn try_read() {
let b: Box<[u8]> = vec![1, 2, 3].into();
let l1 = Region::from_buf("l2",3,b,None,None);
{
let mut buf = [0u8; 3];
assert_eq!(l1.try_read(0, &mut buf).ok(), Some(3));
assert_eq!(buf, [1, 2, 3]);
}
{
let mut buf = [0u8; 3];
assert_eq!(l1.try_read(2, &mut buf).ok(), Some(1));
assert_eq!(buf, [3, 0, 0]);
}
{
let mut buf = [0u8; 3];
assert_eq!(l1.try_read(1, &mut buf).ok(), Some(2));
assert_eq!(buf, [2, 3, 0]);
}
}
}