use std::fs::{self, OpenOptions};
use core_types::{ErrorCode, ErrorDomain, RtError};
use super::config::validate_cfg;
use super::header::{HEADER_SIZE, read_header};
use super::segment_io::{init_segment_files, io_to_err, map_ro, segment_path, slot_for};
use super::{SharedMemoryBufferRef, SharedMemoryLease, SharedMemoryReader, ShmTransportConfig};
impl SharedMemoryReader {
pub fn new(cfg: ShmTransportConfig) -> Result<Self, RtError> {
validate_cfg(&cfg)?;
fs::create_dir_all(&cfg.directory)
.map_err(|err| io_to_err("create shm directory failed", err))?;
init_segment_files(&cfg)?;
Ok(Self { cfg })
}
pub fn loan(&self, reference: SharedMemoryBufferRef) -> Result<SharedMemoryLease, RtError> {
if reference.len > self.cfg.segment_payload_bytes {
return Err(RtError::new(
ErrorCode::InvalidState,
ErrorDomain::Core,
false,
"invalid shared memory reference length",
));
}
let slot = slot_for(reference.buffer_id, self.cfg.segment_count);
let segment_path = segment_path(&self.cfg, slot);
let file = OpenOptions::new()
.read(true)
.open(&segment_path)
.map_err(|err| io_to_err("open shm segment for read failed", err))?;
let mmap = map_ro(&file)?;
let (buf_id, len) = read_header(&mmap)?;
if buf_id != reference.buffer_id {
return Err(RtError::new(
ErrorCode::InvalidState,
ErrorDomain::Core,
false,
format!(
"stale shm slot content: expected buffer_id={} actual={}",
reference.buffer_id, buf_id
),
));
}
if len != reference.len {
return Err(RtError::new(
ErrorCode::InvalidState,
ErrorDomain::Core,
false,
format!(
"shm payload length mismatch: expected={} actual={}",
reference.len, len
),
));
}
let start = HEADER_SIZE + reference.offset;
let end = start + reference.len;
if end > HEADER_SIZE + self.cfg.segment_payload_bytes {
return Err(RtError::new(
ErrorCode::InvalidState,
ErrorDomain::Core,
false,
"shm reference range out of bounds",
));
}
Ok(SharedMemoryLease {
mmap,
offset: start,
len: reference.len,
})
}
}