use std::fs::File;
use std::io::{self, Seek, SeekFrom, Write};
use std::mem;
const ONE_GB: u64 = 1 << 30;
const WORD: usize = mem::size_of::<usize>();
pub const SPARSE_SEGMENT_SIZE: usize = 32 * 1024;
#[inline]
pub fn read_le32(src: &[u8]) -> u32 {
u32::from_le_bytes([src[0], src[1], src[2], src[3]])
}
#[cfg(unix)]
pub fn fwrite_sparse(
file: &mut File,
buf: &[u8],
sparse_threshold: usize,
stored_skips: u64,
sparse_mode: bool,
) -> io::Result<u64> {
if !sparse_mode {
file.write_all(buf)?;
return Ok(0);
}
let mut stored_skips = stored_skips;
if stored_skips > ONE_GB {
file.seek(SeekFrom::Current(ONE_GB as i64))?;
stored_skips -= ONE_GB;
}
let seg_size_words = (sparse_threshold / WORD).max(1);
let aligned_len = buf.len() / WORD; let mut buf_remaining = aligned_len;
let mut buf_pos = 0usize;
while buf_pos < aligned_len * WORD {
let seg_words = seg_size_words.min(buf_remaining);
buf_remaining -= seg_words;
let mut nb_zeros = 0usize;
for i in 0..seg_words {
let start = buf_pos + i * WORD;
let word = usize::from_ne_bytes(buf[start..start + WORD].try_into().unwrap());
if word != 0 {
break;
}
nb_zeros += 1;
}
stored_skips += (nb_zeros * WORD) as u64;
if nb_zeros != seg_words {
file.seek(SeekFrom::Current(stored_skips as i64))?;
stored_skips = 0;
let write_start = buf_pos + nb_zeros * WORD;
let write_len = (seg_words - nb_zeros) * WORD;
file.write_all(&buf[write_start..write_start + write_len])?;
}
buf_pos += seg_words * WORD;
}
let rest = &buf[aligned_len * WORD..];
if !rest.is_empty() {
let nb_zero_bytes = rest.iter().take_while(|&&b| b == 0).count();
stored_skips += nb_zero_bytes as u64;
if nb_zero_bytes < rest.len() {
file.seek(SeekFrom::Current(stored_skips as i64))?;
stored_skips = 0;
file.write_all(&rest[nb_zero_bytes..])?;
}
}
Ok(stored_skips)
}
#[cfg(not(unix))]
pub fn fwrite_sparse(
file: &mut File,
buf: &[u8],
_sparse_threshold: usize,
_stored_skips: u64,
_sparse_mode: bool,
) -> io::Result<u64> {
file.write_all(buf)?;
Ok(0)
}
pub fn fwrite_sparse_end(file: &mut File, stored_skips: u64) -> io::Result<()> {
if stored_skips > 0 {
file.seek(SeekFrom::Current((stored_skips - 1) as i64))?;
file.write_all(&[0u8])?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::{Read, Seek, SeekFrom};
#[test]
fn test_read_le32_zero() {
assert_eq!(read_le32(&[0, 0, 0, 0]), 0);
}
#[test]
fn test_read_le32_one() {
assert_eq!(read_le32(&[1, 0, 0, 0]), 1);
}
#[test]
fn test_read_le32_max() {
assert_eq!(read_le32(&[0xFF, 0xFF, 0xFF, 0xFF]), u32::MAX);
}
#[test]
fn test_read_le32_known_value() {
assert_eq!(read_le32(&[0x01, 0x02, 0x03, 0x04]), 0x04030201);
}
#[test]
fn test_fwrite_sparse_end_no_skips() {
let mut f = tempfile::tempfile().unwrap();
fwrite_sparse_end(&mut f, 0).unwrap();
let mut buf = Vec::new();
f.seek(SeekFrom::Start(0)).unwrap();
f.read_to_end(&mut buf).unwrap();
assert!(buf.is_empty());
}
#[test]
fn test_fwrite_sparse_end_extends_file() {
let mut f = tempfile::tempfile().unwrap();
fwrite_sparse_end(&mut f, 4).unwrap();
let len = f.seek(SeekFrom::End(0)).unwrap();
assert_eq!(len, 4);
}
#[cfg(unix)]
mod unix_tests {
use super::*;
#[test]
fn test_fwrite_sparse_plain_data() {
let mut f = tempfile::tempfile().unwrap();
let data: Vec<u8> = (1u8..=16).collect();
let skips = fwrite_sparse(&mut f, &data, SPARSE_SEGMENT_SIZE, 0, true).unwrap();
assert_eq!(skips, 0, "no trailing zeros expected");
f.seek(SeekFrom::Start(0)).unwrap();
let mut out = vec![0u8; 16];
f.read_exact(&mut out).unwrap();
assert_eq!(out, data);
}
#[test]
fn test_fwrite_sparse_all_zeros_accumulates() {
let mut f = tempfile::tempfile().unwrap();
let zeros = vec![0u8; 64];
let skips = fwrite_sparse(&mut f, &zeros, SPARSE_SEGMENT_SIZE, 0, true).unwrap();
assert_eq!(skips, 64);
let pos = f.seek(SeekFrom::Current(0)).unwrap();
assert_eq!(pos, 0);
}
#[test]
fn test_fwrite_sparse_zeros_then_data() {
let mut buf = vec![0u8; WORD]; buf.extend_from_slice(&[1u8, 2, 3, 4, 5, 6, 7, 8]);
let mut f = tempfile::tempfile().unwrap();
let skips = fwrite_sparse(&mut f, &buf, SPARSE_SEGMENT_SIZE, 0, true).unwrap();
assert_eq!(skips, 0, "non-zero data should have flushed pending skips");
let logical_pos = f.seek(SeekFrom::Current(0)).unwrap();
assert_eq!(logical_pos as usize, WORD + 8);
}
#[test]
fn test_fwrite_sparse_end_after_sparse_write() {
let mut f = tempfile::tempfile().unwrap();
let zeros = vec![0u8; 16];
let skips = fwrite_sparse(&mut f, &zeros, SPARSE_SEGMENT_SIZE, 0, true).unwrap();
fwrite_sparse_end(&mut f, skips).unwrap();
let len = f.seek(SeekFrom::End(0)).unwrap();
assert_eq!(len, 16, "file logical size should equal buffer size");
}
#[test]
fn test_fwrite_sparse_mixed_content_round_trip() {
let mut buf = Vec::new();
buf.extend_from_slice(&[0xABu8; 8]); buf.extend_from_slice(&[0u8; 16]); buf.extend_from_slice(&[0xCDu8; 8]);
let mut f = tempfile::tempfile().unwrap();
let skips = fwrite_sparse(&mut f, &buf, SPARSE_SEGMENT_SIZE, 0, true).unwrap();
fwrite_sparse_end(&mut f, skips).unwrap();
f.seek(SeekFrom::Start(0)).unwrap();
let mut out = vec![0u8; buf.len()];
f.read_exact(&mut out).unwrap();
assert_eq!(out, buf);
}
}
#[cfg(not(unix))]
mod non_unix_tests {
use super::*;
#[test]
fn test_fwrite_sparse_fallback() {
let mut f = tempfile::tempfile().unwrap();
let data = vec![0u8; 32];
let skips = fwrite_sparse(&mut f, &data, SPARSE_SEGMENT_SIZE, 0, true).unwrap();
assert_eq!(skips, 0);
let len = f.seek(SeekFrom::End(0)).unwrap();
assert_eq!(len, 32);
}
}
}