1
+ #[ cfg( master3) ]
2
+ use std:: any:: TypeId ;
1
3
use std:: ffi:: CString ;
2
4
#[ cfg( windows) ]
3
5
use std:: ffi:: OsStr ;
@@ -14,10 +16,12 @@ use std::{io, ptr};
14
16
use aead:: { generic_array:: typenum:: Unsigned , AeadCore , AeadMutInPlace , Key , KeyInit } ;
15
17
use synchronoise:: SignalEvent ;
16
18
19
+ #[ cfg( master3) ]
20
+ use super :: checksum_func_wrapper;
17
21
#[ cfg( master3) ]
18
22
use super :: encrypted_env:: { encrypt_func_wrapper, EncryptedEnv } ;
19
23
use super :: env:: Env ;
20
- use super :: { canonicalize_path, OPENED_ENV } ;
24
+ use super :: { canonicalize_path, Checksum , NoChecksum , OPENED_ENV } ;
21
25
#[ cfg( windows) ]
22
26
use crate :: envs:: OsStrExtLmdb as _;
23
27
use crate :: mdb:: error:: mdb_result;
@@ -28,28 +32,28 @@ use crate::{EnvFlags, Error, Result};
28
32
/// Options and flags which can be used to configure how an environment is opened.
29
33
#[ derive( Debug , PartialEq , Eq ) ]
30
34
#[ cfg_attr( feature = "serde" , derive( serde:: Serialize , serde:: Deserialize ) ) ]
31
- pub struct EnvOpenOptions < T : TlsUsage > {
35
+ pub struct EnvOpenOptions < T : TlsUsage , C : Checksum > {
32
36
map_size : Option < usize > ,
33
37
max_readers : Option < u32 > ,
34
38
max_dbs : Option < u32 > ,
35
39
flags : EnvFlags ,
36
- _tls_marker : PhantomData < T > ,
40
+ _marker : PhantomData < ( T , C ) > ,
37
41
}
38
42
39
- impl EnvOpenOptions < WithTls > {
43
+ impl EnvOpenOptions < WithTls , NoChecksum > {
40
44
/// Creates a blank new set of options ready for configuration.
41
- pub fn new ( ) -> EnvOpenOptions < WithTls > {
45
+ pub fn new ( ) -> EnvOpenOptions < WithTls , NoChecksum > {
42
46
EnvOpenOptions {
43
47
map_size : None ,
44
48
max_readers : None ,
45
49
max_dbs : None ,
46
50
flags : EnvFlags :: empty ( ) ,
47
- _tls_marker : PhantomData ,
51
+ _marker : PhantomData ,
48
52
}
49
53
}
50
54
}
51
55
52
- impl < T : TlsUsage > EnvOpenOptions < T > {
56
+ impl < T : TlsUsage , C : Checksum + ' static > EnvOpenOptions < T , C > {
53
57
/// Make the read transactions `!Send` by specifying they will
54
58
/// use Thread Local Storage (TLS). It is often faster to open
55
59
/// TLS-backed transactions.
@@ -81,9 +85,9 @@ impl<T: TlsUsage> EnvOpenOptions<T> {
81
85
/// is_sendable(rtxn);
82
86
/// # Ok(()) }
83
87
/// ```
84
- pub fn read_txn_with_tls ( self ) -> EnvOpenOptions < WithTls > {
85
- let Self { map_size, max_readers, max_dbs, flags, _tls_marker : _ } = self ;
86
- EnvOpenOptions { map_size, max_readers, max_dbs, flags, _tls_marker : PhantomData }
88
+ pub fn read_txn_with_tls ( self ) -> EnvOpenOptions < WithTls , C > {
89
+ let Self { map_size, max_readers, max_dbs, flags, _marker : _ } = self ;
90
+ EnvOpenOptions { map_size, max_readers, max_dbs, flags, _marker : PhantomData }
87
91
}
88
92
89
93
/// Make the read transactions `Send` by specifying they will
@@ -126,9 +130,105 @@ impl<T: TlsUsage> EnvOpenOptions<T> {
126
130
/// is_sendable(rtxn);
127
131
/// # Ok(()) }
128
132
/// ```
129
- pub fn read_txn_without_tls ( self ) -> EnvOpenOptions < WithoutTls > {
130
- let Self { map_size, max_readers, max_dbs, flags, _tls_marker : _ } = self ;
131
- EnvOpenOptions { map_size, max_readers, max_dbs, flags, _tls_marker : PhantomData }
133
+ pub fn read_txn_without_tls ( self ) -> EnvOpenOptions < WithoutTls , C > {
134
+ let Self { map_size, max_readers, max_dbs, flags, _marker : _ } = self ;
135
+ EnvOpenOptions { map_size, max_readers, max_dbs, flags, _marker : PhantomData }
136
+ }
137
+
138
+ #[ cfg( master3) ]
139
+ /// Changes the checksum algorithm to use.
140
+ ///
141
+ /// # Basic Example
142
+ ///
143
+ /// Creates and open a database. The [`Env`] is using a [`crc`](https://github.com/mrhooray/crc-rs)
144
+ /// algorithm.
145
+ ///
146
+ /// Note that you cannot use **any** type of crc algorithm as it is possible to tell
147
+ /// the size of the crc to LMDB.
148
+ ///
149
+ /// ```
150
+ /// use std::fs;
151
+ /// use std::path::Path;
152
+ /// use memchr::memmem::find;
153
+ /// use argon2::Argon2;
154
+ /// use chacha20poly1305::{ChaCha20Poly1305, Key};
155
+ /// use heed3::types::*;
156
+ /// use heed3::{EnvOpenOptions, Checksum, Database, Error, MdbError};
157
+ ///
158
+ /// /// A checksum algorithm based on the well-known CRC_32_BZIP2.
159
+ /// enum Crc32Bzip2 {}
160
+ ///
161
+ /// impl Checksum for Crc32Bzip2 {
162
+ /// // Be careful the size is in bytes not bits.
163
+ /// const SIZE: u32 = 32 / 8;
164
+ ///
165
+ /// fn checksum(input: &[u8], output: &mut [u8], _key: Option<&[u8]>) {
166
+ /// let sum = crc::Crc::<u32>::new(&crc::CRC_32_BZIP2).checksum(input);
167
+ /// output.copy_from_slice(&sum.to_ne_bytes());
168
+ /// }
169
+ /// }
170
+ ///
171
+ /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
172
+ /// let env_path = tempfile::tempdir()?;
173
+ ///
174
+ /// fs::create_dir_all(&env_path)?;
175
+ ///
176
+ /// // We open the environment
177
+ /// let mut options = EnvOpenOptions::new().checksum::<Crc32Bzip2>();
178
+ /// let env = unsafe {
179
+ /// options
180
+ /// .map_size(10 * 1024 * 1024) // 10MB
181
+ /// .max_dbs(3)
182
+ /// .open(&env_path)?
183
+ /// };
184
+ ///
185
+ /// let key1 = "first-key";
186
+ /// let val1 = "this is my first value";
187
+ /// let key2 = "second-key";
188
+ /// let val2 = "this is a second information";
189
+ ///
190
+ /// // We create a database and write values in it
191
+ /// let mut wtxn = env.write_txn()?;
192
+ /// let db = env.create_database::<Str, Str>(&mut wtxn, Some("first"))?;
193
+ /// db.put(&mut wtxn, key1, val1)?;
194
+ /// db.put(&mut wtxn, key2, val2)?;
195
+ /// wtxn.commit()?;
196
+ ///
197
+ /// // We check that we can read the values back
198
+ /// let rtxn = env.read_txn()?;
199
+ /// assert_eq!(db.get(&rtxn, key1)?, Some(val1));
200
+ /// assert_eq!(db.get(&rtxn, key2)?, Some(val2));
201
+ /// drop(rtxn);
202
+ ///
203
+ /// // We close the env and check that we can read in it
204
+ /// env.prepare_for_closing().wait();
205
+ ///
206
+ /// // We modify the content of the data file
207
+ /// let mut content = fs::read(env_path.path().join("data.mdb"))?;
208
+ /// let pos = find(&content, b"value").unwrap();
209
+ /// content[pos..pos + 5].copy_from_slice(b"thing");
210
+ /// fs::write(env_path.path().join("data.mdb"), content)?;
211
+ ///
212
+ /// // We reopen the environment
213
+ /// let mut options = EnvOpenOptions::new().checksum::<Crc32Bzip2>();
214
+ /// let env = unsafe {
215
+ /// options
216
+ /// .map_size(10 * 1024 * 1024) // 10MB
217
+ /// .max_dbs(3)
218
+ /// .open(&env_path)?
219
+ /// };
220
+ ///
221
+ /// // We check that we can read the values back
222
+ /// let rtxn = env.read_txn()?;
223
+ /// let db = env.open_database::<Str, Str>(&rtxn, Some("first"))?.unwrap();
224
+ /// assert!(matches!(db.get(&rtxn, key1).unwrap_err(), Error::Mdb(MdbError::BadChecksum)));
225
+ /// drop(rtxn);
226
+ ///
227
+ /// # Ok(()) }
228
+ /// ```
229
+ pub fn checksum < NC : Checksum > ( self ) -> EnvOpenOptions < T , NC > {
230
+ let Self { map_size, max_readers, max_dbs, flags, _marker } = self ;
231
+ EnvOpenOptions { map_size, max_readers, max_dbs, flags, _marker : PhantomData }
132
232
}
133
233
134
234
/// Set the size of the memory map to use for this environment.
@@ -237,18 +337,6 @@ impl<T: TlsUsage> EnvOpenOptions<T> {
237
337
path. as_ref ( ) ,
238
338
#[ cfg( master3) ]
239
339
None ,
240
- #[ cfg( master3) ]
241
- None ,
242
- )
243
- }
244
-
245
- pub unsafe fn open_checksummed < P : AsRef < Path > > ( & self , path : P ) -> Result < Env < T > > {
246
- self . raw_open_with_checksum_and_encryption (
247
- path. as_ref ( ) ,
248
- #[ cfg( master3) ]
249
- None ,
250
- #[ cfg( master3) ]
251
- None ,
252
340
)
253
341
}
254
342
@@ -404,7 +492,6 @@ impl<T: TlsUsage> EnvOpenOptions<T> {
404
492
{
405
493
self . raw_open_with_checksum_and_encryption (
406
494
path. as_ref ( ) ,
407
- None ,
408
495
Some ( ( Some ( encrypt_func_wrapper :: < E > ) , & key, <E as AeadCore >:: TagSize :: U32 ) ) ,
409
496
)
410
497
. map ( |inner| EncryptedEnv { inner } )
@@ -413,7 +500,6 @@ impl<T: TlsUsage> EnvOpenOptions<T> {
413
500
fn raw_open_with_checksum_and_encryption (
414
501
& self ,
415
502
path : & Path ,
416
- #[ cfg( master3) ] sum : Option < ( ffi:: MDB_sum_func , u32 ) > ,
417
503
#[ cfg( master3) ] enc : Option < ( ffi:: MDB_enc_func , & [ u8 ] , u32 ) > ,
418
504
) -> Result < Env < T > > {
419
505
let mut lock = OPENED_ENV . write ( ) . unwrap ( ) ;
@@ -451,6 +537,15 @@ impl<T: TlsUsage> EnvOpenOptions<T> {
451
537
) ) ?;
452
538
}
453
539
540
+ #[ cfg( master3) ]
541
+ if TypeId :: of :: < C > ( ) != TypeId :: of :: < NoChecksum > ( ) {
542
+ mdb_result ( ffi:: mdb_env_set_checksum (
543
+ env,
544
+ Some ( checksum_func_wrapper :: < C > ) ,
545
+ C :: SIZE ,
546
+ ) ) ?;
547
+ }
548
+
454
549
if let Some ( size) = self . map_size {
455
550
if size % page_size:: get ( ) != 0 {
456
551
let msg = format ! (
@@ -496,15 +591,15 @@ impl<T: TlsUsage> EnvOpenOptions<T> {
496
591
}
497
592
}
498
593
499
- impl Default for EnvOpenOptions < WithTls > {
594
+ impl Default for EnvOpenOptions < WithTls , NoChecksum > {
500
595
fn default ( ) -> Self {
501
596
Self :: new ( )
502
597
}
503
598
}
504
599
505
- impl < T : TlsUsage > Clone for EnvOpenOptions < T > {
600
+ impl < T : TlsUsage , C : Checksum > Clone for EnvOpenOptions < T , C > {
506
601
fn clone ( & self ) -> Self {
507
- let Self { map_size, max_readers, max_dbs, flags, _tls_marker } = * self ;
508
- EnvOpenOptions { map_size, max_readers, max_dbs, flags, _tls_marker }
602
+ let Self { map_size, max_readers, max_dbs, flags, _marker } = * self ;
603
+ EnvOpenOptions { map_size, max_readers, max_dbs, flags, _marker }
509
604
}
510
605
}
0 commit comments