1use binbuf::{BytesPtr, bytes_ptr, Fixed as _, Entry as _};
2use crate::utils::{slice_to_array, slice_to_array_mut};
3use std::{fs::File, marker::PhantomData, path::Path};
4use memmap2::{MmapAsRawDesc, MmapMut, MmapOptions};
5pub use header::Value as Header;
6use super::OpenMode;
7
8pub mod header;
9
10#[derive(Debug)]
11pub enum GetError {
12 InvalidId,
13}
14
15#[derive(Debug)]
16pub enum AddError {
17 Io(std::io::Error),
18}
19
20#[derive(Debug)]
21pub enum RemoveLastError {
22 Io(std::io::Error),
23}
24
25#[derive(Debug)]
26pub enum SwapRemoveError {
27 RemoveLastError(RemoveLastError),
28 }
30
31#[derive(Debug)]
32pub enum CreateError {
33 Io(std::io::Error),
34}
35
36#[derive(Debug)]
37pub enum OpenError {
38 Io(std::io::Error),
39}
40
41pub struct OpenConfig {
42 pub mode: OpenMode,
43 pub file: File,
44 pub max_margin: u64,
45}
46
47pub struct Value<E> {
48 next_entry_id: u64,
49 file: File,
50 file_map: MmapMut,
51 margin: u64,
52 max_margin: u64,
53 _marker: PhantomData<fn() -> E>
54}
55
56impl<E: binbuf::Fixed> Value<E> {
57 pub unsafe fn open(mode: OpenMode, file: File, max_margin: u64) -> Result<Self, OpenError> {
58 let header_len = Header::LEN;
59 if let OpenMode::New = &mode {
60 file.set_len(header_len as u64).map_err(OpenError::Io)?;
61 }
62 let mut file_map = MmapMut::map_mut(&file).map_err(OpenError::Io)?;
63 let ptr = bytes_ptr::Const::new(file_map[0 .. header_len].as_ptr(), header_len);
64 let next_entry_id = match mode {
65 OpenMode::Existing => binbuf::fixed::decode(Header::buf(ptr).next_entry_id()),
66 OpenMode::New => unsafe {
67 binbuf::fixed::encode_ptr(
68 bytes_ptr::Mut::from_slice(&mut file_map[0 .. header_len]),
69 &Header { next_entry_id: 0 }
70 );
71 0
72 }
73 };
74 Ok(Self {
75 next_entry_id,
76 margin: 0,
77 max_margin,
78 file,
79 file_map,
80 _marker: PhantomData
81 })
82 }
83
84 pub fn len(&self) -> u64 {
85 self.next_entry_id
86 }
87
88 pub fn last_id(&self) -> u64 {
89 self.len() - 1
90 }
91
92 pub fn is_empty(&self) -> bool {
93 self.len() == 0
94 }
95
96 fn entry_offset(&self, id: u64) -> usize {
101 Header::LEN + E::LEN * (id as usize)
102 }
103
104 pub fn last_entry_id(&self) -> Option<u64> {
113 match self.next_entry_id {
114 0 => None,
115 _ => Some(self.next_entry_id - 1),
116 }
117 }
118
119 fn header_buf(&self) -> binbuf::BufConst<Header> {
120 let len = Header::LEN;
121 let ptr = unsafe { bytes_ptr::Const::from_slice(self.file_map.get_unchecked(0 .. len)) };
122 unsafe { Header::buf(ptr) }
123 }
124
125 fn header_buf_mut(&mut self) -> binbuf::BufMut<Header> {
126 let len = Header::LEN;
127 let ptr = unsafe { bytes_ptr::Mut::from_slice(self.file_map.get_unchecked_mut(0 .. len)) };
128 unsafe { Header::buf(ptr) }
129 }
130
131 fn set_next_entry_id(&mut self, value: u64) {
132 self.next_entry_id = value;
133 let v = self.next_entry_id;
134 v.encode(self.header_buf_mut().next_entry_id());
135 }
136
137 pub unsafe fn buf_unchecked(&self, id: u64) -> binbuf::BufConst<E> {
140 let offset = self.entry_offset(id);
141 let ptr = bytes_ptr::Const::new(self.file_map.get_unchecked(offset .. offset + E::LEN).as_ptr(), E::LEN);
142 E::buf(ptr)
143 }
144
145 pub unsafe fn buf_mut_unchecked(&mut self, id: u64) -> binbuf::BufMut<E> {
146 let offset = self.entry_offset(id);
147 let ptr = bytes_ptr::Mut::new(self.file_map.get_unchecked_mut(offset .. offset + E::LEN).as_mut_ptr(), E::LEN);
148 E::buf(ptr)
149 }
150
151 pub fn last_buf(&self) -> Option<binbuf::BufConst<E>> {
152 if self.is_empty() {
153 None
154 } else {
155 Some(unsafe { self.buf_unchecked(self.len() - 1) })
156 }
157 }
158
159 pub fn is_id_valid(&self, id: u64) -> bool {
160 self.next_entry_id > id
161 }
162
163 pub fn buf(&self, id: u64) -> binbuf::BufConst<E> {
164 if self.is_id_valid(id) {
165 unsafe { self.buf_unchecked(id) }
166 } else {
167 panic!("Id is invalid: {id}")
168 }
169 }
170
171 pub fn buf_mut(&mut self, id: u64) -> binbuf::BufMut<E> {
172 if self.is_id_valid(id) {
173 unsafe { self.buf_mut_unchecked(id) }
174 } else {
175 panic!("Id is invalid: {id}")
176 }
177 }
178 pub fn add(&mut self, entry: impl binbuf::fixed::Readable<E>) -> Result<u64, AddError> {
181 let id = self.next_entry_id;
182 if self.margin == 0 {
183 let new_len = self.entry_offset(id + self.max_margin + 2);
184 self.file.set_len(new_len as u64).map_err(AddError::Io)?;
185 self.file_map = unsafe { MmapOptions::new().len(new_len).map_mut(&self.file).map_err(AddError::Io)? };
186 self.margin = self.max_margin + 1;
187 }
188 self.margin -= 1;
189 entry.write_to(unsafe { self.buf_mut_unchecked(id) });
190 self.set_next_entry_id(self.next_entry_id + 1);
191 Ok(id)
192 }
193
194 pub fn remove_last(&mut self) -> Result<(), RemoveLastError> {
195 let id = self.next_entry_id;
196 if self.margin >= self.max_margin {
197 let new_len = self.entry_offset(id);
198 self.file.set_len(new_len as u64).map_err(RemoveLastError::Io)?;
199 self.file_map = unsafe { MmapOptions::new().len(new_len).map_mut(&self.file).map_err(RemoveLastError::Io)? };
200 self.margin = 0;
201 }
202 self.margin += 1;
203 self.set_next_entry_id(id - 1);
204 Ok(())
205 }
206
207 pub fn remove_if_last(&mut self, id: u64) -> Result<bool, RemoveLastError> {
210 if id == self.last_id() {
211 self.remove_last()?;
212 Ok(false)
213 } else {
214 Ok(true)
215 }
216 }
217
218 pub fn all_ids(&self) -> impl Iterator<Item = u64> {
219 0 .. self.next_entry_id
220 }
221
222 pub unsafe fn copy(
240 &mut self,
241 src_id: u64,
242 dst_id: u64,
243 ) {
244 let mut dst = self.buf_mut_unchecked(dst_id);
245 let src = self.buf_unchecked(src_id);
246 binbuf::fixed::buf_copy_to::<E>(src, dst);
247 }
248
249 pub unsafe fn swap(
252 &mut self,
253 a_id: u64,
254 b_id: u64,
255 ) {
256 if a_id != b_id {
257 let mut a = self.buf_mut_unchecked(a_id);
258 let mut b = self.buf_mut_unchecked(b_id);
259 binbuf::fixed::buf_swap::<E>(a, b);
260 }
261 }
262
263 pub unsafe fn swap_remove(&mut self, id: u64) -> Result<(), SwapRemoveError> {
268 if let Some(last_entry_id) = self.last_entry_id() {
269 self.swap(id, last_entry_id);
270 }
271 self.remove_last().map_err(SwapRemoveError::RemoveLastError)?;
272 Ok(())
273 }
274
275 pub fn set(&mut self, id: u64, value: impl binbuf::fixed::Readable<E>) {
276 if self.is_id_valid(id) {
277 unsafe { value.write_to(self.buf_mut_unchecked(id)) }
278 } else {
279 panic!("Invalid id: {id}");
280 }
281 }
282}
283
284impl<E: binbuf::fixed::Decode> Value<E> {
285 pub fn get(&self, id: u64) -> E {
286 E::decode(self.buf(id))
287 }
288}