ojcmp 0.4.0

online judge comparer
Documentation
use std::fs::File;
use std::io::{self, BufRead, Read};
use std::panic::panic_any;
use std::ptr;
use std::slice;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct IoByte {
    byte: u8,
    eof: bool,
}

impl IoByte {
    pub const EOF: IoByte = IoByte { byte: 0, eof: true };

    #[inline(always)]
    pub fn from_u8(byte: u8) -> Self {
        Self { byte, eof: false }
    }

    #[inline(always)]
    pub fn as_u8(self) -> u8 {
        self.byte
    }

    #[inline(always)]
    pub fn is_eof(self) -> bool {
        self.eof
    }
}

pub trait ByteRead: BufRead {
    fn next_byte(&mut self) -> IoByte;

    #[allow(clippy::missing_safety_doc)]
    unsafe fn consume_unchecked(&mut self, amt: usize);
}

impl ByteRead for &'_ [u8] {
    fn next_byte(&mut self) -> IoByte {
        match self {
            [] => IoByte::EOF,
            [byte, remain @ ..] => {
                *self = remain;
                IoByte::from_u8(*byte)
            }
        }
    }
    unsafe fn consume_unchecked(&mut self, amt: usize) {
        *self = &self[amt..];
    }
}

pub unsafe trait TrustedRead: Read {
    #[inline]
    fn trusted_read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let nread = <Self as Read>::read(self, buf)?;
        Ok(nread.min(buf.len()))
    }
}

unsafe impl TrustedRead for File {}

#[derive(Debug)]
pub struct ByteReader<R> {
    inner: R,
    buf: Box<[u8]>,
    head: *const u8,
    tail: *const u8,
}

impl<R: Read> ByteReader<R> {
    pub fn with_capacity(capacity: usize, reader: R) -> Self {
        Self {
            inner: reader,
            buf: vec![0; capacity].into(),
            head: ptr::null(),
            tail: ptr::null(),
        }
    }

    #[allow(clippy::missing_safety_doc)]
    pub unsafe fn from_raw(buf: *mut [u8], reader: R) -> Self {
        Self {
            inner: reader,
            buf: Box::from_raw(buf),
            head: ptr::null(),
            tail: ptr::null(),
        }
    }

    #[allow(clippy::missing_safety_doc)]
    pub unsafe fn into_raw(self) -> (*mut [u8], R) {
        let buf = Box::into_raw(self.buf);
        let reader = self.inner;
        (buf, reader)
    }
}

impl<R: Read> Read for ByteReader<R> {
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        self.inner.read(buf)
    }
}

impl<R: TrustedRead> BufRead for ByteReader<R> {
    fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
        if self.head != self.tail {
            let len = self.tail as usize - self.head as usize;
            Ok(unsafe { slice::from_raw_parts(self.head, len) })
        } else {
            let nread = self.inner.trusted_read(&mut *self.buf)?;
            if nread == 0 {
                self.head = ptr::null();
                self.tail = ptr::null();
                Ok(&[])
            } else {
                self.head = self.buf.as_ptr();
                self.tail = unsafe { self.head.add(nread) };
                Ok(unsafe { slice::from_raw_parts(self.head, nread) })
            }
        }
    }

    fn consume(&mut self, amt: usize) {
        self.head = (self.head as usize)
            .saturating_add(amt)
            .min(self.tail as usize) as *const u8;
    }
}

impl<R: TrustedRead> ByteRead for ByteReader<R> {
    #[inline(always)]
    fn next_byte(&mut self) -> IoByte {
        if self.head != self.tail {
            unsafe {
                let byte = *self.head;
                self.head = self.head.add(1);
                IoByte::from_u8(byte)
            }
        } else {
            match self.inner.trusted_read(&mut *self.buf) {
                Ok(nread) => {
                    if nread == 0 {
                        IoByte::EOF
                    } else {
                        unsafe {
                            let byte = *self.buf.as_ptr();
                            self.head = self.buf.as_ptr().add(1);
                            self.tail = self.buf.as_ptr().add(nread);
                            IoByte::from_u8(byte)
                        }
                    }
                }
                Err(e) => panic_any(e),
            }
        }
    }
    unsafe fn consume_unchecked(&mut self, amt: usize) {
        self.head = self.head.add(amt);
    }
}

#[cfg(unix)]
pub mod unix {
    use super::TrustedRead;

    use std::fs::File;
    use std::io::{self, Read};
    use std::os::raw::c_void;
    use std::os::unix::io::AsRawFd;
    use std::panic::panic_any;

    #[derive(Debug)]
    pub struct UnixFdReader {
        file: File,
    }

    impl UnixFdReader {
        pub fn from_file(file: File) -> Self {
            Self { file }
        }
    }

    impl Read for UnixFdReader {
        #[inline(always)]
        fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
            unsafe {
                let buf_ptr: *mut c_void = buf.as_mut_ptr().cast();
                let fd = self.file.as_raw_fd();
                let ret: isize = libc::read(fd, buf_ptr, buf.len());
                if ret < 0 {
                    panic_any(io::Error::last_os_error());
                }
                assert!(ret as usize <= buf.len());
                Ok(ret as usize)
            }
        }
    }

    unsafe impl TrustedRead for UnixFdReader {
        fn trusted_read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
            <Self as Read>::read(self, buf)
        }
    }
}