use ::std::{
cmp::Ordering::{self, Equal},
hash::{Hash, Hasher},
io::{self, prelude::*, SeekFrom},
ptr,
sync::Mutex,
};
pub struct IOInterner<T: Write + Read + Seek> {
pub inner: Mutex<T>
}
impl<T: Write + Read + Seek> IOInterner<T> {
#[inline]
pub fn new(x: T) -> Self {
Self { inner: Mutex::new(x) }
}
#[inline]
pub fn flush(&self) -> io::Result<()> {
self.inner.lock().unwrap().flush()
}
pub fn get_or_intern<U: Read + Seek>(&self, mut buf: U) -> io::Result<IOEntry<'_, T>> {
let mut l = self.inner.lock().unwrap();
let buf_len = buf.seek(SeekFrom::End(0))?;
if buf_len == 0 {
return Ok(IOEntry {
start_init: 0,
len: 0,
guard: &self.inner,
});
}
let len = l.seek(SeekFrom::End(0))?;
for start in 0..len.saturating_sub(buf_len) {
l.seek(SeekFrom::Start(start))?;
buf.seek(SeekFrom::Start(0))?;
if starts_with(&mut *l, &mut buf)? {
return Ok(IOEntry {
start_init: start,
len: buf_len,
guard: &self.inner,
});
}
}
l.seek(SeekFrom::Start(len))?;
buf.seek(SeekFrom::Start(0))?;
io::copy(&mut buf, &mut *l)?;
l.flush()?;
Ok(IOEntry {
start_init: len,
len: buf_len,
guard: &self.inner,
})
}
}
pub struct IOEntry<'a, T> {
start_init: u64,
len: u64,
guard: &'a Mutex<T>,
}
impl<'a, T> Clone for IOEntry<'a, T> {
fn clone(&self) -> Self {
unsafe { ptr::read(self) }
}
}
impl<'a, T> IOEntry<'a, T> {
pub fn get_object(&self) -> IOObj<'a, T> {
IOObj {
start_init: self.start_init,
start: self.start_init,
len: self.len,
guard: self.guard
}
}
}
impl<'a, T> PartialEq for IOEntry<'a, T> {
fn eq(&self, other: &Self) -> bool {
if ptr::eq(self.guard, other.guard) {
self.start_init == other.start_init && self.len == other.len
} else {
panic!("entries from different interners cannot be compared,consider use `IOEntry::get_object` to create `IOObj`ects")
}
}
}
impl<'a, T> Eq for IOEntry<'a, T> {}
impl<'a, T> Hash for IOEntry<'a, T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(self.start_init, self.len).hash(state);
}
}
pub struct IOObj<'a, T> {
start_init: u64,
start: u64,
len: u64,
guard: &'a Mutex<T>,
}
impl<'a, T> Clone for IOObj<'a, T> {
fn clone(&self) -> Self {
unsafe { ptr::read(self) }
}
}
impl<'a, T> IOObj<'a, T> {
#[inline]
pub fn to_entry(&self) -> IOEntry<'a, T> {
let mut a = self.clone();
a.seek(SeekFrom::Start(0)).unwrap();
IOEntry {
start_init: a.start,
len: a.len,
guard: a.guard,
}
}
fn fields_eq(&self, other: &Self) -> bool {
ptr::eq(self.guard, other.guard) && self.start == other.start && self.len == other.len
}
}
impl<'a, T: Read + Seek> Read for IOObj<'a, T> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if self.len == 0 {
return Ok(0);
}
let mut l = self.guard.lock().unwrap();
l.seek(SeekFrom::Start(self.start))?;
let len = Read::take(&mut *l, self.len).read(buf)?;
self.seek(SeekFrom::Current(len as _))?;
Ok(len)
}
}
impl<'a, T: Read + Seek> PartialEq for IOObj<'a, T> {
fn eq(&self, other: &Self) -> bool {
self.fields_eq(other) || eq(self.clone(), other.clone()).expect("io error while testing for equality")
}
}
impl<'a, T: Read + Seek> Eq for IOObj<'a, T> {}
impl<'a, T: Read + Seek> Hash for IOObj<'a, T> {
fn hash<H: Hasher>(&self, state: &mut H) {
hash(self.clone(), state).expect("io error while hashing")
}
}
impl<'a, T: Read + Seek> PartialOrd for IOObj<'a, T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<'a, T: Read + Seek> Ord for IOObj<'a, T> {
fn cmp(&self, other: &Self) -> Ordering {
if self.fields_eq(other) {
return Equal;
}
cmp(self.clone(), other.clone()).expect("io error while comparing the order")
}
}
impl<'a, T> Seek for IOObj<'a, T> {
#[inline]
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
match pos {
SeekFrom::Current(x) => {
if x.is_negative() {
let x = x.abs() as u64;
let new_start = self.start - x;
if new_start < self.start_init {
return Err(io::ErrorKind::InvalidInput.into());
}
self.start = new_start;
self.len += x;
} else {
let x = x.abs() as u64;
if x > self.len {
self.start += self.len;
self.len = 0;
} else {
self.start += x;
self.len -= x;
}
}
Ok(self.start - self.start_init)
}
SeekFrom::Start(x) => {
let new_start = self.start_init + x;
if new_start > self.start {
self.seek(SeekFrom::Current((new_start - self.start) as i64))
} else {
self.seek(SeekFrom::Current(-((self.start - new_start) as i64)))
}
}
SeekFrom::End(x) => {
self.start += self.len;
self.len = 0;
self.seek(SeekFrom::Current(x))
}
}
}
}
pub fn io_op<R1: Read, R2: Read, T>(
mut x: R1,
mut y: R2,
callback: impl Fn(&[u8], &[u8]) -> Option<T>,
) -> io::Result<Option<T>> {
let mut buf1 = [0; 512];
let mut buf2 = [0; 512];
Ok(loop {
let mut buf1r = &mut buf1[..];
let mut buf2r = &mut buf2[..];
let mut x = Read::take(&mut x, buf1r.len() as _);
let mut y = Read::take(&mut y, buf1r.len() as _);
let readed1 = io::copy(&mut x, &mut buf1r)? as usize;
let readed2 = io::copy(&mut y, &mut buf2r)? as usize;
let a = callback(&buf1[..readed1], &buf2[..readed2]);
if a.is_some() {
break a;
}
if readed1 == 0 || readed2 == 0 {
break None;
}
})
}
pub fn eq<R1: Read, R2: Read>(x: R1, y: R2) -> io::Result<bool> {
io_op(x, y, |x, y| if x == y { None } else { Some(()) }).map(|e| e.is_none())
}
pub fn starts_with<R1: Read, R2: Read>(haystack: R1, needle: R2) -> io::Result<bool> {
io_op(haystack, needle, |haystack, needle| if haystack.starts_with(needle) { None } else { Some(()) }).map(|e| e.is_none())
}
pub fn cmp<R1: Read, R2: Read>(x: R1, y: R2) -> io::Result<Ordering> {
io_op(x, y, |x, y| match x.cmp(y) {
Equal => None,
x => Some(x)
}).map(|e| e.unwrap_or(Equal))
}
pub fn hash<R1: Read, H: Hasher>(
mut x: R1,
state: &mut H,
) -> io::Result<()> {
let mut buf1 = [0; 512];
let mut len = 0;
loop {
let mut buf1r = &mut buf1[..];
let mut x = Read::take(&mut x, buf1r.len() as _);
let readed1 = io::copy(&mut x, &mut buf1r)? as usize;
Hash::hash_slice(&buf1[..readed1], state);
len += readed1;
if readed1 == 0 {
break;
}
}
len.hash(state);
Ok(())
}