1use std::cmp::min;
6use std::fs::File;
7use std::io::{self, Seek, SeekFrom, Write};
8
9use fallocate;
10use FallocateMode;
11
12pub trait PunchHole {
14 fn punch_hole(&mut self, offset: u64, length: u64) -> io::Result<()>;
16}
17
18impl PunchHole for File {
19 fn punch_hole(&mut self, offset: u64, length: u64) -> io::Result<()> {
20 fallocate(self, FallocateMode::PunchHole, true, offset, length as u64)
21 .map_err(|e| io::Error::from_raw_os_error(e.errno()))
22 }
23}
24
25pub trait WriteZeroes {
27 fn write_zeroes(&mut self, length: usize) -> io::Result<usize>;
29}
30
31impl<T: PunchHole + Seek + Write> WriteZeroes for T {
32 fn write_zeroes(&mut self, length: usize) -> io::Result<usize> {
33 let offset = self.seek(SeekFrom::Current(0))?;
35 match self.punch_hole(offset, length as u64) {
36 Ok(()) => {
37 self.seek(SeekFrom::Current(length as i64))?;
39 return Ok(length);
40 }
41 Err(_) => {} }
43
44 let buf_size = min(length, 0x10000);
47 let buf = vec![0u8; buf_size];
48 let mut nwritten: usize = 0;
49 while nwritten < length {
50 let remaining = length - nwritten;
51 let write_size = min(remaining, buf_size);
52 nwritten += self.write(&buf[0..write_size])?;
53 }
54 Ok(length)
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61 use std::fs::OpenOptions;
62 use std::io::{Read, Seek, SeekFrom};
63 use std::path::PathBuf;
64
65 use TempDir;
66
67 #[test]
68 fn simple_test() {
69 let tempdir = TempDir::new("/tmp/write_zeroes_test").unwrap();
70 let mut path = PathBuf::from(tempdir.as_path().unwrap());
71 path.push("file");
72 let mut f = OpenOptions::new()
73 .read(true)
74 .write(true)
75 .create(true)
76 .open(&path)
77 .unwrap();
78 f.set_len(16384).unwrap();
79
80 let orig_data = [0x55u8; 5678];
82 f.seek(SeekFrom::Start(1234)).unwrap();
83 f.write(&orig_data).unwrap();
84
85 let mut readback = [0u8; 16384];
87 f.seek(SeekFrom::Start(0)).unwrap();
88 f.read(&mut readback).unwrap();
89 for read in readback[0..1234].iter() {
91 assert_eq!(*read, 0);
92 }
93 for read in readback[1234..(1234 + 5678)].iter() {
95 assert_eq!(*read, 0x55);
96 }
97 for read in readback[(1234 + 5678)..].iter() {
99 assert_eq!(*read, 0);
100 }
101
102 f.seek(SeekFrom::Start(2345)).unwrap();
104 f.write_zeroes(4321).expect("write_zeroes failed");
105 assert_eq!(f.seek(SeekFrom::Current(0)).unwrap(), 2345 + 4321);
107
108 f.seek(SeekFrom::Start(0)).unwrap();
110 f.read(&mut readback).unwrap();
111 for read in readback[0..1234].iter() {
113 assert_eq!(*read, 0);
114 }
115 for read in readback[1234..2345].iter() {
117 assert_eq!(*read, 0x55);
118 }
119 for read in readback[2345..(2345 + 4321)].iter() {
121 assert_eq!(*read, 0);
122 }
123 for read in readback[(2345 + 4321)..(1234 + 5678)].iter() {
125 assert_eq!(*read, 0x55);
126 }
127 for read in readback[(1234 + 5678)..].iter() {
129 assert_eq!(*read, 0);
130 }
131 }
132
133 #[test]
134 fn large_write_zeroes() {
135 let tempdir = TempDir::new("/tmp/write_zeroes_test").unwrap();
136 let mut path = PathBuf::from(tempdir.as_path().unwrap());
137 path.push("file");
138 let mut f = OpenOptions::new()
139 .read(true)
140 .write(true)
141 .create(true)
142 .open(&path)
143 .unwrap();
144 f.set_len(16384).unwrap();
145
146 let orig_data = [0x55u8; 0x20000];
148 f.seek(SeekFrom::Start(0)).unwrap();
149 f.write(&orig_data).unwrap();
150
151 f.seek(SeekFrom::Start(0)).unwrap();
153 f.write_zeroes(0x10001).expect("write_zeroes failed");
154 assert_eq!(f.seek(SeekFrom::Current(0)).unwrap(), 0x10001);
156
157 let mut readback = [0u8; 0x20000];
159 f.seek(SeekFrom::Start(0)).unwrap();
160 f.read(&mut readback).unwrap();
161 for read in readback[0..0x10001].iter() {
163 assert_eq!(*read, 0);
164 }
165 for read in readback[0x10001..0x20000].iter() {
167 assert_eq!(*read, 0x55);
168 }
169 }
170}