use std::fs::File;
use std::io::{self, BufRead, BufReader, Cursor, Read, Seek, SeekFrom};
use std::os::raw::{c_char, c_int, c_void};
use flate2::read::GzDecoder;
pub struct SeqReader {
inner: BufReader<Box<dyn ReadSeek>>,
}
trait ReadSeek: Read + Seek {}
impl<T: Read + Seek> ReadSeek for T {}
impl SeqReader {
pub fn open(path: &str) -> io::Result<Self> {
let mut file = File::open(path)?;
let mut magic = [0u8; 2];
let n = file.read(&mut magic)?;
file.seek(SeekFrom::Start(0))?;
if n >= 2 && magic[0] == 0x1f && magic[1] == 0x8b {
let mut decoder = GzDecoder::new(file);
let mut data = Vec::new();
decoder.read_to_end(&mut data)?;
Ok(SeqReader {
inner: BufReader::new(Box::new(Cursor::new(data))),
})
} else {
Ok(SeqReader {
inner: BufReader::new(Box::new(file)),
})
}
}
pub fn stdin() -> io::Result<Self> {
let mut data = Vec::new();
io::stdin().read_to_end(&mut data)?;
Ok(SeqReader {
inner: BufReader::new(Box::new(Cursor::new(data))),
})
}
pub fn gets(&mut self, buf: *mut c_char, max_len: c_int) -> bool {
let max = max_len as usize;
if max == 0 {
return false;
}
unsafe {
*buf = 0;
}
let mut line = Vec::new();
let mut total = 0usize;
loop {
let available = self.inner.fill_buf().unwrap_or(&[]);
if available.is_empty() {
break; }
let limit = (max - 1 - total).min(available.len());
let mut consumed = 0;
for i in 0..limit {
line.push(available[i]);
consumed = i + 1;
total += 1;
if available[i] == b'\n' {
break;
}
}
self.inner.consume(consumed);
if total >= max - 1 || line.last() == Some(&b'\n') {
break;
}
}
if line.is_empty() {
return false;
}
unsafe {
std::ptr::copy_nonoverlapping(line.as_ptr(), buf as *mut u8, line.len());
*buf.add(line.len()) = 0;
}
true
}
pub fn rewind(&mut self) -> bool {
self.inner.seek(SeekFrom::Start(0)).is_ok()
}
}
pub unsafe fn seq_reader_open(path: *const c_char) -> *mut c_void {
let c_str = std::ffi::CStr::from_ptr(path);
let path_str = match c_str.to_str() {
Ok(s) => s,
Err(_) => return std::ptr::null_mut(),
};
match SeqReader::open(path_str) {
Ok(reader) => Box::into_raw(Box::new(reader)) as *mut c_void,
Err(_) => std::ptr::null_mut(),
}
}
pub fn seq_reader_open_stdin() -> *mut c_void {
match SeqReader::stdin() {
Ok(reader) => Box::into_raw(Box::new(reader)) as *mut c_void,
Err(_) => std::ptr::null_mut(),
}
}
pub unsafe fn seq_reader_gets(
handle: *mut c_void,
buf: *mut c_char,
max_len: c_int,
) -> *mut c_char {
let reader = &mut *(handle as *mut SeqReader);
if reader.gets(buf, max_len) {
buf
} else {
std::ptr::null_mut()
}
}
pub unsafe fn seq_reader_seek(handle: *mut c_void, _offset: i64, _whence: c_int) -> i64 {
let reader = &mut *(handle as *mut SeqReader);
if reader.rewind() {
0
} else {
-1
}
}
pub unsafe fn seq_reader_close(handle: *mut c_void) -> c_int {
if !handle.is_null() {
drop(Box::from_raw(handle as *mut SeqReader));
}
0
}