Skip to main content

parallel_processor/memory_fs/file/
writer.rs

1use crate::memory_fs::allocator::{AllocatedChunk, CHUNKS_ALLOCATOR};
2use crate::memory_fs::file::internal::{
3    FileChunk, MemoryFileInternal, MemoryFileMode, OpenMode, UnderlyingFile,
4};
5use crate::memory_fs::stats;
6use bincode::enc::write::Writer;
7use parking_lot::{RwLock, RwLockWriteGuard};
8use std::io::{Seek, SeekFrom, Write};
9use std::ops::{Deref, DerefMut};
10use std::path::{Path, PathBuf};
11use std::slice::from_raw_parts_mut;
12use std::sync::atomic::{AtomicU64, Ordering};
13use std::sync::Arc;
14
15pub struct FileWriter {
16    path: PathBuf,
17    current_buffer: RwLock<AllocatedChunk>,
18    file_length: AtomicU64,
19    file: Arc<RwLock<MemoryFileInternal>>,
20}
21
22impl FileWriter {
23    /// Creates a new file with the specified mode
24    pub fn create(path: impl AsRef<Path>, mode: MemoryFileMode) -> Self {
25        Self {
26            path: PathBuf::from(path.as_ref()),
27            current_buffer: RwLock::new(
28                CHUNKS_ALLOCATOR.request_chunk(chunk_usage!(TemporarySpace)),
29            ),
30            file_length: AtomicU64::new(0),
31            file: {
32                let file = MemoryFileInternal::create_new(path, mode);
33                file.write().open(OpenMode::Write).unwrap();
34                file
35            },
36        }
37    }
38
39    /// Returns the total length of the file
40    pub fn len(&self) -> u64 {
41        self.file_length.load(Ordering::Relaxed) + self.current_buffer.read().len() as u64
42    }
43
44    /// Overwrites bytes at the start of the file, the data field should not be longer than 128 bytes
45    pub fn write_at_start(&mut self, data: &[u8]) -> Result<(), ()> {
46        if data.len() > 128 {
47            return Err(());
48        }
49
50        unsafe {
51            let current_buffer_lock = self.current_buffer.read();
52
53            let file_read = self.file.read();
54
55            if file_read.get_chunks_count() > 0 {
56                drop(current_buffer_lock);
57                match file_read.get_chunk(0).read().deref() {
58                    FileChunk::OnDisk { .. } => {
59                        if let UnderlyingFile::WriteMode { file, .. } =
60                            file_read.get_underlying_file()
61                        {
62                            let mut disk_file_lock = file.lock();
63                            let mut disk_file = disk_file_lock.get_file();
64                            let position = disk_file.stream_position().unwrap();
65                            disk_file.seek(SeekFrom::Start(0)).unwrap();
66                            disk_file.write_all(data).unwrap();
67                            disk_file.seek(SeekFrom::Start(position)).unwrap();
68                            Ok(())
69                        } else {
70                            Err(())
71                        }
72                    }
73                    FileChunk::OnMemory { chunk } => {
74                        std::ptr::copy_nonoverlapping(
75                            data.as_ptr(),
76                            chunk.get_mut_ptr(),
77                            data.len(),
78                        );
79                        Ok(())
80                    }
81                }
82            } else {
83                std::ptr::copy_nonoverlapping(
84                    data.as_ptr(),
85                    current_buffer_lock.get_mut_ptr(),
86                    data.len(),
87                );
88                Ok(())
89            }
90        }
91    }
92
93    pub fn write_all_unsync_from_readfn(
94        &self,
95        mut read_fn: impl FnMut(&mut [u8]) -> usize,
96        length: usize,
97    ) -> u64 {
98        // Update stats
99        stats::add_files_usage(length as u64);
100
101        let buffer = self.current_buffer.read();
102
103        unsafe {
104            let remaining_slice = from_raw_parts_mut(
105                buffer.get_mut_ptr().add(buffer.len()),
106                buffer.max_len() - buffer.len(),
107            );
108            if remaining_slice.len() >= length {
109                let amount_read = read_fn(&mut remaining_slice[..length]);
110                buffer.set_len(self.len() as usize + amount_read);
111                self.file_length.load(Ordering::Relaxed) + buffer.len() as u64
112            } else {
113                drop(buffer);
114                let mut buffer = self.current_buffer.write();
115                let mut temp_vec = Vec::new();
116
117                let position = self
118                    .file_length
119                    .fetch_add(buffer.len() as u64, Ordering::SeqCst)
120                    + (buffer.len() as u64);
121
122                replace_with::replace_with_or_abort(buffer.deref_mut(), |buffer| {
123                    let new_buffer = MemoryFileInternal::reserve_space(
124                        &self.file,
125                        buffer,
126                        &mut temp_vec,
127                        length,
128                        length,
129                    );
130                    new_buffer
131                });
132
133                // Add the completely filled chunks to the file, removing the last size as the current chunk is not counted yet in the file_length
134                self.file_length
135                    .fetch_add((length - buffer.len()) as u64, Ordering::SeqCst);
136
137                let _buffer_read = RwLockWriteGuard::downgrade(buffer);
138
139                for (_lock, part) in temp_vec.drain(..) {
140                    read_fn(part);
141                }
142
143                if self.file.read().is_on_disk() {
144                    self.file.write().flush_chunks(usize::MAX);
145                }
146                position
147            }
148            // let read_fn
149        }
150    }
151
152    /// Appends atomically all the data to the file, returning the start position of the written data in the file
153    pub fn write_all_parallel(&self, data: &[u8], el_size: usize) -> u64 {
154        // Update stats
155        stats::add_files_usage(data.len() as u64);
156
157        let buffer = self.current_buffer.read();
158        if let Some(chunk_position) = buffer.write_bytes_noextend(data) {
159            self.file_length.load(Ordering::Relaxed) + chunk_position
160        } else {
161            drop(buffer);
162            let mut buffer = self.current_buffer.write();
163
164            // Check if now the buffer can be written
165            if let Some(chunk_position) = buffer.write_bytes_noextend(data) {
166                return self.file_length.load(Ordering::Relaxed) + chunk_position;
167            }
168
169            let mut temp_vec = Vec::new();
170
171            let position = self
172                .file_length
173                .fetch_add(buffer.len() as u64, Ordering::SeqCst)
174                + (buffer.len() as u64);
175
176            replace_with::replace_with_or_abort(buffer.deref_mut(), |buffer| {
177                let new_buffer = MemoryFileInternal::reserve_space(
178                    &self.file,
179                    buffer,
180                    &mut temp_vec,
181                    data.len(),
182                    el_size,
183                );
184                new_buffer
185            });
186
187            // Add the completely filled chunks to the file, removing the last size as the current chunk is not counted yet in the file_length
188            self.file_length
189                .fetch_add((data.len() - buffer.len()) as u64, Ordering::SeqCst);
190
191            let _buffer_read = RwLockWriteGuard::downgrade(buffer);
192
193            let mut offset = 0;
194            for (_lock, part) in temp_vec.drain(..) {
195                part.copy_from_slice(&data[offset..(offset + part.len())]);
196                offset += part.len();
197            }
198
199            if self.file.read().is_on_disk() {
200                self.file.write().flush_chunks(usize::MAX);
201            }
202            position
203        }
204    }
205
206    pub fn get_path(&self) -> PathBuf {
207        self.path.clone()
208    }
209
210    pub fn flush_async(&self) {}
211}
212
213impl Writer for FileWriter {
214    fn write(&mut self, bytes: &[u8]) -> Result<(), bincode::error::EncodeError> {
215        self.write_all_parallel(bytes, 1);
216        Ok(())
217    }
218}
219
220impl Write for FileWriter {
221    #[inline(always)]
222    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
223        self.write_all_parallel(buf, 1);
224        Ok(buf.len())
225    }
226
227    #[inline(always)]
228    fn flush(&mut self) -> std::io::Result<()> {
229        Ok(())
230    }
231}
232
233impl Drop for FileWriter {
234    fn drop(&mut self) {
235        let mut current_buffer = self.current_buffer.write();
236        if current_buffer.len() > 0 {
237            MemoryFileInternal::add_chunk(
238                &self.file,
239                std::mem::replace(current_buffer.deref_mut(), AllocatedChunk::INVALID),
240            );
241            if self.file.read().is_on_disk() {
242                self.file.write().flush_chunks(usize::MAX);
243            }
244        }
245        self.file.write().close();
246    }
247}