use std::fs::{self, File, OpenOptions};
use std::io::{Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};
use core_types::{BufferId, ErrorCode, ErrorDomain, RtError};
use memmap2::{Mmap, MmapMut};
use super::config::ShmTransportConfig;
use super::header::HEADER_SIZE;
pub(super) fn init_segment_files(cfg: &ShmTransportConfig) -> Result<(), RtError> {
let file_size = HEADER_SIZE + cfg.segment_payload_bytes;
for slot in 0..cfg.segment_count {
let path = segment_path(cfg, slot);
if !path.exists() {
create_segment_file(&path, file_size)?;
continue;
}
let md =
fs::metadata(&path).map_err(|err| io_to_err("read segment metadata failed", err))?;
if md.len() != file_size as u64 {
create_segment_file(&path, file_size)?;
}
}
Ok(())
}
pub(super) fn segment_path(cfg: &ShmTransportConfig, slot: usize) -> PathBuf {
cfg.directory
.join(format!("{}-slot-{}.shm", cfg.namespace, slot))
}
pub(super) fn slot_for(id: BufferId, segment_count: usize) -> usize {
(id.0 as usize) % segment_count
}
pub(super) fn open_rw(path: &Path) -> Result<File, RtError> {
OpenOptions::new()
.read(true)
.write(true)
.open(path)
.map_err(|err| io_to_err("open segment file failed", err))
}
pub(super) fn map_mut(file: &File) -> Result<MmapMut, RtError> {
unsafe { MmapMut::map_mut(file) }.map_err(|err| io_to_err("map_mut failed", err))
}
pub(super) fn map_ro(file: &File) -> Result<Mmap, RtError> {
unsafe { Mmap::map(file) }.map_err(|err| io_to_err("map read-only failed", err))
}
fn create_segment_file(path: &Path, file_size: usize) -> Result<(), RtError> {
let mut file = OpenOptions::new()
.create(true)
.truncate(true)
.read(true)
.write(true)
.open(path)
.map_err(|err| io_to_err("create segment file failed", err))?;
file.seek(SeekFrom::Start((file_size - 1) as u64))
.map_err(|err| io_to_err("seek segment file failed", err))?;
file.write_all(&[0u8])
.map_err(|err| io_to_err("extend segment file failed", err))?;
file.flush()
.map_err(|err| io_to_err("flush segment file failed", err))?;
Ok(())
}
pub(super) fn io_to_err(prefix: &str, err: std::io::Error) -> RtError {
RtError::new(
ErrorCode::Disconnected,
ErrorDomain::Core,
true,
format!("{prefix}: {err}"),
)
}