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,106 @@ 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
+ /// eprintln!("checksumming {input:?} which gives {sum:?}");
168
+ /// output.copy_from_slice(&sum.to_ne_bytes());
169
+ /// }
170
+ /// }
171
+ ///
172
+ /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
173
+ /// let env_path = tempfile::tempdir()?;
174
+ ///
175
+ /// fs::create_dir_all(&env_path)?;
176
+ ///
177
+ /// // We open the environment
178
+ /// let mut options = EnvOpenOptions::new().checksum::<Crc32Bzip2>();
179
+ /// let env = unsafe {
180
+ /// options
181
+ /// .map_size(10 * 1024 * 1024) // 10MB
182
+ /// .max_dbs(3)
183
+ /// .open(&env_path)?
184
+ /// };
185
+ ///
186
+ /// let key1 = "first-key";
187
+ /// let val1 = "this is my first value";
188
+ /// let key2 = "second-key";
189
+ /// let val2 = "this is a second information";
190
+ ///
191
+ /// // We create a database and write values in it
192
+ /// let mut wtxn = env.write_txn()?;
193
+ /// let db = env.create_database::<Str, Str>(&mut wtxn, Some("first"))?;
194
+ /// db.put(&mut wtxn, key1, val1)?;
195
+ /// db.put(&mut wtxn, key2, val2)?;
196
+ /// wtxn.commit()?;
197
+ ///
198
+ /// // We check that we can read the values back
199
+ /// let rtxn = env.read_txn()?;
200
+ /// assert_eq!(db.get(&rtxn, key1)?, Some(val1));
201
+ /// assert_eq!(db.get(&rtxn, key2)?, Some(val2));
202
+ /// drop(rtxn);
203
+ ///
204
+ /// // We close the env and check that we can read in it
205
+ /// env.prepare_for_closing().wait();
206
+ ///
207
+ /// // We modify the content of the data file
208
+ /// let mut content = fs::read(env_path.path().join("data.mdb"))?;
209
+ /// let pos = find(&content, b"value").unwrap();
210
+ /// content[pos..pos + 5].copy_from_slice(b"thing");
211
+ /// fs::write(env_path.path().join("data.mdb"), content)?;
212
+ ///
213
+ /// // We reopen the environment
214
+ /// let mut options = EnvOpenOptions::new().checksum::<Crc32Bzip2>();
215
+ /// let env = unsafe {
216
+ /// options
217
+ /// .map_size(10 * 1024 * 1024) // 10MB
218
+ /// .max_dbs(3)
219
+ /// .open(&env_path)?
220
+ /// };
221
+ ///
222
+ /// // We check that we can read the values back
223
+ /// let rtxn = env.read_txn()?;
224
+ /// let db = env.open_database::<Str, Str>(&rtxn, Some("first"))?.unwrap();
225
+ /// assert!(matches!(db.get(&rtxn, key1).unwrap_err(), Error::Mdb(MdbError::BadChecksum)));
226
+ /// drop(rtxn);
227
+ ///
228
+ /// # Ok(()) }
229
+ /// ```
230
+ pub fn checksum < NC : Checksum > ( self ) -> EnvOpenOptions < T , NC > {
231
+ let Self { map_size, max_readers, max_dbs, flags, _marker } = self ;
232
+ EnvOpenOptions { map_size, max_readers, max_dbs, flags, _marker : PhantomData }
132
233
}
133
234
134
235
/// Set the size of the memory map to use for this environment.
@@ -237,18 +338,6 @@ impl<T: TlsUsage> EnvOpenOptions<T> {
237
338
path. as_ref ( ) ,
238
339
#[ cfg( master3) ]
239
340
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
341
)
253
342
}
254
343
@@ -404,7 +493,6 @@ impl<T: TlsUsage> EnvOpenOptions<T> {
404
493
{
405
494
self . raw_open_with_checksum_and_encryption (
406
495
path. as_ref ( ) ,
407
- None ,
408
496
Some ( ( Some ( encrypt_func_wrapper :: < E > ) , & key, <E as AeadCore >:: TagSize :: U32 ) ) ,
409
497
)
410
498
. map ( |inner| EncryptedEnv { inner } )
@@ -413,7 +501,6 @@ impl<T: TlsUsage> EnvOpenOptions<T> {
413
501
fn raw_open_with_checksum_and_encryption (
414
502
& self ,
415
503
path : & Path ,
416
- #[ cfg( master3) ] sum : Option < ( ffi:: MDB_sum_func , u32 ) > ,
417
504
#[ cfg( master3) ] enc : Option < ( ffi:: MDB_enc_func , & [ u8 ] , u32 ) > ,
418
505
) -> Result < Env < T > > {
419
506
let mut lock = OPENED_ENV . write ( ) . unwrap ( ) ;
@@ -451,6 +538,16 @@ impl<T: TlsUsage> EnvOpenOptions<T> {
451
538
) ) ?;
452
539
}
453
540
541
+ #[ cfg( master3) ]
542
+ if TypeId :: of :: < C > ( ) != TypeId :: of :: < NoChecksum > ( ) {
543
+ eprintln ! ( "Doing some checksumming stuff" ) ;
544
+ mdb_result ( ffi:: mdb_env_set_checksum (
545
+ env,
546
+ Some ( checksum_func_wrapper :: < C > ) ,
547
+ C :: SIZE ,
548
+ ) ) ?;
549
+ }
550
+
454
551
if let Some ( size) = self . map_size {
455
552
if size % page_size:: get ( ) != 0 {
456
553
let msg = format ! (
@@ -496,15 +593,15 @@ impl<T: TlsUsage> EnvOpenOptions<T> {
496
593
}
497
594
}
498
595
499
- impl Default for EnvOpenOptions < WithTls > {
596
+ impl Default for EnvOpenOptions < WithTls , NoChecksum > {
500
597
fn default ( ) -> Self {
501
598
Self :: new ( )
502
599
}
503
600
}
504
601
505
- impl < T : TlsUsage > Clone for EnvOpenOptions < T > {
602
+ impl < T : TlsUsage , C : Checksum > Clone for EnvOpenOptions < T , C > {
506
603
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 }
604
+ let Self { map_size, max_readers, max_dbs, flags, _marker } = * self ;
605
+ EnvOpenOptions { map_size, max_readers, max_dbs, flags, _marker }
509
606
}
510
607
}
0 commit comments