use crate::backend::native::{
graph_file::buffers::WriteBuffer,
graph_file::file_ops::IOMode,
types::NativeResult,
};
#[cfg(feature = "v2_experimental")]
use crate::backend::native::NativeBackendError;
#[cfg(feature = "v2_experimental")]
use memmap2::MmapMut;
pub struct IOBackendManager;
impl IOBackendManager {
#[allow(unused_variables)] pub fn route_read_bytes(
file: &mut std::fs::File,
buffer: &mut [u8],
offset: u64,
write_buffer: &mut WriteBuffer,
#[cfg(feature = "v2_experimental")] mmap: Option<&MmapMut>,
io_mode: IOMode,
) -> NativeResult<()> {
#[cfg(all(feature = "v2_experimental", feature = "v2_io_exclusive_mmap"))]
{
if io_mode.is_exclusive_mmap() {
return Self::read_bytes_mmap_exclusive(mmap, buffer, offset);
}
}
#[cfg(all(feature = "v2_experimental", feature = "v2_io_exclusive_std"))]
{
if io_mode.is_exclusive_std() {
return Self::read_bytes_std_exclusive(file, buffer, offset, write_buffer);
}
}
Self::read_bytes_std(file, buffer, offset)
}
#[allow(unused_variables)] pub fn route_write_bytes(
file: &mut std::fs::File,
data: &[u8],
offset: u64,
write_buffer: &mut WriteBuffer,
#[cfg(feature = "v2_experimental")] mmap: Option<&mut MmapMut>,
io_mode: IOMode,
) -> NativeResult<()> {
#[cfg(all(feature = "v2_experimental", feature = "v2_io_exclusive_mmap"))]
{
if io_mode.is_exclusive_mmap() {
return Self::write_bytes_mmap_exclusive(mmap, data, offset);
}
}
#[cfg(all(feature = "v2_experimental", feature = "v2_io_exclusive_std"))]
{
if io_mode.is_exclusive_std() {
return Self::write_bytes_std_exclusive(file, data, offset, write_buffer);
}
}
Self::write_bytes_std(file, data, offset)
}
#[allow(unused_variables)] pub fn route_buffered_write_bytes(
file: &mut std::fs::File,
data: &[u8],
offset: u64,
write_buffer: &mut WriteBuffer,
#[cfg(feature = "v2_experimental")] mmap: Option<&mut MmapMut>,
io_mode: IOMode,
) -> NativeResult<()> {
#[cfg(all(feature = "v2_experimental", feature = "v2_io_exclusive_mmap"))]
{
if io_mode.is_exclusive_mmap() {
let end_offset = offset + data.len() as u64;
return Self::write_buffered_bytes_mmap_exclusive(mmap, data, offset, end_offset);
}
}
#[cfg(all(feature = "v2_experimental", feature = "v2_io_exclusive_std"))]
{
if io_mode.is_exclusive_std() {
return Self::write_buffered_bytes_std_exclusive(file, data, offset, write_buffer);
}
}
Self::write_buffered_bytes_std(file, data, offset, write_buffer)
}
#[cfg(feature = "v2_experimental")]
pub fn is_mmap_mode_available(io_mode: IOMode) -> bool {
io_mode.is_exclusive_mmap()
}
#[cfg(feature = "v2_experimental")]
pub fn is_exclusive_std_mode_available(io_mode: IOMode) -> bool {
io_mode.is_exclusive_std()
}
pub fn get_backend_description(io_mode: IOMode) -> &'static str {
match io_mode {
#[cfg(feature = "v2_experimental")]
IOMode::ExclusiveMmap => "Exclusive Memory-Mapped I/O",
#[cfg(feature = "v2_experimental")]
IOMode::ExclusiveStd => "Exclusive Standard I/O",
_ => "Default Mixed I/O",
}
}
#[cfg(all(feature = "v2_experimental", feature = "v2_io_exclusive_mmap"))]
fn read_bytes_mmap_exclusive(
mmap: Option<&MmapMut>,
buffer: &mut [u8],
offset: u64,
) -> NativeResult<()> {
let mmap = mmap.ok_or(NativeBackendError::CorruptNodeRecord {
node_id: -1,
reason: "mmap not initialized in exclusive mmap mode".to_string(),
})?;
if offset as usize + buffer.len() > mmap.len() {
return Err(NativeBackendError::CorruptNodeRecord {
node_id: -1,
reason: format!(
"Read beyond mmap region: offset={}, len={}, mmap_size={}",
offset,
buffer.len(),
mmap.len()
),
});
}
let start = offset as usize;
let end = start + buffer.len();
buffer.copy_from_slice(&mmap[start..end]);
Ok(())
}
#[cfg(all(feature = "v2_experimental", feature = "v2_io_exclusive_std"))]
fn read_bytes_std_exclusive(
file: &mut std::fs::File,
buffer: &mut [u8],
offset: u64,
write_buffer: &mut WriteBuffer,
) -> NativeResult<()> {
use std::io::{Read, Seek, SeekFrom};
if !write_buffer.operations.is_empty() {
let ops_count = write_buffer.operations.len();
if std::env::var("WRITEBUF_DEBUG").is_ok() {
println!(
"[WRITEBUF_DEBUG] EXCLUSIVE_STD: CLEARING {} pending ops without flush",
ops_count
);
}
write_buffer.operations.clear();
}
file.seek(SeekFrom::Start(offset))?;
file.read_exact(buffer)?;
Ok(())
}
fn read_bytes_std(
file: &mut std::fs::File,
buffer: &mut [u8],
offset: u64,
) -> NativeResult<()> {
use std::io::{Read, Seek, SeekFrom};
file.seek(SeekFrom::Start(offset))?;
file.read_exact(buffer)?;
Ok(())
}
#[cfg(all(feature = "v2_experimental", feature = "v2_io_exclusive_mmap"))]
fn write_bytes_mmap_exclusive(
mmap: Option<&mut MmapMut>,
data: &[u8],
offset: u64,
) -> NativeResult<()> {
let end_offset = offset + data.len() as u64;
let mmap = mmap.ok_or(NativeBackendError::CorruptNodeRecord {
node_id: -1,
reason: "mmap not initialized in exclusive mmap mode".to_string(),
})?;
if offset as usize + data.len() > mmap.len() {
return Err(NativeBackendError::CorruptNodeRecord {
node_id: -1,
reason: format!(
"mmap write out of bounds: offset={}, len={}, mmap_len={}",
offset,
data.len(),
mmap.len()
),
});
}
mmap[offset as usize..offset as usize + data.len()].copy_from_slice(data);
mmap.flush()?;
Ok(())
}
#[cfg(all(feature = "v2_experimental", feature = "v2_io_exclusive_std"))]
fn write_bytes_std_exclusive(
file: &mut std::fs::File,
data: &[u8],
offset: u64,
write_buffer: &mut WriteBuffer,
) -> NativeResult<()> {
use std::io::{Seek, SeekFrom, Write};
if !write_buffer.operations.is_empty() {
let ops_count = write_buffer.operations.len();
if std::env::var("WRITEBUF_DEBUG").is_ok() {
println!(
"[WRITEBUF_DEBUG] EXCLUSIVE_STD: CLEARING {} pending ops without flush",
ops_count
);
}
write_buffer.operations.clear();
}
file.seek(SeekFrom::Start(offset))?;
file.write_all(data)?;
Ok(())
}
fn write_bytes_std(file: &mut std::fs::File, data: &[u8], offset: u64) -> NativeResult<()> {
use std::io::{Seek, SeekFrom, Write};
file.seek(SeekFrom::Start(offset))?;
file.write_all(data)?;
Ok(())
}
#[cfg(all(feature = "v2_experimental", feature = "v2_io_exclusive_mmap"))]
fn write_buffered_bytes_mmap_exclusive(
mmap: Option<&mut MmapMut>,
data: &[u8],
offset: u64,
end_offset: u64,
) -> NativeResult<()> {
let mmap = mmap.ok_or(NativeBackendError::CorruptNodeRecord {
node_id: -1,
reason: "mmap not initialized in exclusive mmap mode".to_string(),
})?;
if end_offset as usize > mmap.len() {
return Err(NativeBackendError::CorruptNodeRecord {
node_id: -1,
reason: format!(
"Write beyond mmap region: offset={}, len={}, mmap_size={}",
offset,
data.len(),
mmap.len()
),
});
}
let start = offset as usize;
let end = start + data.len();
mmap[start..end].copy_from_slice(data);
mmap.flush()?;
Ok(())
}
#[cfg(all(feature = "v2_experimental", feature = "v2_io_exclusive_std"))]
fn write_buffered_bytes_std_exclusive(
file: &mut std::fs::File,
data: &[u8],
offset: u64,
write_buffer: &mut WriteBuffer,
) -> NativeResult<()> {
use std::io::{Seek, SeekFrom, Write};
if !write_buffer.operations.is_empty() {
let ops_count = write_buffer.operations.len();
if std::env::var("WRITEBUF_DEBUG").is_ok() {
println!(
"[WRITEBUF_DEBUG] EXCLUSIVE_STD: CLEARING {} pending ops without flush",
ops_count
);
}
write_buffer.operations.clear();
}
file.seek(SeekFrom::Start(offset))?;
file.write_all(data)?;
Ok(())
}
fn write_buffered_bytes_std(
file: &mut std::fs::File,
data: &[u8],
offset: u64,
write_buffer: &mut WriteBuffer,
) -> NativeResult<()> {
use std::io::{Seek, SeekFrom, Write};
let data_vec = data.to_vec();
let added = write_buffer.add(offset, data_vec);
if !added {
let operations = write_buffer.flush();
for (op_offset, op_data) in operations {
file.seek(SeekFrom::Start(op_offset))?;
file.write_all(&op_data)?;
}
file.seek(SeekFrom::Start(offset))?;
file.write_all(data)?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct IOBackendStatistics {
pub backend_type: String,
pub is_exclusive_mmap: bool,
pub is_exclusive_std: bool,
pub is_default_mode: bool,
}
impl IOBackendStatistics {
pub fn new(io_mode: IOMode) -> Self {
Self {
backend_type: IOBackendManager::get_backend_description(io_mode).to_string(),
is_exclusive_mmap: io_mode.is_exclusive_mmap(),
is_exclusive_std: io_mode.is_exclusive_std(),
is_default_mode: io_mode.is_default(),
}
}
pub fn get_backend_type(&self) -> &str {
&self.backend_type
}
pub fn is_high_performance(&self) -> bool {
self.is_exclusive_mmap
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempfile;
#[test]
fn test_backend_description() {
let mode = IOMode::current();
let description = IOBackendManager::get_backend_description(mode);
assert!(!description.is_empty());
}
#[test]
fn test_io_backend_statistics() {
let mode = IOMode::current();
let stats = IOBackendStatistics::new(mode);
assert!(!stats.backend_type.is_empty());
assert_eq!(stats.is_default_mode, mode.is_default());
}
#[test]
fn test_standard_read_write() {
let mut temp_file = tempfile().unwrap();
let test_data = b"Hello, I/O Backend!";
IOBackendManager::route_write_bytes(
&mut temp_file,
test_data,
0,
&mut WriteBuffer::new(10),
#[cfg(feature = "v2_experimental")]
None,
IOMode::Default,
)
.unwrap();
let mut buffer = vec![0u8; test_data.len()];
IOBackendManager::route_read_bytes(
&mut temp_file,
&mut buffer,
0,
&mut WriteBuffer::new(10),
#[cfg(feature = "v2_experimental")]
None,
IOMode::Default,
)
.unwrap();
assert_eq!(buffer, test_data);
}
#[test]
fn test_buffered_write() {
let mut temp_file = tempfile().unwrap();
let mut write_buffer = WriteBuffer::new(10);
let test_data = b"Buffered write test";
IOBackendManager::route_buffered_write_bytes(
&mut temp_file,
test_data,
0,
&mut write_buffer,
#[cfg(feature = "v2_experimental")]
None,
IOMode::Default,
)
.unwrap();
let operations = write_buffer.flush();
for (offset, data) in operations {
use std::io::{Seek, SeekFrom, Write};
temp_file.seek(SeekFrom::Start(offset)).unwrap();
temp_file.write_all(&data).unwrap();
}
let mut buffer = vec![0u8; test_data.len()];
use std::io::{Read, Seek, SeekFrom};
temp_file.seek(SeekFrom::Start(0)).unwrap();
temp_file.read_exact(&mut buffer).unwrap();
assert_eq!(buffer, test_data);
}
#[cfg(all(feature = "v2_experimental", feature = "v2_io_exclusive_std"))]
#[test]
fn test_exclusive_std_mode() {
let mode = IOMode::ExclusiveStd;
assert!(IOBackendManager::is_exclusive_std_mode_available(mode));
assert!(!IOBackendManager::is_mmap_mode_available(mode));
assert!(mode.is_exclusive_std());
}
#[test]
fn test_io_mode_properties() {
let default_mode = IOMode::Default;
assert!(default_mode.is_default());
assert!(!default_mode.is_exclusive_mmap());
assert!(!default_mode.is_exclusive_std());
}
}