1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
use crate::{file::open_as_write, FileReader};
use memmap2::{Mmap, MmapMut};
use std::{fmt, fs::File, io, path::Path};
/// `FileWriter` is a structure that allows writing to a file.
/// It uses memory-mapped files for efficient file manipulation.
pub struct FileWriter<P: AsRef<Path> + Send + Sync> {
pub mmap: MmapMut,
pub path: P,
pub file: File,
}
/// Writes "FileWriter({path})" to the provided formatter.
impl<P: AsRef<Path> + Send + Sync> fmt::Display for FileWriter<P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "FileWriter({})", self.path.as_ref().display())
}
}
/// Writes "FileWriter({Path})" to the provided formatter.
impl<P: AsRef<Path> + Send + Sync> fmt::Debug for FileWriter<P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "FileWriter({})", self.path.as_ref().display())
}
}
#[cfg(feature = "filepath")]
use filepath::FilePath;
#[cfg(feature = "filepath")]
impl FileWriter<std::path::PathBuf> {
/// Opens a file and returns a `FileWriter` instance.
pub fn open_file(file: File) -> io::Result<Self> {
let path = file.path()?;
Self::new(file, path)
}
}
impl<P: AsRef<Path> + Send + Sync> FileWriter<P> {
/// Creates a new `FileWriter` instance.
/// It takes a reference to a `File` and a path, and maps the file into memory.
fn new(file: File, path: P) -> io::Result<Self> {
let mmap = unsafe { MmapMut::map_mut(&file)? };
Ok(Self { mmap, path, file })
}
/// Opens a file at the provided path and returns a `FileWriter` instance.
pub fn open_file_at_path(file: File, path: P) -> io::Result<Self> {
Self::new(file, path)
}
/// Opens a file in write mode and returns a `FileWriter` instance.
pub fn open(path: P) -> io::Result<Self> {
let file = open_as_write(path.as_ref())?;
Self::new(file, path)
}
/// Writes bytes to the file.
/// It replaces the entire content of the file with the provided bytes.
pub fn write<B: AsRef<[u8]>>(&mut self, bytes: B) -> &Self {
self.mmap[..].copy_from_slice(bytes.as_ref());
self
}
/// Writes bytes to the file at the provided offset.
pub fn write_to_offset<B: AsRef<[u8]>>(&mut self, bytes: B, offset: usize) -> &Self {
self.mmap[offset..offset + bytes.as_ref().len()].copy_from_slice(bytes.as_ref());
self
}
/// Appends bytes to the file, extending the file length if necessary.
pub fn append<B: AsRef<[u8]>>(&mut self, bytes: B) -> io::Result<&Self> {
let current_len = self.len();
let bytes = bytes.as_ref();
let new_len = current_len + bytes.len();
self.set_len(new_len)?;
self.mmap[current_len..new_len].copy_from_slice(bytes);
Ok(self)
}
/// Overwrites the entire content of the file with the provided bytes. The file's length is set
/// to the length of the provided bytes.
pub fn overwrite<B: AsRef<[u8]>>(&mut self, bytes: B) -> io::Result<&Self> {
let bytes = bytes.as_ref();
let len = bytes.len();
self.set_len(len)?;
self.write(bytes);
Ok(self)
}
/// Returns length of the file data
pub fn len(&self) -> usize {
self.mmap.len()
}
/// Returns true if the file is empty.
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Returns a mutable reference to the bytes of the file.
pub fn as_slice_mut(&mut self) -> &mut [u8] {
&mut self.mmap[..]
}
/// Returns an immutable reference to the bytes of the file.
pub fn bytes(&self) -> &[u8] {
&self.mmap[..]
}
/// Replaces a portion of the file content starting from the provided offset with the provided bytes.
pub fn replace<B: AsRef<[u8]>>(&mut self, bytes: B, offset: usize) -> &Self {
let bytes = bytes.as_ref();
self.mmap[offset..offset + bytes.len()].copy_from_slice(bytes);
self
}
#[cfg(feature = "search")]
/// Finds a sequence of bytes in the file and replaces it with another sequence of bytes. If the sequence to find is not found, it does nothing.
/// If the sequence would be written past the length of the file, the file is extended to accommodate the new bytes.
fn find_replace_inner<B: AsRef<[u8]>, BO: AsRef<[u8]>>(
&mut self,
find: B,
replace: BO,
offset: usize,
) -> io::Result<&Self> {
let find = find.as_ref();
let replace = replace.as_ref();
if replace.len() > find.len() {
let current_bytes = self.mmap[offset + find.len()..].to_vec();
self.extend_len_by(replace.len() - find.len())?;
self.mmap[offset..offset + replace.len()].copy_from_slice(replace);
self.mmap[offset + replace.len()..].copy_from_slice(¤t_bytes);
} else {
self.mmap[offset..offset + replace.len()].copy_from_slice(replace);
}
Ok(self)
}
#[cfg(feature = "search")]
/// Finds a sequence of bytes in the file and replaces it with another sequence of bytes.
/// If the sequence to find is not found, it does nothing.
pub fn find_replace<B: AsRef<[u8]>, BO: AsRef<[u8]>>(
&mut self,
find: B,
replace: BO,
) -> io::Result<&Self> {
if let Some(offset) = crate::read::find_bytes(self.bytes(), &find) {
self.find_replace_inner(&find, &replace, offset)?;
}
Ok(self)
}
#[cfg(feature = "search")]
/// Finds the last occurrence of a slice of bytes in the file and replaces it with another slice of bytes.
pub fn rfind_replace<B: AsRef<[u8]>, BO: AsRef<[u8]>>(
&mut self,
find: B,
replace: BO,
) -> io::Result<&Self> {
if let Some(offset) = crate::read::rfind_bytes(self.bytes(), &find) {
self.find_replace_inner(&find, &replace, offset)?;
}
Ok(self)
}
#[cfg(feature = "search")]
/// Finds the nth occurrence of a slice of bytes in the file, in reverse order, and replaces it with another slice of bytes.
pub fn rfind_replace_nth<B: AsRef<[u8]>, BO: AsRef<[u8]>>(
&mut self,
find: B,
replace: BO,
n: usize,
) -> io::Result<&Self> {
if let Some(offset) = crate::read::rfind_bytes_nth(self.bytes(), &find, n) {
self.find_replace_inner(&find, &replace, offset)?;
}
Ok(self)
}
#[cfg(feature = "search")]
/// Finds the nth occurrence of a slice of bytes in the file and replaces it with another slice of bytes.
/// If the slice to find is not found, no replacement occurs.
pub fn find_replace_nth<B: AsRef<[u8]>, BO: AsRef<[u8]>>(
&mut self,
find: B,
replace: BO,
n: usize,
) -> io::Result<&Self> {
if let Some(offset) = crate::read::find_bytes_nth(self.bytes(), &find, n) {
self.find_replace_inner(&find, &replace, offset)?;
}
Ok(self)
}
#[cfg(feature = "search")]
/// Finds all occurrences of a slice of bytes in the file and replaces them with another slice of bytes.
pub fn find_replace_all<B: AsRef<[u8]>, BO: AsRef<[u8]>>(
&mut self,
find: B,
replace: BO,
) -> io::Result<&Self> {
for offset in &crate::read::find_bytes_all(self.bytes(), &find) {
self.find_replace_inner(&find, &replace, offset.to_owned())?;
}
Ok(self)
}
/// Returns a newly-opened `File` object that represents the file being written to.
pub fn open_new_file(&mut self) -> io::Result<File> {
open_as_write(self.path.as_ref())
}
/// Returns the underlying file object.
pub fn file(self) -> File {
self.file
}
/// Sets the length of the file.
pub fn set_len(&mut self, len: usize) -> io::Result<&mut Self> {
self.file.set_len(len as u64)?;
self.mmap = unsafe { MmapMut::map_mut(&self.file)? };
Ok(self)
}
/// Extends the length of the file by the provided length.
pub fn extend_len_by(&mut self, len: usize) -> io::Result<&mut Self> {
let current_len = self.len();
let new_len = current_len + len;
self.set_len(new_len)?;
Ok(self)
}
/// Returns a reference to the path of the file being written to.
pub fn path(&mut self) -> &P {
&self.path
}
/// Returns a mutable reference to the memory-mapped file.
pub fn mmap_mut(&mut self) -> &mut MmapMut {
&mut self.mmap
}
/// Returns an immutable reference to the memory-mapped file.
/// This fails if the file backing the mmap was not opened as both read and write.
/// By default, unless the file was manually provided using FileWriter::open_file, the file is opened as both read and write.
pub fn mmap(self) -> io::Result<Mmap> {
self.mmap.make_read_only()
}
/// Converts the `FileWriter` into a `FileReader` by opening the file as read-only.
pub fn to_reader(self) -> io::Result<FileReader<P>> {
let file = open_as_write(self.path.as_ref())?;
FileReader::open_file_at_path(file, self.path)
}
/// Converts the `FileWriter` into a `FileReader`.
/// This fails if the file backing the mmap was not opened as both read and write.
/// By default, unless the file was manually provided using FileWriter::open_file, the file is opened as both read and write.
/// In the event that a file was opened as write-only and passed to FileWriter::open_file, use
/// FileWriter::to_reader instead
pub fn as_reader(self) -> io::Result<FileReader<P>> {
Ok(FileReader {
mmap: self.mmap.make_read_only()?,
file: self.file,
path: self.path,
read_index: 0,
})
}
}
impl<P: AsRef<Path> + Send + Sync> AsRef<[u8]> for FileWriter<P> {
fn as_ref(&self) -> &[u8] {
self.bytes()
}
}
impl<P: AsRef<Path> + Send + Sync> AsMut<[u8]> for FileWriter<P> {
fn as_mut(&mut self) -> &mut [u8] {
self.as_slice_mut()
}
}
impl<P: AsRef<Path> + Send + Sync> io::Write for FileWriter<P> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.append(buf)?;
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
self.mmap.flush()
}
}