use std::fs::File;
use std::io::{self, Read, Seek, SeekFrom, Write};
#[cfg(unix)]
use std::os::unix::fs::FileExt;
#[cfg(unix)]
use std::os::unix::io::AsRawFd;
#[cfg(unix)]
use std::os::unix::io::RawFd;
#[cfg(windows)]
use std::os::windows::fs::FileExt as WindowsFileExt;
#[cfg(windows)]
use std::os::windows::io::AsRawHandle;
#[cfg(windows)]
use windows_sys::Win32::Foundation::HANDLE;
#[cfg(windows)]
use windows_sys::Win32::Storage::FileSystem::{
LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY, LockFileEx, ReadFile,
};
#[cfg(windows)]
use windows_sys::Win32::System::IO::OVERLAPPED;
pub(crate) const HEADER_SIZE: u64 = 32;
#[cfg(any(feature = "set", feature = "atomic"))]
pub(crate) const ATOMIC_BLOCK: u64 = 256;
const MOVE_CHUNK: u64 = 4 * 1024;
#[cfg(unix)]
pub(crate) fn pread_exact(file: &File, offset: u64, len: usize) -> io::Result<Vec<u8>> {
let mut buf = vec![0u8; len];
file.read_exact_at(&mut buf, offset)?;
Ok(buf)
}
#[cfg(windows)]
pub(crate) fn pread_exact(file: &File, offset: u64, len: usize) -> io::Result<Vec<u8>> {
let mut buf = vec![0u8; len];
let mut filled = 0usize;
while filled < len {
let n = file.seek_read(&mut buf[filled..], offset + filled as u64)?;
if n == 0 {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"pread_exact: unexpected EOF",
));
}
filled += n;
}
Ok(buf)
}
#[cfg(unix)]
pub(crate) fn pread_exact_into(file: &File, offset: u64, buf: &mut [u8]) -> io::Result<()> {
file.read_exact_at(buf, offset)
}
#[cfg(windows)]
pub(crate) fn pread_exact_into(file: &File, offset: u64, buf: &mut [u8]) -> io::Result<()> {
let len = buf.len();
let mut filled = 0usize;
while filled < len {
let n = file.seek_read(&mut buf[filled..], offset + filled as u64)?;
if n == 0 {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"pread_exact_into: unexpected EOF",
));
}
filled += n;
}
Ok(())
}
#[cfg(unix)]
pub(crate) fn pread_exact_raw(fd: RawFd, offset: u64, buf: &mut [u8]) -> io::Result<()> {
let mut filled = 0usize;
while filled < buf.len() {
let n = unsafe {
libc::pread(
fd,
buf[filled..].as_mut_ptr() as *mut libc::c_void,
buf.len() - filled,
(offset + filled as u64) as libc::off_t,
)
};
if n < 0 {
return Err(io::Error::last_os_error());
}
if n == 0 {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"locked pread: unexpected EOF",
));
}
filled += n as usize;
}
Ok(())
}
#[cfg(windows)]
pub(crate) fn pread_exact_raw_handle(handle: isize, offset: u64, buf: &mut [u8]) -> io::Result<()> {
let handle = handle as HANDLE;
let mut filled = 0usize;
let len = buf.len();
while filled < len {
let current_offset = offset + filled as u64;
let mut overlapped: OVERLAPPED = unsafe { std::mem::zeroed() };
overlapped.Anonymous.Anonymous.Offset = current_offset as u32;
overlapped.Anonymous.Anonymous.OffsetHigh = (current_offset >> 32) as u32;
let mut bytes_read: u32 = 0;
let ret = unsafe {
ReadFile(
handle,
buf[filled..].as_mut_ptr(),
(len - filled) as u32,
&mut bytes_read,
&mut overlapped,
)
};
if ret == 0 {
return Err(io::Error::last_os_error());
}
if bytes_read == 0 {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"locked ReadFile: unexpected EOF",
));
}
filled += bytes_read as usize;
}
Ok(())
}
pub(crate) fn durable_sync(file: &File) -> io::Result<()> {
#[cfg(target_os = "macos")]
{
let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_FULLFSYNC) };
if ret != -1 {
return Ok(());
}
}
file.sync_data()
}
#[cfg(unix)]
pub(crate) fn flock_exclusive(file: &File) -> io::Result<()> {
let ret = unsafe { libc::flock(file.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) };
if ret == 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
#[cfg(windows)]
pub(crate) fn lock_file_exclusive(file: &File) -> io::Result<()> {
let handle = file.as_raw_handle() as windows_sys::Win32::Foundation::HANDLE;
let mut overlapped: OVERLAPPED = unsafe { std::mem::zeroed() };
let ret = unsafe {
LockFileEx(
handle,
LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY,
0, u32::MAX, u32::MAX, &mut overlapped,
)
};
if ret != 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
#[cfg(any(feature = "set", feature = "atomic"))]
pub(crate) fn is_atomic_write(offset: u64, len: u64) -> bool {
if len == 0 {
return true;
}
let Some(start) = HEADER_SIZE.checked_add(offset) else {
return false;
};
let Some(last) = start.checked_add(len - 1) else {
return false;
};
start / ATOMIC_BLOCK == last / ATOMIC_BLOCK
}
pub(crate) fn read_at(file: &mut File, offset: u64, buf: &mut [u8]) -> io::Result<()> {
file.seek(SeekFrom::Start(HEADER_SIZE + offset))?;
file.read_exact(buf)
}
pub(crate) fn write_at(file: &mut File, offset: u64, data: &[u8]) -> io::Result<()> {
file.seek(SeekFrom::Start(HEADER_SIZE + offset))?;
file.write_all(data)
}
pub(crate) fn move_chunked(file: &mut File, src: u64, dst: u64, n: u64) -> io::Result<()> {
let cap = n.min(MOVE_CHUNK) as usize;
let mut buf = vec![0u8; cap];
let mut done = 0u64;
while done < n {
let take = ((n - done) as usize).min(cap);
read_at(file, src + done, &mut buf[..take])?;
write_at(file, dst + done, &buf[..take])?;
done += take as u64;
}
Ok(())
}
pub(crate) fn write_committed_len(file: &mut File, clen: &mut u64, len: u64) -> io::Result<()> {
file.seek(SeekFrom::Start(8))?;
file.write_all(&len.to_le_bytes())?;
*clen = len;
Ok(())
}
#[repr(u64)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) enum WipAux {
Set = 0,
SpliceGrow = u64::MAX - 1,
SpliceShrink = u64::MAX - 2,
Repeat = u64::MAX - 3,
Copy = u64::MAX - 4,
MultiWrite = u64::MAX - 5,
}
impl From<WipAux> for u64 {
#[inline]
fn from(aux: WipAux) -> Self {
aux as u64
}
}
impl TryFrom<u64> for WipAux {
type Error = ();
#[inline]
fn try_from(value: u64) -> Result<Self, Self::Error> {
match value {
v if v == WipAux::Set as u64 => Ok(WipAux::Set),
v if v == WipAux::SpliceGrow as u64 => Ok(WipAux::SpliceGrow),
v if v == WipAux::SpliceShrink as u64 => Ok(WipAux::SpliceShrink),
v if v == WipAux::Repeat as u64 => Ok(WipAux::Repeat),
v if v == WipAux::Copy as u64 => Ok(WipAux::Copy),
v if v == WipAux::MultiWrite as u64 => Ok(WipAux::MultiWrite),
_ => Err(()),
}
}
}
#[cfg(any(feature = "set", feature = "atomic"))]
pub(crate) fn write_wip(file: &mut File, wip_ptr: u64, wip_aux: WipAux) -> io::Result<()> {
let mut buf = [0u8; 16];
buf[0..8].copy_from_slice(&wip_ptr.to_le_bytes());
buf[8..16].copy_from_slice(&u64::from(wip_aux).to_le_bytes());
file.seek(SeekFrom::Start(16))?;
file.write_all(&buf)
}
pub(crate) fn write_header_commit(
file: &mut File,
clen: u64,
wip_ptr: u64,
wip_aux: WipAux,
) -> io::Result<()> {
let mut buf = [0u8; 24];
buf[0..8].copy_from_slice(&clen.to_le_bytes());
buf[8..16].copy_from_slice(&wip_ptr.to_le_bytes());
buf[16..24].copy_from_slice(&u64::from(wip_aux).to_le_bytes());
file.seek(SeekFrom::Start(8))?;
file.write_all(&buf)
}
pub(crate) fn recover_wip(
file: &mut File,
committed_len: u64,
wip_ptr: u64,
wip_aux: u64,
raw_size: u64,
) -> io::Result<u64> {
let tail_start = HEADER_SIZE + committed_len;
let tail_len = raw_size.saturating_sub(tail_start);
let mut final_clen = committed_len;
match WipAux::try_from(wip_aux) {
Ok(WipAux::Set) => {
if tail_len > 0
&& wip_ptr >= HEADER_SIZE
&& wip_ptr.saturating_add(tail_len) <= tail_start
{
move_chunked(file, committed_len, wip_ptr - HEADER_SIZE, tail_len)?;
durable_sync(file)?;
}
}
Ok(WipAux::Repeat) => {
if tail_len >= 8 {
let mut kbuf = [0u8; 8];
file.seek(SeekFrom::Start(tail_start))?;
file.read_exact(&mut kbuf)?;
let k = u64::from_le_bytes(kbuf);
let s_len = tail_len - 8;
let total = k.saturating_mul(s_len);
if s_len > 0
&& wip_ptr >= HEADER_SIZE
&& wip_ptr.saturating_add(total) <= tail_start
&& let Ok(s_len_usize) = usize::try_from(s_len)
{
let mut s = vec![0u8; s_len_usize];
file.read_exact(&mut s)?;
write_repeated(file, wip_ptr, &s, k)?;
durable_sync(file)?;
}
}
}
Ok(WipAux::Copy) => {
if tail_len >= 16 && wip_ptr >= HEADER_SIZE {
let mut meta = [0u8; 16];
file.seek(SeekFrom::Start(tail_start))?;
file.read_exact(&mut meta)?;
let src = u64::from_le_bytes(meta[0..8].try_into().unwrap());
let n = u64::from_le_bytes(meta[8..16].try_into().unwrap());
let dst = wip_ptr - HEADER_SIZE;
let disjoint = dst >= src.saturating_add(n) || src >= dst.saturating_add(n);
if n > 0
&& src.saturating_add(n) <= committed_len
&& dst.saturating_add(n) <= committed_len
&& disjoint
{
move_chunked(file, src, dst, n)?;
durable_sync(file)?;
}
}
}
Ok(dir @ (WipAux::SpliceGrow | WipAux::SpliceShrink)) => {
let grow = matches!(dir, WipAux::SpliceGrow);
if wip_ptr >= HEADER_SIZE {
let a = wip_ptr - HEADER_SIZE;
let payload_end = raw_size - HEADER_SIZE;
let clen_new = if grow {
payload_end.checked_add(a).map(|x| x / 2)
} else {
payload_end
.checked_add(a)
.and_then(|x| x.checked_sub(committed_len))
};
if let Some(clen_new) = clen_new {
let s = committed_len.max(clen_new);
let m = clen_new.saturating_sub(a);
let dir_ok = if grow {
clen_new > committed_len
} else {
clen_new < committed_len
};
if a <= committed_len
&& clen_new >= a
&& dir_ok
&& s.saturating_add(m) == payload_end
{
move_chunked(file, s, a, m)?;
durable_sync(file)?;
final_clen = clen_new;
}
}
}
}
Ok(WipAux::MultiWrite) => {}
Err(()) => {}
}
write_header_commit(file, final_clen, 0, WipAux::Set)?;
durable_sync(file)?;
file.set_len(HEADER_SIZE + final_clen)?;
durable_sync(file)?;
Ok(final_clen)
}
#[cfg(any(feature = "set", feature = "atomic"))]
pub(crate) fn journaled_set(
file: &mut File,
data_size: u64,
offset: u64,
data: &[u8],
) -> io::Result<()> {
write_at(file, data_size, data)?;
durable_sync(file)?;
write_wip(file, HEADER_SIZE + offset, WipAux::Set)?;
durable_sync(file)?;
write_at(file, offset, data)?;
durable_sync(file)?;
write_wip(file, 0, WipAux::Set)?;
durable_sync(file)?;
file.set_len(HEADER_SIZE + data_size)
}
#[cfg(any(feature = "set", feature = "atomic"))]
#[inline]
pub(crate) fn set_in_place(
file: &mut File,
data_size: u64,
offset: u64,
data: &[u8],
) -> io::Result<()> {
if is_atomic_write(offset, data.len() as u64) {
write_at(file, offset, data)?;
durable_sync(file)
} else {
journaled_set(file, data_size, offset, data)
}
}
#[cfg(feature = "set")]
pub(crate) fn journaled_repeat(
file: &mut File,
data_size: u64,
offset: u64,
s: &[u8],
k: u64,
) -> io::Result<()> {
let tail = HEADER_SIZE + data_size;
file.seek(SeekFrom::Start(tail))?;
file.write_all(&k.to_le_bytes())?;
file.write_all(s)?;
durable_sync(file)?;
write_wip(file, HEADER_SIZE + offset, WipAux::Repeat)?;
durable_sync(file)?;
write_repeated(file, HEADER_SIZE + offset, s, k)?;
durable_sync(file)?;
write_wip(file, 0, WipAux::Set)?;
durable_sync(file)?;
file.set_len(HEADER_SIZE + data_size)
}
pub(crate) fn write_repeated(file: &mut File, phys: u64, s: &[u8], k: u64) -> io::Result<()> {
let unit = s.len() as u64;
if unit == 0 || k == 0 {
return Ok(());
}
let total = k.checked_mul(unit).ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
"write_repeated: length overflow",
)
})?;
let copies = (MOVE_CHUNK / unit).max(1).min(k);
let mut buf = Vec::with_capacity((copies * unit) as usize);
for _ in 0..copies {
buf.extend_from_slice(s);
}
let mut done = 0u64;
while done < total {
let take = ((total - done) as usize).min(buf.len());
file.seek(SeekFrom::Start(phys + done))?;
file.write_all(&buf[..take])?;
done += take as u64;
}
Ok(())
}
#[cfg(feature = "set")]
#[inline]
pub(crate) fn repeat_fill(
file: &mut File,
data_size: u64,
offset: u64,
s: &[u8],
k: u64,
) -> io::Result<()> {
let total = k.saturating_mul(s.len() as u64);
if is_atomic_write(offset, total) {
write_repeated(file, HEADER_SIZE + offset, s, k)?;
durable_sync(file)
} else {
journaled_repeat(file, data_size, offset, s, k)
}
}
#[cfg(all(feature = "set", feature = "atomic"))]
pub(crate) fn journaled_exchange(
file: &mut File,
data_size: u64,
a: u64,
b: u64,
n: u64,
) -> io::Result<()> {
move_chunked(file, a, data_size, n)?;
durable_sync(file)?;
write_wip(file, HEADER_SIZE + a, WipAux::Set)?;
durable_sync(file)?;
move_chunked(file, b, a, n)?;
durable_sync(file)?;
write_wip(file, HEADER_SIZE + b, WipAux::Set)?;
durable_sync(file)?;
move_chunked(file, data_size, b, n)?;
durable_sync(file)?;
write_wip(file, 0, WipAux::Set)?;
durable_sync(file)?;
file.set_len(HEADER_SIZE + data_size)
}
#[cfg(all(feature = "set", feature = "atomic"))]
pub(crate) fn journaled_move(
file: &mut File,
data_size: u64,
src: u64,
dst: u64,
n: u64,
) -> io::Result<()> {
move_chunked(file, src, data_size, n)?;
durable_sync(file)?;
write_wip(file, HEADER_SIZE + dst, WipAux::Set)?;
durable_sync(file)?;
move_chunked(file, data_size, dst, n)?;
durable_sync(file)?;
write_wip(file, 0, WipAux::Set)?;
durable_sync(file)?;
file.set_len(HEADER_SIZE + data_size)
}
#[cfg(all(feature = "set", feature = "atomic"))]
pub(crate) fn journaled_copy(
file: &mut File,
data_size: u64,
src: u64,
dst: u64,
n: u64,
) -> io::Result<()> {
let mut meta = [0u8; 16];
meta[0..8].copy_from_slice(&src.to_le_bytes());
meta[8..16].copy_from_slice(&n.to_le_bytes());
write_at(file, data_size, &meta)?;
durable_sync(file)?;
write_wip(file, HEADER_SIZE + dst, WipAux::Copy)?;
durable_sync(file)?;
move_chunked(file, src, dst, n)?;
durable_sync(file)?;
write_wip(file, 0, WipAux::Set)?;
durable_sync(file)?;
file.set_len(HEADER_SIZE + data_size)
}
#[inline]
pub(crate) fn commit_grow(
file: &mut File,
clen: &mut u64,
new_len: u64,
old_len: u64,
file_end: u64,
) -> io::Result<()> {
if let Err(e) = write_committed_len(file, clen, new_len).and_then(|_| durable_sync(file)) {
let _ = file.set_len(file_end);
*clen = old_len;
let _ = write_committed_len(file, clen, old_len);
let _ = durable_sync(file);
return Err(e);
}
Ok(())
}
#[inline]
pub(crate) fn commit_shrink(file: &mut File, clen: &mut u64, new_len: u64) -> io::Result<()> {
file.set_len(HEADER_SIZE + new_len)?;
*clen = new_len;
write_committed_len(file, clen, new_len)?;
durable_sync(file)
}
#[cfg(feature = "atomic")]
pub(crate) fn journaled_splice(
file: &mut File,
clen: &mut u64,
n_old: u64,
dn: &[u8],
) -> io::Result<()> {
let old_clen = *clen;
let a = old_clen - n_old; let m = dn.len() as u64;
let clen_new = a + m; let s = old_clen.max(clen_new);
file.set_len(HEADER_SIZE + s + m)?;
write_at(file, s, dn)?;
durable_sync(file)?;
let dir = if m > n_old {
WipAux::SpliceGrow
} else {
WipAux::SpliceShrink
};
write_wip(file, HEADER_SIZE + a, dir)?;
durable_sync(file)?;
move_chunked(file, s, a, m)?;
durable_sync(file)?;
write_header_commit(file, clen_new, 0, WipAux::Set)?;
*clen = clen_new;
durable_sync(file)?;
file.set_len(HEADER_SIZE + clen_new)
}
#[cfg(feature = "atomic")]
pub(crate) fn commit_tail_replace(
file: &mut File,
clen: &mut u64,
new_tail_start: u64,
n: u64,
buf: &[u8],
file_end: u64,
) -> io::Result<()> {
let m = buf.len() as u64;
let a = new_tail_start; if m == 0 {
commit_shrink(file, clen, a)
} else if n == 0 {
let final_data_len = a + m;
file.set_len(HEADER_SIZE + final_data_len)?;
if let Err(e) = write_at(file, a, buf) {
let _ = file.set_len(file_end);
return Err(e);
}
if let Err(e) = durable_sync(file) {
let _ = file.set_len(file_end);
return Err(e);
}
write_committed_len(file, clen, final_data_len)?;
durable_sync(file)
} else if m == n {
set_in_place(file, *clen, a, buf)
} else {
journaled_splice(file, clen, n, buf)
}
}
#[cfg(all(feature = "set", feature = "atomic"))]
pub(crate) fn journaled_multi_set(
file: &mut File,
data_size: u64,
blocks: &[(u64, &[u8])],
) -> io::Result<()> {
file.seek(SeekFrom::Start(HEADER_SIZE + data_size))?;
for (offset, data) in blocks {
let end = offset + data.len() as u64;
file.write_all(&offset.to_le_bytes())?;
file.write_all(&end.to_le_bytes())?;
file.write_all(data)?;
}
durable_sync(file)?;
write_wip(file, 0, WipAux::MultiWrite)?;
durable_sync(file)?;
for (offset, data) in blocks {
write_at(file, *offset, data)?;
}
durable_sync(file)?;
write_wip(file, 0, WipAux::Set)?;
durable_sync(file)?;
file.set_len(HEADER_SIZE + data_size)
}
fn walk_multi_blocks(
file: &mut File,
committed_len: u64,
tail_start: u64,
raw_size: u64,
mut apply: impl FnMut(&mut File, u64, u64, u64) -> io::Result<()>,
) -> io::Result<bool> {
let mut cursor = tail_start;
while cursor < raw_size {
if raw_size - cursor < 16 {
return Ok(false);
}
let mut hdr = [0u8; 16];
file.seek(SeekFrom::Start(cursor))?;
file.read_exact(&mut hdr)?;
let s = u64::from_le_bytes(hdr[0..8].try_into().unwrap());
let e = u64::from_le_bytes(hdr[8..16].try_into().unwrap());
if e < s || e > committed_len {
return Ok(false);
}
let plen = e - s;
let payload_phys = cursor + 16;
if payload_phys.saturating_add(plen) > raw_size {
return Ok(false);
}
apply(file, s, payload_phys - HEADER_SIZE, plen)?;
cursor = payload_phys + plen;
}
Ok(cursor == raw_size)
}
pub(crate) fn recover_multi_write(
file: &mut File,
committed_len: u64,
raw_size: u64,
) -> io::Result<u64> {
let actual_len = raw_size.saturating_sub(HEADER_SIZE);
let committed_len = committed_len.min(actual_len);
let tail_start = HEADER_SIZE + committed_len;
if raw_size > tail_start {
let valid = walk_multi_blocks(file, committed_len, tail_start, raw_size, |_, _, _, _| {
Ok(())
})?;
if valid {
walk_multi_blocks(
file,
committed_len,
tail_start,
raw_size,
|f, dst, src, n| move_chunked(f, src, dst, n),
)?;
durable_sync(file)?;
}
}
write_header_commit(file, committed_len, 0, WipAux::Set)?;
durable_sync(file)?;
file.set_len(tail_start)?;
durable_sync(file)?;
Ok(committed_len)
}
#[cfg(all(feature = "set", feature = "atomic"))]
pub(crate) fn inplace_overlay_insert<'a>(
overlay: &mut Vec<(u64, &'a [u8])>,
off: u64,
data: &'a [u8],
) {
let end = off + data.len() as u64;
let lo = overlay.partition_point(|&(s, d)| s + d.len() as u64 <= off);
let hi = overlay.partition_point(|&(s, _)| s < end);
let mut repl: Vec<(u64, &'a [u8])> = Vec::with_capacity(3);
if lo < hi {
let (s0, d0) = overlay[lo];
if s0 < off {
repl.push((s0, &d0[..(off - s0) as usize]));
}
}
repl.push((off, data));
if lo < hi {
let (s_last, d_last) = overlay[hi - 1];
let e_last = s_last + d_last.len() as u64;
if e_last > end {
repl.push((end, &d_last[(end - s_last) as usize..]));
}
}
overlay.splice(lo..hi, repl);
}
#[cfg(all(feature = "set", feature = "atomic"))]
pub(crate) fn inplace_validate_write(
offset: u64,
data: &[u8],
data_size: u64,
locked: u64,
) -> io::Result<()> {
if data.is_empty() {
return Ok(());
}
let end = crate::checked_end(
offset,
data.len() as u64,
"inplace_gen: write offset + data.len() overflows u64",
)?;
if offset < locked {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!(
"inplace_gen: write range [{offset}, {end}) overlaps locked region [0, {locked})"
),
));
}
if end > data_size {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!(
"inplace_gen: write range [{offset}, {end}) exceeds payload size ({data_size})"
),
));
}
Ok(())
}
#[cfg(all(feature = "set", feature = "atomic"))]
pub(crate) fn inplace_overlay_read(
file: &mut File,
data_size: u64,
offset: u64,
buf: &mut [u8],
overlay: &[(u64, &[u8])],
) -> io::Result<()> {
let end = crate::checked_end(
offset,
buf.len() as u64,
"inplace_gen: read offset + buf.len() overflows u64",
)?;
if end > data_size {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("inplace_gen: read range [{offset}, {end}) exceeds payload size ({data_size})"),
));
}
if buf.is_empty() {
return Ok(());
}
let start = overlay.partition_point(|&(s, d)| s + d.len() as u64 <= offset);
let mut run_end = start;
let mut covered_to = offset;
let mut has_gap = false;
for &(s, d) in &overlay[start..] {
if s >= end {
break;
}
if s > covered_to {
has_gap = true; }
covered_to = s + d.len() as u64;
run_end += 1;
}
if covered_to < end {
has_gap = true; }
if has_gap {
read_at(file, offset, buf)?;
}
for &(s, d) in &overlay[start..run_end] {
let e = s + d.len() as u64;
let lo = s.max(offset);
let hi = e.min(end);
let (b_lo, b_hi) = ((lo - offset) as usize, (hi - offset) as usize);
let (d_lo, d_hi) = ((lo - s) as usize, (hi - s) as usize);
buf[b_lo..b_hi].copy_from_slice(&d[d_lo..d_hi]);
}
Ok(())
}