use crate::collection::collect::{AnonymousSlice, WorkingBuffers};
use crate::util::{self, BufferLike, LazyQuantity};
use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
const EMPTY_BUFFER: &[u8] = &[];
pub fn entry(file: &Option<File>, buffers: &mut WorkingBuffers) {
read_to_buffer(file, buffers);
let trimmed = buffers.buffer.trim();
if util::content_len_raw(trimmed) == 0 {
buffers.record.push_field(&EMPTY_BUFFER[..]);
} else {
buffers.record.push_field(trimmed);
}
buffers.buffer.clear()
}
pub fn stat_file(file: &Option<File>, offsets: &[usize], buffers: &mut WorkingBuffers) {
let successful = read_to_buffer(file, buffers).is_some();
let mut success_count = 0;
if successful {
let mut line_start = 0;
for offset in offsets {
let target = line_start + offset + 1;
if target >= buffers.buffer.len {
break;
}
if let Some(newline) = util::find_char(&buffers.buffer.b, target, util::is_newline) {
let number_slice = &buffers.buffer.b[target..newline];
buffers.record.push_field(util::trim_raw(number_slice));
line_start = newline + 1;
success_count += 1;
} else {
break;
}
}
}
for _ in 0..(offsets.len() - success_count) {
buffers.record.push_field(&EMPTY_BUFFER[..]);
}
buffers.buffer.clear();
}
pub struct StatFileLayout {
lines: Vec<Option<StatFileLine>>,
}
pub struct StatFileLine {
entry: usize,
offset: usize,
}
impl StatFileLayout {
#[must_use]
pub fn new(file: &Option<File>, entries: &[&[u8]]) -> Self {
let mut buffer: Vec<u8> = Vec::new();
let read_successful = match file {
None => false,
Some(file) => {
let mut file_mut = file;
let result = file_mut.read_to_end(&mut buffer);
let _ = file_mut.seek(SeekFrom::Start(0));
result.is_ok()
},
};
if read_successful {
let mut lines_to_entries: Vec<Option<StatFileLine>> = Vec::new();
let lines = util::ByteLines::new(&buffer);
for (line, _) in lines {
match util::find_char(line, 0, util::is_space) {
None => {},
Some(space_pos) => {
let key = util::trim_raw(&line[0..space_pos]);
let index_option = find_index(entries, key);
lines_to_entries.push(index_option.map(|idx| StatFileLine {
entry: idx,
offset: entries[idx].len(),
}));
},
}
}
Self {
lines: lines_to_entries,
}
} else {
Self {
lines: Vec::with_capacity(0),
}
}
}
}
fn find_index(source: &[&[u8]], target: &[u8]) -> Option<usize> {
for (i, slice) in source.iter().enumerate() {
if slice.len() == target.len() {
let mut all_equal = true;
for j in 0..target.len() {
if slice[j] != target[j] {
all_equal = false;
break;
}
}
if all_equal {
return Some(i);
}
}
}
None
}
pub fn with_layout(file: &Option<File>, layout: &StatFileLayout, buffers: &mut WorkingBuffers) {
let successful = read_to_buffer(file, buffers).is_some();
if successful {
let lines = util::ByteLines::new(&buffers.buffer.b);
for (i, (line, start)) in lines.enumerate() {
match &layout.lines[i] {
None => {},
Some(line_metadata) => {
let value_start = start + line_metadata.offset + 1;
let value_end = start + line.len();
buffers.slices[line_metadata.entry] = AnonymousSlice {
start: value_start,
length: value_end - value_start,
}
},
}
}
}
for i in 0..buffers.slices.len() {
let slice: &[u8] = match buffers.slices[i].consume(&buffers.buffer.b) {
Some(s) => s,
None => EMPTY_BUFFER,
};
buffers.record.push_field(slice);
}
clear_slice_buffer(buffers);
buffers.buffer.clear();
}
fn clear_slice_buffer(buffers: &mut WorkingBuffers) {
let default_value = <AnonymousSlice>::default();
for i in 0..buffers.slices.len() {
buffers.slices[i] = default_value;
}
}
fn read_to_buffer(file: &Option<File>, buffers: &mut WorkingBuffers) -> Option<usize> {
match file {
None => None,
Some(f) => {
let mut file_mut = f;
let result = match file_mut.read(&mut buffers.buffer.b) {
Err(_) => None,
Ok(len) => {
buffers.buffer.len += len;
if len == 0 {
None
} else {
Some(len)
}
},
};
let _ = file_mut.seek(SeekFrom::Start(0));
result
},
}
}
#[derive(Default)]
struct IoQuantities<'a> {
read_total: LazyQuantity<'a, u64>,
write_total: LazyQuantity<'a, u64>,
sync_total: LazyQuantity<'a, u64>,
async_total: LazyQuantity<'a, u64>,
}
pub fn io(file: &Option<File>, buffers: &mut WorkingBuffers) {
read_to_buffer(file, buffers);
let trimmed = buffers.buffer.trim();
if util::content_len_raw(trimmed) == 0 {
buffers.record.push_field(&EMPTY_BUFFER[..]);
buffers.record.push_field(&EMPTY_BUFFER[..]);
buffers.record.push_field(&EMPTY_BUFFER[..]);
buffers.record.push_field(&EMPTY_BUFFER[..]);
} else {
aggregate_lines(buffers);
}
buffers.buffer.clear();
}
fn aggregate_lines<'a>(buffers: &'a mut WorkingBuffers) {
let mut quantities: IoQuantities<'a> = IoQuantities::default();
let lines = util::ByteLines::new(&buffers.buffer.b);
for (line, _) in lines {
if let Some(space) = util::find_char(line, 0, util::is_space) {
let category_to_end = &line[(space + 1)..];
if let Some(number_slice) = parse_category(category_to_end, b"Read") {
quantities.read_total = quantities.read_total.plus(number_slice);
} else if let Some(number_slice) = parse_category(category_to_end, b"Write") {
quantities.write_total = quantities.write_total.plus(number_slice);
} else if let Some(number_slice) = parse_category(category_to_end, b"Sync") {
quantities.sync_total = quantities.sync_total.plus(number_slice);
} else if let Some(number_slice) = parse_category(category_to_end, b"Async") {
quantities.async_total = quantities.async_total.plus(number_slice);
}
}
}
let IoQuantities {
read_total,
write_total,
sync_total,
async_total,
} = quantities;
read_total.write_to_record(&mut buffers.copy_buffer, &mut buffers.record);
write_total.write_to_record(&mut buffers.copy_buffer, &mut buffers.record);
sync_total.write_to_record(&mut buffers.copy_buffer, &mut buffers.record);
async_total.write_to_record(&mut buffers.copy_buffer, &mut buffers.record);
}
fn parse_category<'a>(slice: &'a [u8], prefix: &[u8]) -> Option<&'a [u8]> {
if slice.len() < prefix.len() {
return None;
}
for (i, b) in prefix.iter().enumerate() {
if slice[i] != *b {
return None;
}
}
if let Some(space) = util::find_char(slice, 0, util::is_space) {
return Some(&slice[(space + 1)..]);
}
None
}
pub fn simple_io(file: &Option<File>, buffers: &mut WorkingBuffers) {
read_to_buffer(file, buffers);
let trimmed = buffers.buffer.trim();
if util::content_len_raw(trimmed) == 0 {
buffers.record.push_field(&EMPTY_BUFFER[..]);
} else {
aggregate_lines_simple(buffers);
}
buffers.buffer.clear();
}
fn aggregate_lines_simple<'a>(buffers: &'a mut WorkingBuffers) {
let mut quantity: LazyQuantity<'a, u64> = LazyQuantity::default();
let lines = util::ByteLines::new(&buffers.buffer.b);
for (line, _) in lines {
if let Some(space) = util::find_char(line, 0, util::is_space) {
let number_slice = &line[(space + 1)..];
quantity = quantity.plus(number_slice);
}
}
quantity.write_to_record(&mut buffers.copy_buffer, &mut buffers.record);
}