├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src ├── cursor.rs ├── dbi.rs ├── env.rs ├── error.rs ├── example_helpers.rs ├── ffi2.rs ├── iter.rs ├── lib.rs ├── mdb_vals.rs ├── test_helpers.rs ├── traits.rs ├── tx.rs └── unaligned.rs /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | target 3 | Cargo.lock 4 | .cargo 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: false 3 | dist: trusty 4 | rust: 5 | - 1.12.0 6 | - stable 7 | - beta 8 | - nightly 9 | 10 | cache: cargo 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lmdb-zero" 3 | version = "0.4.4" 4 | authors = ["FullContact, Inc", "Jason Lingle"] 5 | license = "MIT/Apache-2.0" 6 | readme = "README.md" 7 | repository = "https://github.com/AltSysrq/lmdb-zero" 8 | documentation = "https://docs.rs/lmdb-zero" 9 | keywords = ["lmdb", "zero-copy", "btree"] 10 | categories = ["database"] 11 | 12 | description = """ 13 | An almost-safe, near-zero-cost, feature-complete, unabashedly non-abstract 14 | wrapper around LMDB. 15 | """ 16 | 17 | [badges] 18 | travis-ci = { repository = "AltSysrq/lmdb-zero" } 19 | 20 | [dependencies] 21 | liblmdb-sys = "0.2.2" 22 | bitflags = "0.9.1" 23 | libc = "0.2.14" 24 | supercow = "0.1.0" 25 | 26 | [dev-dependencies] 27 | tempdir = "=0.3.4" 28 | 29 | # These are redundant / not directly used dependencies which increased the 30 | # required rust version in a minor version bump. Pinning them like this is 31 | # necessary to test on Rust 1.12.0. 32 | libc = "=0.2.18" 33 | gcc = "=0.3.39" 34 | rand = "=0.3.15" 35 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 FullContact, Inc 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lmdb-zero 2 | 3 | [![Build Status](https://travis-ci.org/AltSysrq/lmdb-zero.svg?branch=master)](https://travis-ci.org/AltSysrq/lmdb-zero) 4 | [![](http://meritbadge.herokuapp.com/lmdb-zero)](https://crates.io/crates/lmdb-zero) 5 | 6 | lmdb-zero is a near-zero-cost wrapper around [LMDB](http://lmdb.tech/) designed 7 | to allow using the full range of features offered by LMDB while keeping it 8 | reasonably easy to write safe programs. 9 | 10 | `lmdb-zero` is as much as possible a 1:1 mapping of the raw API, mainly 11 | providing RAII constructs and integration into Rust's borrow checker to ensure 12 | safety. 13 | 14 | [Documentation](https://docs.rs/lmdb-zero) 15 | 16 | ## Features 17 | 18 | - Zero-copy API. Reads return references into the memory-mapped file. Using 19 | `MDB_RESERVE` to allocate space in the file and directly write to it is 20 | supported. 21 | 22 | - Cursors directly map to the same operations provided by LMDB, but in a 23 | typesafe manner. 24 | 25 | - Nested transactions. 26 | 27 | - Full integration with the borrow checker. Read references are checked to not 28 | outlive their transaction or overlap with a write in the same transaction. 29 | 30 | - Cursors and read transactions can be reset and reused. 31 | 32 | ## Status 33 | 34 | The API is complete and reasonably stable and is believed to be sound insofar 35 | as Rust's unsafety rules are actually defined. 36 | 37 | This crate has not been thoroughly tested on architectures with strong 38 | alignment constraints, though the tests pass on ARM7. While the conversion API 39 | checks for correct alignment by default, issues such as 40 | [#27060](https://github.com/rust-lang/rust/issues/27060) could come up, and it 41 | is of course possible there are bugs in handling alignment here. 42 | 43 | ## Changelog 44 | 45 | **0.4.4**: New `Environment` and `Database` methods to enable interoperating 46 | with native C/C++ code. `Database::dbi()` renamed to `as_raw()`. The old name 47 | is still available but deprecated. 48 | 49 | **0.4.3**: Fix panic on `Cursor::get_multiple()` if the current key has exactly 50 | one item. `LmdbResultExt` is now reexported from the crate root for better 51 | visibility. 52 | 53 | **0.4.2**: Fix being unable to open databases in read-only environments. Fix 54 | future-incompatibility warning arising from `Unaligned`. 55 | 56 | **0.4.1**: Tests updated to work on Rust 1.20. `bitflags` dependency updated. 57 | Neither of these are expected to have any impact on external code. 58 | 59 | **0.4.0**: Minor breaking changes. `ConstAccessor` and `WriteAccessor` can now 60 | be dropped and re-obtained. Most types now support additional 61 | ownership/borrowing modes, which allows for dynamic lifetime management and 62 | other possibilities. Upgrade to `liblmdb-sys` 0.2.2 and `bitflags` 0.8.0. 63 | Fixes to documentation. 64 | 65 | **0.3.1**: Metadata updates to reflect change of crate ownership. No software 66 | changes were made in this version. 67 | 68 | **0.3.0**: **Breaking Changes** to the API, see section below. Migration is 69 | expected to be easy for most use-cases. Slight performance improvement due to 70 | additions of `#[inline]`. 71 | 72 | **0.2.2**: `ResetTransaction` is now actually public, making that part of the 73 | API more accessible. Add documentation for lifetimes. 74 | 75 | **0.2.1**: Fix use-after-free when passing database name to `mdb_dbi_open`. Fix 76 | calling `mdb_txn_abort` after transaction commit fails. 77 | [#1](https://github.com/AltSysrq/lmdb-zero/pull/1). 78 | 79 | **0.2.0**: Switch from `lmdb-sys` to newer `liblmdb-sys`. 80 | 81 | **0.1.0**: Initial release. 82 | 83 | ### Breaking Changes in 0.4.0 84 | 85 | A number of functions which formerly took an `&SomeType` parameter now take an 86 | `Into>`. For the vast majority of existing code, this has no 87 | effect, but it could cause issues if older code was relying on an implicit 88 | `Deref` call (for example, via `lazy_static!`) to produce the correct reference 89 | type. If this causes issues, explicitly writing the dereferencing is required. 90 | For example, if your code originally had `lmdb::Database::open(&ENV, ...)` where 91 | `ENV` was declared via `lazy_static!`, it would need to be changed to 92 | `lmdb::Database::open(&*ENV, ...)`. 93 | 94 | `ConstAccessor` and `WriteAccessor` must now be _strictly_ outlived by their 95 | transactions. No practical cases where this would be an issue are apparent, but 96 | if it comes up, code must be rearranged to ensure the accessor is dropped 97 | before the transaction. Note that now one can drop the accessor and later 98 | re-obtain it. 99 | 100 | ### Breaking Changes in 0.3.0 101 | 102 | `lmdb::Error` has been completely reworked. It is now an enum with the 103 | lmdb-zero errors cleanly separated from native LMDB errors. `ValRejected` now 104 | includes an error message. 105 | 106 | `FromLmdbBytes.from_lmdb_bytes()` now returns a `Result<&Self, String>` instead 107 | of an `Option`. This is mainly to make alignment issues less subtle and point 108 | people directly to advice on how to fix the problem, but should be able to make 109 | other things clearer as well. 110 | 111 | The mostly untested and somewhat questionable `lax_alignment` feature has been 112 | dropped. `LmdbRaw` now always enforces alignment requirements. Client code 113 | which wishes to operate on misaligned values which cannot use the `Unaligned` 114 | or `#[repr(packed)]` solutions will need to provide its own `FromLmdbBytes` 115 | implementations. 116 | 117 | The primitive types which have alignment requirements (eg, `i32`, `u64`) are no 118 | longer `LmdbRaw`, as this made it too easy to write code depending on 119 | happenstance to align the values correctly. Client code now _must_ wrap them in 120 | `Unaligned` to read them directly, or else provide its own unit structs if it 121 | has other needs. Note that these types and their arrays are still 122 | `AsLmdbBytes`. 123 | 124 | Unfortunately, as a side-effect of the above, `Wrapping` and `Wrapping` 125 | are no longer `LmdbRaw` or `LmdbOrdKey`, but instead only 126 | `LmdbRawIfUnaligned` and `LmdbOrdKeyIfUnaligned`. Wrapping these in `Unalinged` 127 | will work in most cases without overhead. 128 | 129 | ## Contribution 130 | 131 | Unless you explicitly state otherwise, any contribution intentionally submitted 132 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall 133 | be dual licensed as above, without any additional terms or conditions. 134 | -------------------------------------------------------------------------------- /src/dbi.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 FullContact, Inc 2 | // Copyright 2017, 2018 Jason Lingle 3 | // 4 | // Licensed under the Apache License, Version 2.0 or the MIT license 6 | // , at your 7 | // option. This file may not be copied, modified, or distributed 8 | // except according to those terms. 9 | 10 | use std::cmp::{Ord, Ordering}; 11 | use std::ffi::CString; 12 | use std::mem; 13 | use std::ptr; 14 | use libc::c_int; 15 | 16 | use ffi; 17 | use supercow::Supercow; 18 | 19 | use env::{self, Environment}; 20 | use error::{Error, Result}; 21 | use mdb_vals::*; 22 | use traits::*; 23 | use tx::TxHandle; 24 | 25 | /// Flags used when opening databases. 26 | pub mod db { 27 | use ffi; 28 | use libc; 29 | 30 | bitflags! { 31 | /// Flags used when opening databases. 32 | pub struct Flags : libc::c_uint { 33 | /// Keys are strings to be compared in reverse order, from the end 34 | /// of the strings to the beginning. By default, Keys are treated 35 | /// as strings and compared from beginning to end. 36 | /// 37 | /// NOTE: This is *not* reverse sort, but rather right-to-left 38 | /// comparison. 39 | /// 40 | /// ## Example 41 | /// 42 | /// ``` 43 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 44 | /// # fn main() { 45 | /// # let env = create_env(); 46 | /// let db = lmdb::Database::open( 47 | /// &env, Some("reversed"), &lmdb::DatabaseOptions::new( 48 | /// lmdb::db::REVERSEKEY | lmdb::db::CREATE)).unwrap(); 49 | /// let txn = lmdb::WriteTransaction::new(&env).unwrap(); 50 | /// { 51 | /// let mut access = txn.access(); 52 | /// let f = lmdb::put::Flags::empty(); 53 | /// access.put(&db, "Germany", "Berlin", f).unwrap(); 54 | /// access.put(&db, "Latvia", "Rīga", f).unwrap(); 55 | /// access.put(&db, "France", "Paris", f).unwrap(); 56 | /// 57 | /// let mut cursor = txn.cursor(&db).unwrap(); 58 | /// // The keys are compared as if we had input "aivtaL", "ecnarF", 59 | /// // and "ynamreG", so "Latvia" comes first and "Germany" comes 60 | /// // last. 61 | /// assert_eq!(("Latvia", "Rīga"), cursor.first(&access).unwrap()); 62 | /// assert_eq!(("Germany", "Berlin"), cursor.last(&access).unwrap()); 63 | /// } 64 | /// txn.commit().unwrap(); 65 | /// # } 66 | /// ``` 67 | const REVERSEKEY = ffi::MDB_REVERSEKEY; 68 | /// Duplicate keys may be used in the database. (Or, from another 69 | /// perspective, keys may have multiple data items, stored in 70 | /// sorted order.) By default keys must be unique and may have only 71 | /// a single data item. 72 | /// 73 | /// ## Example 74 | /// ``` 75 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 76 | /// # fn main() { 77 | /// # let env = create_env(); 78 | /// let db = lmdb::Database::open( 79 | /// &env, Some("example"), &lmdb::DatabaseOptions::new( 80 | /// lmdb::db::DUPSORT | lmdb::db::CREATE)).unwrap(); 81 | /// let txn = lmdb::WriteTransaction::new(&env).unwrap(); 82 | /// { 83 | /// let mut access = txn.access(); 84 | /// let f = lmdb::put::Flags::empty(); 85 | /// access.put(&db, "Fruit", "Orange", f).unwrap(); 86 | /// access.put(&db, "Fruit", "Apple", f).unwrap(); 87 | /// access.put(&db, "Veggie", "Carrot", f).unwrap(); 88 | /// 89 | /// let mut cursor = txn.cursor(&db).unwrap(); 90 | /// assert_eq!(("Fruit", "Apple"), 91 | /// cursor.seek_k_both(&access, "Fruit").unwrap()); 92 | /// assert_eq!(("Fruit", "Orange"), cursor.next(&access).unwrap()); 93 | /// assert_eq!(("Veggie", "Carrot"), cursor.next(&access).unwrap()); 94 | /// } 95 | /// txn.commit().unwrap(); 96 | /// # } 97 | /// ``` 98 | const DUPSORT = ffi::MDB_DUPSORT; 99 | /// Keys are binary integers in native byte order, either 100 | /// `libc::c_uint` or `libc::size_t`, and will be sorted as such. 101 | /// The keys must all be of the same size. 102 | /// 103 | /// ## Example 104 | /// 105 | /// ``` 106 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 107 | /// # fn main() { 108 | /// # let env = create_env(); 109 | /// use lmdb::unaligned as u; 110 | /// 111 | /// let db = lmdb::Database::open( 112 | /// &env, Some("reversed"), &lmdb::DatabaseOptions::new( 113 | /// lmdb::db::INTEGERKEY | lmdb::db::CREATE)).unwrap(); 114 | /// let txn = lmdb::WriteTransaction::new(&env).unwrap(); 115 | /// { 116 | /// let mut access = txn.access(); 117 | /// let f = lmdb::put::Flags::empty(); 118 | /// // Write the keys in native byte order. 119 | /// // Note that on little-endian systems this means a 120 | /// // byte-by-byte comparison would not order the keys the way 121 | /// // one might expect. 122 | /// access.put(&db, &42u32, "Fourty-two", f).unwrap(); 123 | /// access.put(&db, &65536u32, "65'536", f).unwrap(); 124 | /// access.put(&db, &0u32, "Zero", f).unwrap(); 125 | /// 126 | /// let mut cursor = txn.cursor(&db).unwrap(); 127 | /// // But because we used `INTEGERKEY`, they are in fact sorted 128 | /// // ascending by integer value. 129 | /// assert_eq!((u(&0u32), "Zero"), cursor.first(&access).unwrap()); 130 | /// assert_eq!((u(&42u32), "Fourty-two"), cursor.next(&access).unwrap()); 131 | /// assert_eq!((u(&65536u32), "65'536"), cursor.next(&access).unwrap()); 132 | /// } 133 | /// txn.commit().unwrap(); 134 | /// # } 135 | /// ``` 136 | const INTEGERKEY = ffi::MDB_INTEGERKEY; 137 | /// This flag may only be used in combination with `DUPSORT`. This 138 | /// option tells the library that the data items for this database 139 | /// are all the same size, which allows further optimizations in 140 | /// storage and retrieval. When all data items are the same size, 141 | /// the `get_multiple` and `next_multiple` cursor operations may be 142 | /// used to retrieve multiple items at once. 143 | /// 144 | /// ## Notes 145 | /// 146 | /// There are no runtime checks that values are actually the same 147 | /// size; failing to uphold this constraint may result in 148 | /// unpredictable behaviour. 149 | /// 150 | /// ## Example 151 | /// 152 | /// ``` 153 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 154 | /// # fn main() { 155 | /// # let env = create_env(); 156 | /// use lmdb::Unaligned as U; 157 | /// use lmdb::LmdbResultExt; 158 | /// 159 | /// let db = lmdb::Database::open( 160 | /// &env, Some("reversed"), &lmdb::DatabaseOptions::new( 161 | /// lmdb::db::DUPSORT | lmdb::db::DUPFIXED | 162 | /// lmdb::db::INTEGERDUP | lmdb::db::CREATE)) 163 | /// .unwrap(); 164 | /// let txn = lmdb::WriteTransaction::new(&env).unwrap(); 165 | /// { 166 | /// let mut access = txn.access(); 167 | /// let f = lmdb::put::Flags::empty(); 168 | /// // Map strings to their constituent chars 169 | /// for s in &["foo", "bar", "xyzzy"] { 170 | /// for c in s.chars() { 171 | /// access.put(&db, *s, &c, f).unwrap(); 172 | /// } 173 | /// } 174 | /// 175 | /// // Read in all the chars of "xyzzy" in sorted order via 176 | /// // cursoring. 177 | /// // Note that we have to read `u32`s because `char`s are not 178 | /// // directly convertable from byte arrays. 179 | /// let mut xyzzy: Vec = Vec::new(); 180 | /// let mut cursor = txn.cursor(&db).unwrap(); 181 | /// cursor.seek_k::>(&access, "xyzzy").unwrap(); 182 | /// loop { 183 | /// let chars = if xyzzy.is_empty() { 184 | /// // First page. 185 | /// // Note that in this example we probably get everything 186 | /// // on the first page, but with more values we'd need to 187 | /// // use `next_multiple` to get the rest. 188 | /// cursor.get_multiple::<[U]>(&access).unwrap() 189 | /// } else { 190 | /// // .to_opt() to turn NOTFOUND into None 191 | /// match cursor.next_multiple::<[U]>(&access).to_opt() { 192 | /// // Ok if there's still more for the current key 193 | /// Ok(Some(c)) => c, 194 | /// // NOTFOUND error (=> None) at end of key 195 | /// Ok(None) => break, 196 | /// // Handle other errors 197 | /// Err(e) => panic!("LMDB error: {}", e), 198 | /// } 199 | /// }; 200 | /// for ch in chars { 201 | /// xyzzy.push(ch.get()); 202 | /// } 203 | /// } 204 | /// // Now we've read in all the values in sorted order. 205 | /// assert_eq!(&['x' as u32, 'y' as u32, 'z' as u32], 206 | /// &xyzzy[..]); 207 | /// } 208 | /// txn.commit().unwrap(); 209 | /// # } 210 | /// ``` 211 | const DUPFIXED = ffi::MDB_DUPFIXED; 212 | /// This option specifies that duplicate data items are binary 213 | /// integers, similar to `INTEGERKEY` keys. 214 | const INTEGERDUP = ffi::MDB_INTEGERDUP; 215 | /// This option specifies that duplicate data items should be 216 | /// compared as strings in reverse order. 217 | /// 218 | /// NOTE: As with `REVERSEKEY`, this indicates a right-to-left 219 | /// comparison, *not* an order reversal. 220 | /// 221 | /// ## Example 222 | /// 223 | /// ``` 224 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 225 | /// # fn main() { 226 | /// # let env = create_env(); 227 | /// let db = lmdb::Database::open( 228 | /// &env, Some("reversed"), &lmdb::DatabaseOptions::new( 229 | /// lmdb::db::DUPSORT | lmdb::db::REVERSEDUP | 230 | /// lmdb::db::CREATE)).unwrap(); 231 | /// let txn = lmdb::WriteTransaction::new(&env).unwrap(); 232 | /// { 233 | /// let mut access = txn.access(); 234 | /// let f = lmdb::put::Flags::empty(); 235 | /// access.put(&db, "Colorado", "Denver", f).unwrap(); 236 | /// access.put(&db, "Colorado", "Golden", f).unwrap(); 237 | /// access.put(&db, "Colorado", "Lakewood", f).unwrap(); 238 | /// 239 | /// let mut cursor = txn.cursor(&db).unwrap(); 240 | /// // doowekaL, nedloG, revneD 241 | /// assert_eq!(("Colorado", "Lakewood"), cursor.first(&access).unwrap()); 242 | /// assert_eq!(("Colorado", "Golden"), cursor.next(&access).unwrap()); 243 | /// assert_eq!(("Colorado", "Denver"), cursor.next(&access).unwrap()); 244 | /// } 245 | /// txn.commit().unwrap(); 246 | /// # } 247 | /// ``` 248 | const REVERSEDUP = ffi::MDB_REVERSEDUP; 249 | /// Create the named database if it doesn't exist. This option is 250 | /// not allowed in a read-only environment. 251 | const CREATE = ffi::MDB_CREATE; 252 | } 253 | } 254 | } 255 | 256 | #[derive(Debug)] 257 | struct DbHandle<'a> { 258 | env: Supercow<'a, Environment>, 259 | dbi: ffi::MDB_dbi, 260 | close_on_drop: bool, 261 | } 262 | 263 | impl<'a> Drop for DbHandle<'a> { 264 | fn drop(&mut self) { 265 | if self.close_on_drop { 266 | env::dbi_close(&self.env, self.dbi); 267 | } 268 | } 269 | } 270 | 271 | /// A handle on an LMDB database within an environment. 272 | /// 273 | /// Note that in many respects the RAII aspect of this struct is more a matter 274 | /// of convenience than correctness. In particular, if holding a read 275 | /// transaction open, it is possible to obtain a handle to a database created 276 | /// after that transaction started, but this handle will not point to anything 277 | /// within that transaction. 278 | /// 279 | /// The library does, however, guarantee that there will be at most one 280 | /// `Database` object with the same dbi and environment per process. 281 | /// 282 | /// ## Lifetime 283 | /// 284 | /// A `Database` in borrowed mode must be strictly outlived by its 285 | /// `Environment`. 286 | /// 287 | /// `'a` is covariant: given two lifetimes `'x` and `'y` where `'x: 'y`, a 288 | /// `&Database<'x>` will implicitly coerce to `&Database<'y>`. 289 | /// 290 | /// ```rust,norun 291 | /// # #![allow(dead_code)] 292 | /// # extern crate lmdb_zero as lmdb; 293 | /// # fn main() { } 294 | /// # 295 | /// fn convariance<'x, 'y>(db: &lmdb::Database<'x>) 296 | /// where 'x: 'y { 297 | /// let _db2: &lmdb::Database<'y> = db; 298 | /// } 299 | /// ``` 300 | /// 301 | /// Because of this property, if you need to hold onto an `&lmdb::Database` and 302 | /// must explicitly name both lifetimes, it is usually best to use the same 303 | /// lifetime for both the reference and the parameter, eg `&'x lmdb::Database<'x>`. 304 | /// 305 | /// ## Ownership Modes 306 | /// 307 | /// All three ownership modes are fully supported. Most examples use borrowed 308 | /// mode, which is used by simply passing an `&'env Environment` to `open`. 309 | /// 310 | /// ### Owned Mode 311 | /// 312 | /// Owned mode is useful when your application only uses one `Database`; this 313 | /// alleviates the need to track both the `Environment` and the `Database`. 314 | /// 315 | /// ``` 316 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 317 | /// fn setup() -> lmdb::Database<'static> { 318 | /// // N.B. Unneeded type and lifetime annotations included for clarity. 319 | /// let env: lmdb::Environment = create_env(); 320 | /// // Move `env` into the new `Database` because we only want to use the 321 | /// // default database. Since it owns the `Environment`, its lifetime 322 | /// // parameter is simply `'static`. 323 | /// let db: lmdb::Database<'static> = lmdb::Database::open( 324 | /// env, None, &lmdb::DatabaseOptions::defaults()).unwrap(); 325 | /// // And since it owns the `Environment`, we can even return it without 326 | /// // worrying about `env`. 327 | /// db 328 | /// } 329 | /// 330 | /// # fn main() { 331 | /// let db = setup(); 332 | /// // Do stuff with `db`... 333 | /// 334 | /// // When `db` is dropped, so is the inner `Environment`. 335 | /// # } 336 | /// ``` 337 | /// 338 | /// ### Shared Mode 339 | /// 340 | /// Shared mode allows to have the `Database` hold on to the `Environment` via 341 | /// an `Arc` instead of a bare reference. This has all the benefits of owned 342 | /// mode and none of the drawbacks, but makes it harder to determine when 343 | /// exactly the `Environment` gets dropped since this only happens after all 344 | /// referents are (dynamically) dropped. 345 | /// 346 | /// Without resorting to `unsafe`, shared mode is also the only way to define a 347 | /// structure which holds both the `Environment` itself and its child 348 | /// `Database` values. 349 | /// 350 | /// ``` 351 | /// # #![allow(dead_code)] 352 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 353 | /// use std::sync::Arc; 354 | /// 355 | /// struct ApplicationContext { 356 | /// env: Arc, 357 | /// // You could of course also put these under `Arc`s as well, for example 358 | /// // if using shared mode with transactions and/or cursors. 359 | /// dict: lmdb::Database<'static>, 360 | /// freq: lmdb::Database<'static>, 361 | /// } 362 | /// 363 | /// impl ApplicationContext { 364 | /// fn into_env(self) -> Arc { self.env } 365 | /// } 366 | /// 367 | /// # fn main() { 368 | /// let env = Arc::new(create_env()); 369 | /// let dict = lmdb::Database::open( 370 | /// env.clone(), Some("dict"), 371 | /// &lmdb::DatabaseOptions::create_map::()).unwrap(); 372 | /// let freq = lmdb::Database::open( 373 | /// env.clone(), Some("freq"), 374 | /// &lmdb::DatabaseOptions::create_map::()).unwrap(); 375 | /// 376 | /// let context = ApplicationContext { 377 | /// env: env, 378 | /// dict: dict, 379 | /// freq: freq, 380 | /// }; 381 | /// 382 | /// // Pass `context` around the application freely... 383 | /// 384 | /// // We could just let `ApplicationContext` drop, but if we want to be 385 | /// // absolutely sure we know when the `Environment` drops (by panicking if 386 | /// // it doesn't do so when we want), we can disassemble the struct and check 387 | /// // manually. 388 | /// let env = context.into_env(); // Databases get dropped 389 | /// Arc::try_unwrap(env).unwrap(); // Regain ownership of `Environment`, 390 | /// // then drop it. 391 | /// # } 392 | /// ``` 393 | #[derive(Debug)] 394 | pub struct Database<'a> { 395 | db: DbHandle<'a>, 396 | } 397 | 398 | /// Describes the options used for creating or opening a database. 399 | #[derive(Clone,Debug)] 400 | pub struct DatabaseOptions { 401 | /// The integer flags to pass to LMDB 402 | pub flags: db::Flags, 403 | key_cmp: Option, 404 | val_cmp: Option, 405 | } 406 | 407 | impl DatabaseOptions { 408 | /// Creates a new `DatabaseOptions` with the given flags, using natural key 409 | /// and value ordering. 410 | pub fn new(flags: db::Flags) -> DatabaseOptions { 411 | DatabaseOptions { 412 | flags: flags, 413 | key_cmp: None, 414 | val_cmp: None, 415 | } 416 | } 417 | 418 | /// Synonym for `DatabaseOptions::new(db::Flags::empty())`. 419 | /// 420 | /// Note that this does not even have `db::CREATE` set. This is most useful 421 | /// when opening the anonymous default database. 422 | pub fn defaults() -> DatabaseOptions { 423 | DatabaseOptions::new(db::Flags::empty()) 424 | } 425 | 426 | /// Sorts keys in the database by interpreting them as `K` and using their 427 | /// comparison order. Keys which fail to convert are considered equal. 428 | /// 429 | /// The comparison function is called whenever it is necessary to compare a 430 | /// key specified by the application with a key currently stored in the 431 | /// database. If no comparison function is specified, and no special key 432 | /// flags were specified in the options, the keys are compared lexically, 433 | /// with shorter keys collating before longer keys. 434 | /// 435 | /// ## Warning 436 | /// 437 | /// This function must be called before any data access functions are used, 438 | /// otherwise data corruption may occur. The same comparison function must 439 | /// be used by every program accessing the database, every time the 440 | /// database is used. 441 | /// 442 | /// ## Example 443 | /// 444 | /// ``` 445 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 446 | /// #[repr(C, packed)] 447 | /// #[derive(Clone,Copy,Debug,PartialEq,Eq,PartialOrd,Ord)] 448 | /// struct MyStruct { 449 | /// x: i32, 450 | /// y: i32, 451 | /// } 452 | /// unsafe impl lmdb::traits::LmdbRaw for MyStruct { } 453 | /// unsafe impl lmdb::traits::LmdbOrdKey for MyStruct { } 454 | /// 455 | /// fn my(x: i32, y: i32) -> MyStruct { 456 | /// MyStruct { x: x, y: y } 457 | /// } 458 | /// 459 | /// # fn main() { 460 | /// # let env = create_env(); 461 | /// let mut opts = lmdb::DatabaseOptions::new(lmdb::db::CREATE); 462 | /// opts.sort_keys_as::(); 463 | /// let db = lmdb::Database::open(&env, Some("example"), &opts).unwrap(); 464 | /// let txn = lmdb::WriteTransaction::new(&env).unwrap(); 465 | /// { 466 | /// let mut access = txn.access(); 467 | /// let f = lmdb::put::Flags::empty(); 468 | /// access.put(&db, &my(0, 0), "origin", f).unwrap(); 469 | /// access.put(&db, &my(-1, 0), "x=-1", f).unwrap(); 470 | /// access.put(&db, &my(1, 0), "x=+1", f).unwrap(); 471 | /// access.put(&db, &my(0, -1), "y=-1", f).unwrap(); 472 | /// 473 | /// let mut cursor = txn.cursor(&db).unwrap(); 474 | /// // The keys are sorted by the Rust-derived comparison. The default 475 | /// // byte-string comparison would have resulted in (0,0), (0,-1), 476 | /// // (1,0), (-1,0). 477 | /// assert_eq!((&my(-1, 0), "x=-1"), cursor.first(&access).unwrap()); 478 | /// assert_eq!((&my(0, -1), "y=-1"), cursor.next(&access).unwrap()); 479 | /// assert_eq!((&my(0, 0), "origin"), cursor.next(&access).unwrap()); 480 | /// assert_eq!((&my(1, 0), "x=+1"), cursor.next(&access).unwrap()); 481 | /// } 482 | /// txn.commit().unwrap(); 483 | /// # } 484 | pub fn sort_keys_as(&mut self) { 485 | self.key_cmp = Some(DatabaseOptions::entry_cmp_as::); 486 | } 487 | 488 | /// Sorts duplicate values in the database by interpreting them as `V` and 489 | /// using their comparison order. Values which fail to convert are 490 | /// considered equal. 491 | /// 492 | /// This comparison function is called whenever it is necessary to compare 493 | /// a data item specified by the application with a data item currently 494 | /// stored in the database. This function only takes effect if the database 495 | /// iss opened with the `DUPSORT` flag. If no comparison function is 496 | /// specified, and no special key flags were specified in the flags of 497 | /// these options, the data items are compared lexically, with shorter 498 | /// items collating before longer items. 499 | /// 500 | /// ## Warning 501 | /// 502 | /// This function must be called before any data access functions are used, 503 | /// otherwise data corruption may occur. The same comparison function must 504 | /// be used by every program accessing the database, every time the 505 | /// database is used. 506 | pub fn sort_values_as(&mut self) { 507 | self.val_cmp = Some(DatabaseOptions::entry_cmp_as::); 508 | } 509 | 510 | /// Concisely creates a `DatabaseOptions` to configure a database to have a 511 | /// 1:1 mapping using the given key type. 512 | /// 513 | /// The flags always have `db::CREATE` set. If `K` is understood by LMDB as 514 | /// an integer, `db::INTEGERKEY` is set. Otherwise, unless `K` sorts 515 | /// properly via byte-string comparison, `sort_keys_as` is called to 516 | /// configure the database to use `K`'s `Ord` implementation. 517 | pub fn create_map() -> Self { 518 | let mut this = DatabaseOptions::new(db::CREATE); 519 | if K::ordered_as_integer() { 520 | this.flags |= db::INTEGERKEY; 521 | } else if !K::ordered_by_bytes() { 522 | this.sort_keys_as::(); 523 | } 524 | this 525 | } 526 | 527 | /// Concisely creates a `DatabaseOptions` to configure a database to have a 528 | /// 1:M mapping using the given key and unsized value types. 529 | /// 530 | /// The flags are configured as described with `create_map` with 531 | /// `db::DUPSORT` added. If `V` is understood by LMDB as an integer, 532 | /// `INTEGERDUP` is set. Otherwise, if `V` is not byte-string comparable, 533 | /// `sort_values_as` is used to order values by `V`'s `Ord` 534 | /// implementation. 535 | pub fn create_multimap_unsized 537 | () -> Self 538 | { 539 | let mut this = DatabaseOptions::create_map::(); 540 | this.flags |= db::DUPSORT; 541 | if V::ordered_as_integer() { 542 | this.flags |= db::INTEGERDUP; 543 | } else if !V::ordered_by_bytes() { 544 | this.sort_values_as::(); 545 | } 546 | this 547 | } 548 | 549 | /// Concisely creates a `DatabaseOptions` to configure a database to have a 550 | /// 1:M mapping using the given key and fixed-size value types. 551 | /// 552 | /// This is the same as `create_multimap_unsized`, except that `DUPFIXED` 553 | /// is additionally set unconditionally. 554 | pub fn create_multimap 556 | () -> Self 557 | { 558 | let mut this = DatabaseOptions::create_multimap_unsized::(); 559 | this.flags |= db::DUPFIXED; 560 | this 561 | } 562 | 563 | extern fn entry_cmp_as( 564 | ap: *const ffi::MDB_val, bp: *const ffi::MDB_val) -> c_int 565 | { 566 | match unsafe { 567 | V::from_lmdb_bytes(mdb_val_as_bytes(&ap, &*ap)).cmp( 568 | &V::from_lmdb_bytes(mdb_val_as_bytes(&bp, &*bp))) 569 | } { 570 | Ordering::Less => -1, 571 | Ordering::Equal => 0, 572 | Ordering::Greater => 1, 573 | } 574 | } 575 | } 576 | 577 | impl<'a> Database<'a> { 578 | /// Open a database in the environment. 579 | /// 580 | /// A database handle denotes the name and parameters of a database, 581 | /// independently of whether such a database exists. The database handle is 582 | /// implicitly closed when the `Database` object is dropped. 583 | /// 584 | /// To use named databases (with `name != None`), 585 | /// `EnvBuilder::set_maxdbs()` must have been called to reserve space for 586 | /// the extra databases. Database names are keys in the unnamed database, 587 | /// and may be read but not written. 588 | /// 589 | /// Transaction-local databases are not supported because the resulting 590 | /// ownership semantics are not expressible in rust. This call implicitly 591 | /// creates a write transaction and uses it to create the database, then 592 | /// commits it on success. 593 | /// 594 | /// One may not open the same database handle multiple times. Attempting to 595 | /// do so will result in the `Error::Reopened` error. 596 | /// 597 | /// Beware that if the underlying `MDB_env` is being shared (for example, 598 | /// with native code via `Environment::borrow_raw`), using this method to 599 | /// open a `Database` can result in premature closing of the database 600 | /// handle since the `Database` is presumed to own the database handle, but 601 | /// LMDB will return a shared handle if the database is already open 602 | /// elsewhere. The [`disown()`](#method.disown) method can be used to 603 | /// ensure the `Database` does not assume ownership of the database 604 | /// handle. 605 | /// 606 | /// ## Examples 607 | /// 608 | /// ### Open the default database with default options 609 | /// 610 | /// ``` 611 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 612 | /// # #[allow(unused_vars)] 613 | /// # fn main() { 614 | /// # let env = create_env(); 615 | /// { 616 | /// let db = lmdb::Database::open( 617 | /// &env, None, &lmdb::DatabaseOptions::defaults()).unwrap(); 618 | /// // Do stuff with `db` 619 | /// } // The `db` handle is released 620 | /// # } 621 | /// ``` 622 | /// 623 | /// ### Open a named database, creating it if it doesn't exist 624 | /// 625 | /// ``` 626 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 627 | /// # #[allow(unused_vars)] 628 | /// # fn main() { 629 | /// # let env = create_env(); 630 | /// // NOT SHOWN: Call `EnvBuilder::set_maxdbs()` with a value greater than 631 | /// // one so that there is space for the named database(s). 632 | /// { 633 | /// let db = lmdb::Database::open( 634 | /// &env, Some("example-db"), &lmdb::DatabaseOptions::new( 635 | /// lmdb::db::CREATE)).unwrap(); 636 | /// // Do stuff with `db` 637 | /// } // The `db` handle is released 638 | /// # } 639 | /// ``` 640 | /// 641 | /// ### Trying to open the same database more than once 642 | /// ``` 643 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 644 | /// # #[allow(unused_vars)] 645 | /// # fn main() { 646 | /// # let env = create_env(); 647 | /// { 648 | /// let db = lmdb::Database::open( 649 | /// &env, None, &lmdb::DatabaseOptions::defaults()).unwrap(); 650 | /// // Can't open the same database twice 651 | /// assert!(lmdb::Database::open( 652 | /// &env, None, &lmdb::DatabaseOptions::defaults()).is_err()); 653 | /// } 654 | /// # } 655 | /// ``` 656 | /// 657 | /// ### Open a database on a read-only environment 658 | /// 659 | /// Databases can be opened in read-only environments as long as they 660 | /// already exist. 661 | /// 662 | /// ``` 663 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 664 | /// # fn main() { 665 | /// # let tdir = tempdir::TempDir::new_in(".", "lmdbzero").unwrap(); 666 | /// # { 667 | /// # let mut builder = lmdb::EnvBuilder::new().unwrap(); 668 | /// # builder.set_maxdbs(2).unwrap(); 669 | /// # let env = unsafe { builder.open( 670 | /// # tdir.path().to_str().unwrap(), 671 | /// # lmdb::open::Flags::empty(), 0o600).unwrap() }; 672 | /// # let db = lmdb::Database::open( 673 | /// # &env, None, &lmdb::DatabaseOptions::defaults()).unwrap(); 674 | /// # } 675 | /// # let env = { 676 | /// # let mut builder = lmdb::EnvBuilder::new().unwrap(); 677 | /// # builder.set_maxdbs(2).unwrap(); 678 | /// # unsafe { builder.open( 679 | /// # tdir.path().to_str().unwrap(), 680 | /// # lmdb::open::RDONLY, 0o400).unwrap() } 681 | /// # }; 682 | /// { 683 | /// // Succeeds -- The DB already exists 684 | /// let db = lmdb::Database::open( 685 | /// &env, None, 686 | /// &lmdb::DatabaseOptions::new(lmdb::db::Flags::empty())).unwrap(); 687 | /// // Fails -- Can't create a new one in read-only mode 688 | /// assert!(lmdb::Database::open( 689 | /// &env, Some("name"), 690 | /// &lmdb::DatabaseOptions::new(lmdb::db::CREATE)).is_err()); 691 | /// } 692 | /// # } 693 | /// ``` 694 | pub fn open(env: E, name: Option<&str>, 695 | options: &DatabaseOptions) 696 | -> Result> 697 | where E : Into> { 698 | let env: Supercow<'a, Environment> = env.into(); 699 | 700 | let mut raw: ffi::MDB_dbi = 0; 701 | let name_cstr = match name { 702 | None => None, 703 | Some(s) => Some(try!(CString::new(s))), 704 | }; 705 | let raw = unsafe { 706 | use env; 707 | // Locking the hash set here is also used to serialise calls to 708 | // `mdb_dbi_open()`, which are not permitted to be concurrent. 709 | let mut locked_dbis = env::env_open_dbis(&env).lock() 710 | .expect("open_dbis lock poisoned"); 711 | 712 | let mut raw_tx: *mut ffi::MDB_txn = ptr::null_mut(); 713 | let mut txn_flags = 0; 714 | if env.flags().unwrap().contains(env::open::RDONLY) { 715 | txn_flags = ffi::MDB_RDONLY; 716 | } 717 | lmdb_call!(ffi::mdb_txn_begin( 718 | env::env_ptr(&env), ptr::null_mut(), txn_flags, &mut raw_tx)); 719 | let mut wrapped_tx = TxHandle(raw_tx); // For auto-closing etc 720 | lmdb_call!(ffi::mdb_dbi_open( 721 | raw_tx, name_cstr.as_ref().map_or(ptr::null(), |s| s.as_ptr()), 722 | options.flags.bits(), &mut raw)); 723 | 724 | if !locked_dbis.insert(raw) { 725 | return Err(Error::Reopened) 726 | } 727 | 728 | if let Some(fun) = options.key_cmp { 729 | lmdb_call!(ffi::mdb_set_compare(raw_tx, raw, fun)); 730 | } 731 | if let Some(fun) = options.val_cmp { 732 | lmdb_call!(ffi::mdb_set_dupsort(raw_tx, raw, fun)); 733 | } 734 | 735 | try!(wrapped_tx.commit()); 736 | raw 737 | }; 738 | 739 | Ok(Database { 740 | db: DbHandle { 741 | env: env, 742 | dbi: raw, 743 | close_on_drop: true, 744 | } 745 | }) 746 | } 747 | 748 | /// Wrap a raw `MDB_dbi` associated with this environment. 749 | /// 750 | /// This call assumes ownership of the `MDB_dbi`, and the resulting 751 | /// `Database` will close it on drop. If this is not desired, see 752 | /// [`borrow_raw()`](#method.borrow_raw) instead. 753 | /// 754 | /// ## Unsafety 755 | /// 756 | /// `raw` must reference a database currently open in `env`. 757 | /// 758 | /// The caller must ensure that nothing closes the handle until 759 | /// `into_raw()` is called and that nothing uses it after the `Database` is 760 | /// dropped normally. 761 | /// 762 | /// ## Panics 763 | /// 764 | /// Panics if `raw` is a handle already owned by `env`. 765 | pub unsafe fn from_raw(env: E, raw: ffi::MDB_dbi) -> Self 766 | where E : Into> { 767 | let env: Supercow<'a, Environment> = env.into(); 768 | 769 | use env; 770 | { 771 | let mut locked_dbis = env::env_open_dbis(&env).lock() 772 | .expect("open_dbis lock poisoned"); 773 | 774 | if !locked_dbis.insert(raw) { 775 | panic!("DBI {} already opened in this environment", raw); 776 | } 777 | } 778 | 779 | Database { 780 | db: DbHandle { 781 | env: env, 782 | dbi: raw, 783 | close_on_drop: true, 784 | } 785 | } 786 | } 787 | 788 | /// Wrap a raw `MDB_dbi` associated with this environment without taking 789 | /// ownership. 790 | /// 791 | /// This call does not assume ownership of `raw`, and as a result neither 792 | /// checks whether any other `Database`s exist with the same handle, nor 793 | /// records this particular handle in `env`'s list of owned databases. 794 | /// 795 | /// ## Unsafety 796 | /// 797 | /// `raw` must reference a database currently open in `env`. 798 | /// 799 | /// The caller must ensure that nothing closes the handle until the 800 | /// resulting `Database` is dropped. 801 | pub unsafe fn borrow_raw(env: E, raw: ffi::MDB_dbi) -> Self 802 | where E : Into> { 803 | Database { 804 | db: DbHandle { 805 | env: env.into(), 806 | dbi: raw, 807 | close_on_drop: false, 808 | } 809 | } 810 | } 811 | 812 | /// Deletes this database. 813 | /// 814 | /// This call implicitly creates a new write transaction to perform the 815 | /// operation, so that the lifetime of the database handle does not depend 816 | /// on the outcome. The database handle is closed implicitly by this 817 | /// operation. 818 | /// 819 | /// Note that the _other_ `mdb_drop` operation which simply clears the 820 | /// database is exposed through `WriteAccessor` and is transactional. 821 | /// 822 | /// ## Example 823 | /// 824 | /// ``` 825 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 826 | /// # #[allow(unused_vars)] 827 | /// # fn main() { 828 | /// # let env = create_env(); 829 | /// // NOT SHOWN: Call `EnvBuilder::set_maxdbs()` with a value greater than 830 | /// // one so that there is space for the named database(s). 831 | /// { 832 | /// let db = lmdb::Database::open( 833 | /// &env, Some("example-db"), &lmdb::DatabaseOptions::new( 834 | /// lmdb::db::CREATE)).unwrap(); 835 | /// // Do stuff with `db` 836 | /// 837 | /// // Delete the database itself. This also consumes `db`. 838 | /// db.delete().unwrap(); 839 | /// 840 | /// // We can now recreate the database if we so desire. 841 | /// // Note that you should not use delete+open to clear a database; use 842 | /// // `WriteAccessor::clear_db()` to do that. 843 | /// let db = lmdb::Database::open( 844 | /// &env, Some("example-db"), &lmdb::DatabaseOptions::new( 845 | /// lmdb::db::CREATE)).unwrap(); 846 | /// } 847 | /// # } 848 | /// ``` 849 | pub fn delete(self) -> Result<()> { 850 | try!(env::dbi_delete(&self.db.env, self.db.dbi)); 851 | mem::forget(self.db); 852 | Ok(()) 853 | } 854 | 855 | /// Returns a reference to the `Environment` to which this `Database` 856 | /// belongs. 857 | /// 858 | /// This can be used to elide needing to pass both an `&Environment` and an 859 | /// `&Database` around, but is also useful for the use-case wherein the 860 | /// `Database` owns the `Environment`. 861 | /// 862 | /// Because this may borrow an `Environment` owned by this `Database`, the 863 | /// lifetime of the returned reference is dependent on self rather than 864 | /// being `'env`. (In fact, `'env` is usually `'static` if the 865 | /// `Environment` is owned by the `Database`, so returning `&'env Environment` 866 | /// is impossible anyway.) 867 | /// 868 | /// ## Example 869 | /// 870 | /// ``` 871 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 872 | /// # #[allow(unused_vars)] 873 | /// # fn main() { 874 | /// let env: lmdb::Environment = create_env(); 875 | /// // We only want one `Database`, so don't bother keeping both variables 876 | /// // around and instead let the `Database` own the `Environment`. 877 | /// let db = lmdb::Database::open( 878 | /// env, None, &lmdb::DatabaseOptions::defaults()).unwrap(); 879 | /// 880 | /// // `env` has been consumed, but we can still do useful things by 881 | /// // getting a reference to the inner value. 882 | /// let txn = lmdb::ReadTransaction::new(db.env()).unwrap(); 883 | /// 884 | /// // Do stuff with `txn`, etc. 885 | /// # } 886 | /// ``` 887 | #[inline] 888 | pub fn env(&self) -> &Environment { 889 | &*self.db.env 890 | } 891 | 892 | /// Checks that `other_env` is the same as the environment on this 893 | /// `Database`. 894 | /// 895 | /// If it matches, returns `Ok(())`; otherwise, returns `Err`. 896 | pub fn assert_same_env(&self, other_env: &Environment) 897 | -> Result<()> { 898 | if &*self.db.env as *const Environment != 899 | other_env as *const Environment 900 | { 901 | Err(Error::Mismatch) 902 | } else { 903 | Ok(()) 904 | } 905 | } 906 | 907 | /// Returns the underlying integer handle for this database. 908 | /// 909 | /// ## Deprecated 910 | /// 911 | /// Renamed to `as_raw()` for consistency. 912 | #[deprecated(since = "0.4.4", note = "use as_raw() instead")] 913 | pub fn dbi(&self) -> ffi::MDB_dbi { 914 | self.db.dbi 915 | } 916 | 917 | /// Returns the underlying integer handle for this database. 918 | #[inline] 919 | pub fn as_raw(&self) -> ffi::MDB_dbi { 920 | self.db.dbi 921 | } 922 | 923 | /// Consume self, returning the underlying integer handle for this 924 | /// database. 925 | /// 926 | /// If this `Database` owns the database handle, it is not closed, but it 927 | /// is removed from the list of handles owned by the `Environment`. 928 | pub fn into_raw(mut self) -> ffi::MDB_dbi { 929 | self.disown(); 930 | self.db.dbi 931 | } 932 | 933 | /// Prevent the underlying `MDB_dbi` handle from being closed when this 934 | /// `Database` is dropped. 935 | /// 936 | /// This is useful when sharing an `MDB_env` with a native library, as in 937 | /// such a context the `MDB_dbi` handles are also involuntarily shared. 938 | pub fn disown(&mut self) { 939 | use env; 940 | 941 | if self.db.close_on_drop { 942 | let mut locked_dbis = env::env_open_dbis(&self.db.env).lock() 943 | .expect("open_dbis lock poisoned"); 944 | 945 | locked_dbis.remove(&self.db.dbi); 946 | self.db.close_on_drop = false; 947 | } 948 | } 949 | } 950 | 951 | #[cfg(test)] 952 | mod test { 953 | use test_helpers::*; 954 | use dbi::*; 955 | use tx::*; 956 | 957 | #[test] 958 | fn disown_allows_sharing() { 959 | let env = create_env(); 960 | let mut db1 = defdb(&env); 961 | db1.disown(); 962 | let db2 = defdb(&env); 963 | 964 | let tx = WriteTransaction::new(&env).unwrap(); 965 | tx.access().put(&db1, "foo", "bar", put::Flags::empty()).unwrap(); 966 | tx.commit().unwrap(); 967 | drop(db1); 968 | 969 | let tx = ReadTransaction::new(&env).unwrap(); 970 | assert_eq!("bar", tx.access().get::(&db2, "foo").unwrap()); 971 | } 972 | 973 | #[test] 974 | fn borrow_raw_allows_sharing() { 975 | let env = create_env(); 976 | let db1 = defdb(&env); 977 | let db2 = unsafe { Database::borrow_raw(&env, db1.as_raw()) }; 978 | 979 | let tx = WriteTransaction::new(&env).unwrap(); 980 | tx.access().put(&db2, "foo", "bar", put::Flags::empty()).unwrap(); 981 | tx.commit().unwrap(); 982 | drop(db2); 983 | 984 | let tx = ReadTransaction::new(&env).unwrap(); 985 | assert_eq!("bar", tx.access().get::(&db1, "foo").unwrap()); 986 | } 987 | } 988 | -------------------------------------------------------------------------------- /src/env.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 FullContact, Inc 2 | // Copyright 2017, 2018 Jason Lingle 3 | // 4 | // Licensed under the Apache License, Version 2.0 or the MIT license 6 | // , at your 7 | // option. This file may not be copied, modified, or distributed 8 | // except according to those terms. 9 | 10 | use std::collections::HashSet; 11 | use std::ffi::{CStr, CString}; 12 | use std::mem; 13 | use std::ptr; 14 | use std::sync::Mutex; 15 | use libc::{self, c_char, c_int, c_uint, c_void}; 16 | 17 | use ffi; 18 | use ffi2; 19 | use tx::TxHandle; 20 | use ::{Fd, FileMode, Result}; 21 | 22 | /// Flags used when opening an LMDB environment. 23 | pub mod open { 24 | use libc; 25 | use ffi; 26 | 27 | bitflags! { 28 | /// Flags used when opening an LMDB environment. 29 | pub struct Flags : libc::c_uint { 30 | /// Use a fixed address for the mmap region. This flag must be 31 | /// specified when creating the environment, and is stored 32 | /// persistently in the environment. If successful, the memory map 33 | /// will always reside at the same virtual address and pointers 34 | /// used to reference data items in the database will be constant 35 | /// across multiple invocations. This option may not always work, 36 | /// depending on how the operating system has allocated memory to 37 | /// shared libraries and other uses. The feature is highly 38 | /// experimental. 39 | const FIXEDMAP = ffi::MDB_FIXEDMAP; 40 | /// By default, LMDB creates its environment in a directory whose 41 | /// pathname is given in path, and creates its data and lock files 42 | /// under that directory. With this option, the `path` passed to 43 | /// `EnvBuilder::open` is used as-is for the database main data 44 | /// file. The database lock file is the path with "-lock" appended. 45 | const NOSUBDIR = ffi::MDB_NOSUBDIR; 46 | /// Open the environment in read-only mode. No write operations 47 | /// will be allowed. LMDB will still modify the lock file - except 48 | /// on read-only filesystems, where LMDB does not use locks. 49 | const RDONLY = ffi::MDB_RDONLY; 50 | /// Use a writeable memory map unless `RDONLY` is set. This is 51 | /// faster and uses fewer mallocs, but loses protection from 52 | /// application bugs like wild pointer writes and other bad updates 53 | /// into the database. Incompatible with nested transactions. Do 54 | /// not mix processes with and without `WRITEMAP` on the same 55 | /// environment. This can defeat durability (`Environment::sync` 56 | /// etc). 57 | const WRITEMAP = ffi::MDB_WRITEMAP; 58 | /// Flush system buffers to disk only once per transaction, omit 59 | /// the metadata flush. Defer that until the system flushes files 60 | /// to disk, or next non-`RDONLY` commit or `Environment::sync()`. 61 | /// This optimization maintains database integrity, but a system 62 | /// crash may undo the last committed transaction. I.e. it 63 | /// preserves the ACI (atomicity, consistency, isolation) but not D 64 | /// (durability) database property. This flag may be changed at any 65 | /// time using `Environment::set_flags()`. 66 | const NOMETASYNC = ffi::MDB_NOMETASYNC; 67 | /// Don't flush system buffers to disk when committing a 68 | /// transaction. This optimization means a system crash can corrupt 69 | /// the database or lose the last transactions if buffers are not 70 | /// yet flushed to disk. The risk is governed by how often the 71 | /// system flushes dirty buffers to disk and how often 72 | /// `Environment::sync()` is called. However, if the filesystem 73 | /// preserves write order and the `WRITEMAP` flag is not used, 74 | /// transactions exhibit ACI (atomicity, consistency, isolation) 75 | /// properties and only lose D (durability). I.e. database 76 | /// integrity is maintained, but a system crash may undo the final 77 | /// transactions. Note that `(NOSYNC | WRITEMAP)` leaves the system 78 | /// with no hint for when to write transactions to disk, unless 79 | /// `Environment::sync()` is called. `(MAPASYNC | WRITEMAP)` may be 80 | /// preferable. This flag may be changed at any time using 81 | /// `Environment::set_flags()`. 82 | const NOSYNC = ffi::MDB_NOSYNC; 83 | /// When using `WRITEMAP`, use asynchronous flushes to disk. As 84 | /// with `NOSYNC`, a system crash can then corrupt the database or 85 | /// lose the last transactions. Calling `Environment::sync()` 86 | /// ensures on-disk database integrity until next commit. This flag 87 | /// may be changed at any time using `Environment::set_flags()`. 88 | const MAPASYNC = ffi::MDB_MAPASYNC; 89 | /// Don't use Thread-Local Storage. Tie reader locktable slots to 90 | /// transaction objects instead of to threads. I.e. 91 | /// `Transaction::reset()` keeps the slot reseved for the 92 | /// transaction object. A thread may use parallel read-only 93 | /// transactions. A read-only transaction may span threads if the 94 | /// user synchronizes its use. Applications that multiplex many 95 | /// user threads over individual OS threads need this option. Such 96 | /// an application must also serialize the write transactions in an 97 | /// OS thread, since LMDB's write locking is unaware of the user 98 | /// threads. 99 | const NOTLS = ffi::MDB_NOTLS; 100 | /// Don't do any locking. If concurrent access is anticipated, the 101 | /// caller must manage all concurrency itself. For proper operation 102 | /// the caller must enforce single-writer semantics, and must 103 | /// ensure that no readers are using old transactions while a 104 | /// writer is active. The simplest approach is to use an exclusive 105 | /// lock so that no readers may be active at all when a writer 106 | /// begins. 107 | const NOLOCK = ffi::MDB_NOLOCK; 108 | /// Turn off readahead. Most operating systems perform readahead on 109 | /// read requests by default. This option turns it off if the OS 110 | /// supports it. Turning it off may help random read performance 111 | /// when the DB is larger than RAM and system RAM is full. The 112 | /// option is not implemented on Windows. 113 | const NORDAHEAD = ffi::MDB_NORDAHEAD; 114 | /// Don't initialize malloc'd memory before writing to unused 115 | /// spaces in the data file. By default, memory for pages written 116 | /// to the data file is obtained using malloc. While these pages 117 | /// may be reused in subsequent transactions, freshly malloc'd 118 | /// pages will be initialized to zeroes before use. This avoids 119 | /// persisting leftover data from other code (that used the heap 120 | /// and subsequently freed the memory) into the data file. Note 121 | /// that many other system libraries may allocate and free memory 122 | /// from the heap for arbitrary uses. E.g., stdio may use the heap 123 | /// for file I/O buffers. This initialization step has a modest 124 | /// performance cost so some applications may want to disable it 125 | /// using this flag. This option can be a problem for applications 126 | /// which handle sensitive data like passwords, and it makes memory 127 | /// checkers like Valgrind noisy. This flag is not needed with 128 | /// `WRITEMAP`, which writes directly to the mmap instead of using 129 | /// malloc for pages. The initialization is also skipped if 130 | /// `RESERVE` is used; the caller is expected to overwrite all of 131 | /// the memory that was reserved in that case. This flag may be 132 | /// changed at any time using `Environment::set_flags()`. 133 | const NOMEMINIT = ffi::MDB_NOMEMINIT; 134 | } 135 | } 136 | } 137 | 138 | /// Flags used when copying an LMDB environment. 139 | pub mod copy { 140 | use ffi2; 141 | use libc; 142 | 143 | bitflags! { 144 | /// Flags used when copying an LMDB environment. 145 | pub struct Flags : libc::c_uint { 146 | /// Perform compaction while copying: omit free pages and sequentially 147 | /// renumber all pages in output. This option consumes more CPU and 148 | /// runs more slowly than the default. 149 | const COMPACT = ffi2::MDB_CP_COMPACT; 150 | } 151 | } 152 | } 153 | 154 | #[derive(Debug)] 155 | struct EnvHandle(*mut ffi::MDB_env, bool); 156 | 157 | unsafe impl Sync for EnvHandle { } 158 | unsafe impl Send for EnvHandle { } 159 | impl Drop for EnvHandle { 160 | fn drop(&mut self) { 161 | if self.1 { 162 | unsafe { 163 | ffi::mdb_env_close(self.0) 164 | } 165 | } 166 | } 167 | } 168 | 169 | 170 | /// Handle on an uninitialised LMDB environment to allow configuring pre-open 171 | /// options. 172 | #[derive(Debug)] 173 | pub struct EnvBuilder { 174 | env: EnvHandle, 175 | } 176 | 177 | impl EnvBuilder { 178 | /// Allocates a new, uninitialised environment. 179 | pub fn new() -> Result { 180 | let mut env: *mut ffi::MDB_env = ptr::null_mut(); 181 | unsafe { 182 | lmdb_call!(ffi::mdb_env_create(&mut env)); 183 | Ok(EnvBuilder { env: EnvHandle(env, true) }) 184 | } 185 | } 186 | 187 | /// Set the size of the memory map to use for this environment. 188 | /// 189 | /// The size should be a multiple of the OS page size. The default is 190 | /// 10485760 bytes. The size of the memory map is also the maximum size of 191 | /// the database. The value should be chosen as large as possible, to 192 | /// accommodate future growth of the database. 193 | /// 194 | /// The new size takes effect immediately for the current process but will 195 | /// not be persisted to any others until a write transaction has been 196 | /// committed by the current process. Also, only mapsize increases are 197 | /// persisted into the environment. 198 | /// 199 | /// See also `Environment::set_mapsize()`. 200 | pub fn set_mapsize(&mut self, size: usize) -> Result<()> { 201 | unsafe { 202 | lmdb_call!(ffi::mdb_env_set_mapsize( 203 | self.env.0, size as libc::size_t)); 204 | } 205 | 206 | Ok(()) 207 | } 208 | 209 | /// Set the maximum number of threads/reader slots for the environment. 210 | /// 211 | /// This defines the number of slots in the lock table that is used to 212 | /// track readers in the the environment. The default is 126. Starting a 213 | /// read-only transaction normally ties a lock table slot to the current 214 | /// thread until the environment closes or the thread exits. If `NOTLS` is 215 | /// in use, starting a transaction instead ties the slot to the transaction 216 | /// object until it or the `Environment` object is destroyed. 217 | pub fn set_maxreaders(&mut self, readers: u32) -> Result<()> { 218 | unsafe { 219 | lmdb_call!(ffi::mdb_env_set_maxreaders( 220 | self.env.0, readers as libc::c_uint)); 221 | } 222 | 223 | Ok(()) 224 | } 225 | 226 | /// Set the maximum number of named databases for the environment. 227 | /// 228 | /// This function is only needed if multiple databases will be used in the 229 | /// environment. Simpler applications that use the environment as a single 230 | /// unnamed database can ignore this option. 231 | /// 232 | /// Currently a moderate number of slots are cheap but a huge number gets 233 | /// expensive: 7-120 words per transaction, and every mdb_dbi_open() does a 234 | /// linear search of the opened slots. 235 | pub fn set_maxdbs(&mut self, dbs: u32) -> Result<()> { 236 | unsafe { 237 | lmdb_call!(ffi::mdb_env_set_maxdbs( 238 | self.env.0, dbs as libc::c_uint)); 239 | } 240 | 241 | Ok(()) 242 | } 243 | 244 | /// Opens the file or directory at `path` with the given `flags` and, on 245 | /// UNIX, permissions given by `mode`. 246 | /// 247 | /// `path` is a `&str` for convenience, and must be convertable to a 248 | /// `CString`. The non-use of `&OsStr` or `AsRef` as normally used in 249 | /// `std` is deliberate, since the path must be convertable to a byte 250 | /// string. But as a result there is no way to address files whose names 251 | /// are not valid UTF-8 through this call. 252 | /// 253 | /// ## Unsafety 254 | /// 255 | /// It is the caller's responsibility to ensure that the underlying file 256 | /// will be used properly. Since LMDB is built on memory-mapping, these 257 | /// responsibilities run quite deep and vary based on `flags`. 258 | /// 259 | /// - The caller must ensure that no other process writes to the file in 260 | /// any way except going through LMDB. If this is violated, segfaults can 261 | /// occur, or immutable references can be observed to have their referent 262 | /// mutated asynchronously. 263 | /// 264 | /// - If the caller uses flags which suppress locking, the caller must 265 | /// additionally ensure that LMDB's locking requirements are upheld. 266 | /// 267 | /// - If the caller uses flags that result in the creation of a sparse 268 | /// file, the caller must ensure there is actually sufficient disk space 269 | /// for all pages of the file or else a segfault may occur. 270 | pub unsafe fn open(self, path: &str, flags: open::Flags, 271 | mode: FileMode) -> Result { 272 | let path_cstr = try!(CString::new(path)); 273 | lmdb_call!(ffi::mdb_env_open( 274 | self.env.0, path_cstr.as_ptr(), flags.bits(), mode)); 275 | Ok(Environment { 276 | env: self.env, 277 | open_dbis: Mutex::new(HashSet::new()), 278 | }) 279 | } 280 | } 281 | 282 | /// An LMDB environment which has been opened to a file. 283 | #[derive(Debug)] 284 | pub struct Environment { 285 | env: EnvHandle, 286 | // Track what DBIs are currently in use, so that an open() call that tries 287 | // to duplicate one fails. 288 | open_dbis: Mutex>, 289 | } 290 | 291 | /// Statistics information about an environment. 292 | #[derive(Debug,Clone,Copy)] 293 | pub struct Stat { 294 | /// Size of a database page. This is currently the same for all databases. 295 | pub psize: u32, 296 | /// Depth (height) of the B-tree 297 | pub depth: u32, 298 | /// Number of internal (non-leaf) pages 299 | pub branch_pages: usize, 300 | /// Number of leaf pages 301 | pub leaf_pages: usize, 302 | /// Number of overflow pages 303 | pub overflow_pages: usize, 304 | /// Number of data items 305 | pub entries: usize, 306 | } 307 | 308 | impl From for Stat { 309 | fn from(raw: ffi::MDB_stat) -> Stat { 310 | Stat { 311 | psize: raw.ms_psize as u32, 312 | depth: raw.ms_depth as u32, 313 | branch_pages: raw.ms_branch_pages as usize, 314 | leaf_pages: raw.ms_leaf_pages as usize, 315 | overflow_pages: raw.ms_overflow_pages as usize, 316 | entries: raw.ms_entries as usize, 317 | } 318 | } 319 | } 320 | 321 | /// Configuration information about an environment. 322 | #[derive(Debug,Clone,Copy)] 323 | pub struct EnvInfo { 324 | /// Address of map, if fixed 325 | pub mapaddr: *const c_void, 326 | /// Size of the data memory map 327 | pub mapsize: usize, 328 | /// ID of the last used page 329 | pub last_pgno: usize, 330 | /// ID of the last committed transaction 331 | pub last_txnid: usize, 332 | /// max reader slots in the environment 333 | pub maxreaders: u32, 334 | /// max reader slots used in the environment 335 | pub numreaders: u32, 336 | } 337 | 338 | impl Environment { 339 | /// Wrap a raw LMDB environment handle in an `Environment`. 340 | /// 341 | /// The `Environment` assumes ownership of the `MDB_env`. If you do not 342 | /// want this, see [`borrow_raw()`](#method.borrow_raw) instead. 343 | /// 344 | /// ## Unsafety 345 | /// 346 | /// `env` must point to a valid `MDB_env`. 347 | /// 348 | /// It is the caller's responsibility to ensure that nothing destroys the 349 | /// `MDB_env` before [`into_raw()`](#method.into_raw) is called, and that 350 | /// nothing attempts to use the `MDB_env` after `Environment` is dropped 351 | /// normally. 352 | /// 353 | /// It is safe for multiple `Environment`s bound to the same `MDB_env` to 354 | /// coexist (though the others would need to be created by `borrow_raw`). 355 | /// However, care must be taken when using databases since by default the 356 | /// `Environment` will assume ownership of those as well. 357 | pub unsafe fn from_raw(env: *mut ffi::MDB_env) -> Self { 358 | Environment { 359 | env: EnvHandle(env, true), 360 | open_dbis: Mutex::new(HashSet::new()), 361 | } 362 | } 363 | 364 | /// Wrap a raw LMDB environment handle in an `Environment` without taking 365 | /// ownership. 366 | /// 367 | /// The `Environment` does not assume ownership of the `MDB_env`, and will 368 | /// not destroy it when it is dropped. See [`from_raw()`](#method.from_raw) 369 | /// if taking ownership is desired. 370 | /// 371 | /// Note that this does not affect assumed ownership of `MDB_dbi` handles; 372 | /// databases opened by `Database::open` are still presumed owned. 373 | /// 374 | /// ## Unsafety 375 | /// 376 | /// `env` must point to a valid `MDB_env`. 377 | /// 378 | /// It is safe for multiple `Environment`s bound to the same `MDB_env` to 379 | /// coexist (though the others would need to be created by `borrow_raw`). 380 | /// However, care must be taken when using databases since by default the 381 | /// `Environment` will assume ownership of those as well. 382 | pub unsafe fn borrow_raw(env: *mut ffi::MDB_env) -> Self { 383 | Environment { 384 | env: EnvHandle(env, false), 385 | open_dbis: Mutex::new(HashSet::new()), 386 | } 387 | } 388 | 389 | /// Return the underlying `MDB_env` handle. 390 | /// 391 | /// ## Safety 392 | /// 393 | /// While this call is in and of itself safe, the caller must ensure that 394 | /// operations against the backing store do not violate Rust aliasing 395 | /// rules, and must not take any action that would cause the `MDB_env` to 396 | /// be destroyed prematurely, or to use it after this `Environment` is 397 | /// destroyed. 398 | pub fn as_raw(&self) -> *mut ffi::MDB_env { 399 | self.env.0 400 | } 401 | 402 | /// Consume this `Environment` and return the underlying handle. 403 | /// 404 | /// After this call, it is the caller's responsibility to ensure that the 405 | /// `MDB_env` is eventually destroyed, if it was actually owned by this 406 | /// `Environment` (compare [`from_raw`](#method.from_raw) and 407 | /// [`borrow_raw`](#method.borrow_raw)). 408 | /// 409 | /// ## Safety 410 | /// 411 | /// While this call is in and of itself safe, the caller must ensure that 412 | /// operations against the backing store do not violate Rust aliasing 413 | /// rules. 414 | pub fn into_raw(self) -> *mut ffi::MDB_env { 415 | let ret = self.env.0; 416 | mem::forget(self.env); 417 | ret 418 | } 419 | 420 | /// Copy an LMDB environment to the specified path, with options. 421 | /// 422 | /// This function may be used to make a backup of an existing environment. 423 | /// No lockfile is created, since it gets recreated at need. 424 | /// 425 | /// ## Note 426 | /// 427 | /// This call can trigger significant file size growth if run in parallel 428 | /// with write transactions, because it employs a read-only transaction. 429 | /// See long-lived transactions under Caveats. 430 | /// 431 | /// ## Example 432 | /// 433 | /// ``` 434 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 435 | /// # fn main() { 436 | /// # let env = create_env(); 437 | /// let out = tempdir::TempDir::new_in(".", "lmdbcopy").unwrap(); 438 | /// env.copy(out.path().to_str().unwrap(), 439 | /// lmdb::copy::COMPACT).unwrap(); 440 | /// // We could now open up an independent environment in `lmdbcopyXXXX` 441 | /// // or upload it somewhere, eg, while `env` could continue being 442 | /// // modified concurrently. 443 | /// # } 444 | /// ``` 445 | pub fn copy(&self, path: &str, flags: copy::Flags) -> Result<()> { 446 | let path_cstr = try!(CString::new(path)); 447 | unsafe { 448 | lmdb_call!(ffi2::mdb_env_copy2( 449 | self.env.0, path_cstr.as_ptr(), flags.bits())); 450 | } 451 | Ok(()) 452 | } 453 | 454 | /// Copy an LMDB environment to the specified file descriptor, with options. 455 | /// 456 | /// This function may be used to make a backup of an existing environment. 457 | /// No lockfile is created, since it gets recreated at need. See 458 | /// `copy()` for further details. 459 | /// 460 | /// ## Note 461 | /// 462 | /// This call can trigger significant file size growth if run in parallel 463 | /// with write transactions, because it employs a read-only transaction. 464 | /// See long-lived transactions under Caveats. 465 | pub fn copyfd(&self, fd: Fd, flags: copy::Flags) -> Result<()> { 466 | unsafe { 467 | lmdb_call!(ffi2::mdb_env_copyfd2(self.env.0, fd, flags.bits())); 468 | } 469 | Ok(()) 470 | } 471 | 472 | /// Return statistics about the LMDB environment. 473 | pub fn stat(&self) -> Result { 474 | let raw = unsafe { 475 | let mut raw = mem::zeroed::(); 476 | lmdb_call!(ffi::mdb_env_stat(self.env.0, &mut raw)); 477 | raw 478 | }; 479 | Ok(raw.into()) 480 | } 481 | 482 | /// Return information about the LMDB environment. 483 | pub fn info(&self) -> Result { 484 | let raw = unsafe { 485 | let mut raw = mem::zeroed::(); 486 | lmdb_call!(ffi::mdb_env_info(self.env.0, &mut raw)); 487 | raw 488 | }; 489 | Ok(EnvInfo { 490 | mapaddr: raw.me_mapaddr as *const c_void, 491 | mapsize: raw.me_mapsize as usize, 492 | last_pgno: raw.me_last_pgno as usize, 493 | last_txnid: raw.me_last_txnid as usize, 494 | maxreaders: raw.me_maxreaders as u32, 495 | numreaders: raw.me_numreaders as u32, 496 | }) 497 | } 498 | 499 | /// Flush the data buffers to disk. 500 | /// 501 | /// Data is always written to disk when transactions are committed, but the 502 | /// operating system may keep it buffered. LMDB always flushes the OS 503 | /// buffers upon commit as well, unless the environment was opened with 504 | /// `NOSYNC` or in part `NOMETASYNC`. This call is not valid if the 505 | /// environment was opened with `RDONLY`. 506 | /// 507 | /// If `force` is true, force a synchronous flush. Otherwise if the 508 | /// environment has the `NOSYNC` flag set the flushes will be omitted, and 509 | /// with `MAPASYNC` they will be asynchronous. 510 | pub fn sync(&self, force: bool) -> Result<()> { 511 | unsafe { 512 | lmdb_call!(ffi::mdb_env_sync(self.env.0, force as c_int)); 513 | } 514 | Ok(()) 515 | } 516 | 517 | /// Set environment flags. 518 | /// 519 | /// This may be used to set some flags in addition to those from 520 | /// `EnvBuilder::open()`, or to unset these flags. If several threads 521 | /// change the flags at the same time, the result is undefined. 522 | /// 523 | /// `flags` specifies the flags to edit, not the new status of all flags. 524 | /// If `onoff` is true, all flags in `flags` are set; otherwise, all flags 525 | /// in `flags` are cleared. 526 | /// 527 | /// ## Unsafety 528 | /// 529 | /// The caller must ensure that multiple threads do not call this 530 | /// concurrently with itself or with `get_flags()`. This could not be 531 | /// accomplished by using `&mut self`, since any open databases necessarily 532 | /// have the environment borrowed already. 533 | /// 534 | /// ## Example 535 | /// ``` 536 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 537 | /// # fn main() { 538 | /// # let env = create_env(); 539 | /// unsafe { 540 | /// // Enable the NOMETASYNC and MAPASYNC flags 541 | /// env.set_flags(lmdb::open::NOMETASYNC | lmdb::open::MAPASYNC, true) 542 | /// .unwrap(); 543 | /// assert!(env.flags().unwrap().contains( 544 | /// lmdb::open::NOMETASYNC | lmdb::open::MAPASYNC)); 545 | /// // Turn MAPASYNC back off, leaving NOMETASYNC set 546 | /// env.set_flags(lmdb::open::MAPASYNC, false).unwrap(); 547 | /// assert!(env.flags().unwrap().contains(lmdb::open::NOMETASYNC)); 548 | /// assert!(!env.flags().unwrap().contains(lmdb::open::MAPASYNC)); 549 | /// } 550 | /// # } 551 | /// ``` 552 | pub unsafe fn set_flags(&self, flags: open::Flags, 553 | onoff: bool) -> Result<()> { 554 | lmdb_call!(ffi::mdb_env_set_flags( 555 | self.env.0, flags.bits(), onoff as c_int)); 556 | Ok(()) 557 | } 558 | 559 | /// Get environment flags. 560 | pub fn flags(&self) -> Result { 561 | let mut raw: c_uint = 0; 562 | unsafe { 563 | lmdb_call!(ffi::mdb_env_get_flags(self.env.0, &mut raw)); 564 | } 565 | Ok(open::Flags::from_bits_truncate(raw)) 566 | } 567 | 568 | /// Return the path that was used in `EnvBuilder::open()`. 569 | /// 570 | /// ## Panics 571 | /// 572 | /// Panics if LMDB returns success but sets the path to a NULL pointer. 573 | pub fn path(&self) -> Result<&CStr> { 574 | let mut raw: *mut c_char = ptr::null_mut(); 575 | unsafe { 576 | lmdb_call!(ffi::mdb_env_get_path(self.env.0, &mut raw)); 577 | if raw.is_null() { 578 | panic!("mdb_env_get_path() returned NULL pointer"); 579 | } 580 | Ok(CStr::from_ptr(raw)) 581 | } 582 | } 583 | 584 | /// Return the filedescriptor for the given environment. 585 | /// 586 | /// ## Unsafety 587 | /// 588 | /// The caller must ensure that the file descriptor is not used to subvert 589 | /// normal LMDB functionality, such as by writing to it or closing it. 590 | pub unsafe fn fd(&self) -> Result { 591 | let mut raw: Fd = 0; 592 | lmdb_call!(ffi::mdb_env_get_fd(self.env.0, &mut raw)); 593 | Ok(raw) 594 | } 595 | 596 | /// Set the size of the memory map to use for this environment. 597 | /// 598 | /// The size should be a multiple of the OS page size. The default is 599 | /// 10485760 bytes. The size of the memory map is also the maximum size of 600 | /// the database. The value should be chosen as large as possible, to 601 | /// accommodate future growth of the database. 602 | /// 603 | /// The new size takes effect immediately for the current process but will 604 | /// not be persisted to any others until a write transaction has been 605 | /// committed by the current process. Also, only mapsize increases are 606 | /// persisted into the environment. 607 | /// 608 | /// If the mapsize is increased by another process, and data has grown 609 | /// beyond the range of the current mapsize, starting a transaction will 610 | /// return `error::MAP_RESIZED`. This function may be called with a size of 611 | /// zero to adopt the new size. 612 | /// 613 | /// ## Unsafety 614 | /// 615 | /// This may only be called if no transactions are active in the current 616 | /// process. Note that the library does not check for this condition, the 617 | /// caller must ensure it explicitly. 618 | pub unsafe fn set_mapsize(&self, size: usize) -> Result<()> { 619 | lmdb_call!(ffi::mdb_env_set_mapsize(self.env.0, size)); 620 | Ok(()) 621 | } 622 | 623 | /// Get the maximum number of threads/reader slots for the environment. 624 | pub fn maxreaders(&self) -> Result { 625 | let mut raw: c_uint = 0; 626 | unsafe { 627 | lmdb_call!(ffi::mdb_env_get_maxreaders(self.env.0, &mut raw)); 628 | } 629 | Ok(raw as u32) 630 | } 631 | 632 | /// Get the maximum size of keys and `DUPSORT` data we can write. 633 | /// 634 | /// Depends on the compile-time constant `MDB_MAXKEYSIZE` in LMDB. 635 | /// Default 511. 636 | pub fn maxkeysize(&self) -> u32 { 637 | unsafe { 638 | ffi::mdb_env_get_maxkeysize(self.env.0) as u32 639 | } 640 | } 641 | 642 | /// Check for stale entries in the reader lock table. 643 | /// 644 | /// Returns the number of stale slots that were cleared. 645 | pub fn reader_check(&self) -> Result { 646 | let mut raw: c_int = 0; 647 | unsafe { 648 | lmdb_call!(ffi::mdb_reader_check(self.env.0, &mut raw)); 649 | } 650 | Ok(raw as i32) 651 | } 652 | } 653 | 654 | // Internal API 655 | pub fn dbi_close(this: &Environment, dbi: ffi::MDB_dbi) { 656 | // Hold the lock through the end of the function to also guard the 657 | // LMDB's unsynchronised DBI table. 658 | let mut locked_dbis = this.open_dbis.lock() 659 | .expect("open_dbis lock poisoned"); 660 | assert!(locked_dbis.remove(&dbi), 661 | "closed dbi that wasn't open"); 662 | 663 | unsafe { 664 | ffi::mdb_dbi_close(this.env.0, dbi); 665 | } 666 | } 667 | 668 | // Internal API 669 | pub fn dbi_delete(this: &Environment, dbi: ffi::MDB_dbi) -> Result<()> { 670 | // Hold the lock across the call to `mdb_drop()` to also guard its 671 | // unsynchronised DBI table. 672 | let mut locked_dbis = this.open_dbis.lock() 673 | .expect("open_dbis lock poisoned"); 674 | unsafe { 675 | let mut raw_txn: *mut ffi::MDB_txn = ptr::null_mut(); 676 | lmdb_call!(ffi::mdb_txn_begin( 677 | this.env.0, ptr::null_mut(), 0, &mut raw_txn)); 678 | let mut txn = TxHandle(raw_txn); 679 | lmdb_call!(ffi::mdb_drop(raw_txn, dbi, 1 /* delete */)); 680 | try!(txn.commit()); 681 | } 682 | assert!(locked_dbis.remove(&dbi), 683 | "closed dbi that wasn't open"); 684 | Ok(()) 685 | } 686 | 687 | // Internal API 688 | pub fn env_ptr(this: &Environment) -> *mut ffi::MDB_env { 689 | this.env.0 690 | } 691 | 692 | // Internal API 693 | pub fn env_open_dbis(this: &Environment) -> &Mutex> { 694 | &this.open_dbis 695 | } 696 | 697 | #[cfg(test)] 698 | mod test { 699 | use test_helpers::*; 700 | use env::*; 701 | use tx::*; 702 | 703 | #[test] 704 | fn borrow_raw_doesnt_take_ownership() { 705 | let outer_env = create_env(); 706 | { 707 | let inner_env = unsafe { 708 | Environment::borrow_raw(outer_env.as_raw()) 709 | }; 710 | let db = defdb(&inner_env); 711 | let tx = WriteTransaction::new(&inner_env).unwrap(); 712 | tx.access().put(&db, "foo", "bar", put::Flags::empty()).unwrap(); 713 | tx.commit().unwrap(); 714 | } 715 | 716 | let db = defdb(&outer_env); 717 | let tx = ReadTransaction::new(&outer_env).unwrap(); 718 | assert_eq!("bar", tx.access().get::(&db, "foo").unwrap()); 719 | } 720 | } 721 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 FullContact, Inc 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | //! Error values and types returned by LMDB and this wrapper. 10 | 11 | use std::error::Error as StdError; 12 | use std::ffi::{CStr, NulError}; 13 | use std::fmt; 14 | use std::result; 15 | use libc::c_int; 16 | 17 | use ffi; 18 | use ffi2; 19 | 20 | /// key/data pair already exists 21 | pub const KEYEXIST: c_int = ffi::MDB_KEYEXIST; 22 | /// key/data pair not found (EOF) 23 | pub const NOTFOUND: c_int = ffi::MDB_NOTFOUND; 24 | /// Requested page not found - this usually indicates corruption 25 | pub const PAGE_NOTFOUND: c_int = ffi::MDB_PAGE_NOTFOUND; 26 | /// Located page was wrong type 27 | pub const CORRUPTED: c_int = ffi::MDB_CORRUPTED; 28 | /// Update of meta page failed or environment had fatal error 29 | pub const PANIC: c_int = ffi::MDB_PANIC; 30 | /// Environment version mismatch 31 | pub const VERSION_MISMATCH: c_int = ffi::MDB_VERSION_MISMATCH; 32 | /// File is not a valid LMDB file 33 | pub const INVALID: c_int = ffi::MDB_INVALID; 34 | /// Environment mapsize reached 35 | pub const MAP_FULL: c_int = ffi::MDB_MAP_FULL; 36 | /// Environment maxdbs reached 37 | pub const DBS_FULL: c_int = ffi::MDB_DBS_FULL; 38 | /// Environment maxreaders reached 39 | pub const READERS_FULL: c_int = ffi::MDB_READERS_FULL; 40 | /// Too many TLS keys in use - Windows only 41 | pub const TLS_FULL: c_int = ffi::MDB_TLS_FULL; 42 | /// Txn has too many dirty pages 43 | pub const TXN_FULL: c_int = ffi::MDB_TXN_FULL; 44 | /// Cursor stack too deep - internal error 45 | pub const CURSOR_FULL: c_int = ffi::MDB_CURSOR_FULL; 46 | /// Page has not enough space - internal error 47 | pub const PAGE_FULL: c_int = ffi::MDB_PAGE_FULL; 48 | /// Database contents grew beyond environment mapsize 49 | pub const MAP_RESIZED: c_int = ffi::MDB_MAP_RESIZED; 50 | /// Operation and DB incompatible, or DB type changed. This can mean: 51 | /// 52 | /// - The operation expects an `DUPSORT` / `DUPFIXED` database. 53 | /// - Opening a named DB when the unnamed DB has `DUPSORT` / `INTEGERKEY`. 54 | /// - Accessing a data record as a database, or vice versa. 55 | /// - The database was dropped and recreated with different flags. 56 | pub const INCOMPATIBLE: c_int = ffi::MDB_INCOMPATIBLE; 57 | /// Invalid reuse of reader locktable slot 58 | pub const BAD_RSLOT: c_int = ffi::MDB_BAD_RSLOT; 59 | /// Transaction must abort, has a child, or is invalid 60 | pub const BAD_TXN: c_int = ffi::MDB_BAD_TXN; 61 | /// Unsupported size of key/DB name/data, or wrong `DUPFIXED` size 62 | pub const BAD_VALSIZE: c_int = ffi::MDB_BAD_VALSIZE; 63 | /// The specified DBI was changed unexpectedly 64 | pub const BAD_DBI: c_int = ffi2::MDB_BAD_DBI; 65 | 66 | /// Error type returned by LMDB. 67 | #[derive(Clone,PartialEq,Eq,Hash)] 68 | pub enum Error { 69 | /// A basic error code returned by LMDB. 70 | /// 71 | /// The code is generally expected to be a constant defined in the `errors` 72 | /// module if negative, or a raw platform error code if positive. 73 | Code(c_int), 74 | /// A string path was given which contains a `NUL` byte. 75 | NulStr, 76 | /// An attempt was made to open a database which is already open. 77 | Reopened, 78 | /// An attempt was made to use two items together which cannot be used 79 | /// together. 80 | /// 81 | /// For example, trying to use a cursor from one transaction to access data 82 | /// in another. 83 | Mismatch, 84 | /// A value conversion was rejected. A message explaining why is included. 85 | ValRejected(String), 86 | // Prevent external code from exhaustively matching on this enum. 87 | #[doc(hidden)] 88 | _NonExhaustive 89 | } 90 | 91 | /// Result type returned for all calls that can fail. 92 | pub type Result = result::Result; 93 | 94 | impl Error { 95 | fn strerror(&self) -> &'static str { 96 | match *self { 97 | Error::NulStr => "NUL byte in path", 98 | Error::Reopened => "Attempt to reopen database", 99 | Error::Mismatch => 100 | "Items from different env/database used together", 101 | Error::ValRejected(..) => 102 | "Value conversion failed", 103 | Error::_NonExhaustive => "Error::_NonExhaustive", 104 | Error::Code(code) => unsafe { 105 | let raw = ffi::mdb_strerror(code); 106 | if raw.is_null() { 107 | "(null)" 108 | } else { 109 | CStr::from_ptr(raw).to_str().unwrap_or("(unknown)") 110 | } 111 | }, 112 | } 113 | } 114 | } 115 | 116 | impl fmt::Debug for Error { 117 | fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { 118 | match *self { 119 | Error::NulStr => 120 | write!(f, "Error::NulStr"), 121 | Error::Reopened => 122 | write!(f, "Error::Reopened"), 123 | Error::Mismatch => 124 | write!(f, "Error::Mismatch"), 125 | Error::ValRejected(ref why) => 126 | write!(f, "Error::ValRejected({:?})", why), 127 | Error::Code(code) => 128 | write!(f, "Error::Code({}, '{}')", code, self.strerror()), 129 | Error::_NonExhaustive => 130 | write!(f, "Error::_NonExhaustive"), 131 | } 132 | } 133 | } 134 | 135 | impl fmt::Display for Error { 136 | fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { 137 | match *self { 138 | Error::ValRejected(ref why) => 139 | write!(f, "Value conversion failed: {}", why), 140 | _ => write!(f, "{}", self.strerror()), 141 | } 142 | } 143 | } 144 | 145 | impl StdError for Error { 146 | fn description(&self) -> &str { 147 | self.strerror() 148 | } 149 | } 150 | 151 | impl From for Error { 152 | fn from(_: NulError) -> Self { 153 | Error::NulStr 154 | } 155 | } 156 | 157 | /// Extension methods for LMDB results 158 | pub trait LmdbResultExt { 159 | #[allow(missing_docs)] 160 | type Inner; 161 | 162 | /// Lift "not found" errors to `None`. 163 | /// 164 | /// If `Ok(val)`, return `Ok(Some(val))`. If `Err` but the error is 165 | /// `Error::Code(NOTFOUND)`, return `Ok(None)`. Otherwise, return self. 166 | fn to_opt(self) -> Result>; 167 | 168 | /// Suppress `KEYEXIST` errors. 169 | /// 170 | /// If this is `Err` and the error is `Error::Code(KEYEXIST)`, switch to 171 | /// `Ok` with the given inner value. 172 | fn ignore_exists(self, inner: Self::Inner) -> Self; 173 | } 174 | 175 | impl LmdbResultExt for Result { 176 | type Inner = T; 177 | 178 | fn to_opt(self) -> Result> { 179 | match self { 180 | Ok(val) => Ok(Some(val)), 181 | Err(Error::Code(code)) if NOTFOUND == code => Ok(None), 182 | Err(error) => Err(error), 183 | } 184 | } 185 | 186 | fn ignore_exists(self, inner: T) -> Self { 187 | match self { 188 | Ok(val) => Ok(val), 189 | Err(Error::Code(code)) if KEYEXIST == code => Ok(inner), 190 | Err(error) => Err(error), 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /src/example_helpers.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 FullContact, Inc 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | // This is not a lmdb-zero module. It's included by the doc tests to provide 10 | // common functionality. 11 | 12 | extern crate tempdir; 13 | extern crate lmdb_zero as lmdb; 14 | 15 | #[allow(dead_code)] 16 | fn create_env() -> lmdb::Environment { 17 | unsafe { 18 | let mut builder = lmdb::EnvBuilder::new().unwrap(); 19 | builder.set_maxdbs(2).unwrap(); 20 | builder.open( 21 | tempdir::TempDir::new_in(".", "lmdbzero").unwrap() 22 | .path().to_str().unwrap(), 23 | lmdb::open::Flags::empty(), 0o600).unwrap() 24 | } 25 | } 26 | 27 | #[allow(dead_code)] 28 | fn dupdb(env: &lmdb::Environment) -> lmdb::Database { 29 | lmdb::Database::open(env, Some("example"), &lmdb::DatabaseOptions::new( 30 | lmdb::db::CREATE | lmdb::db::DUPSORT)).unwrap() 31 | } 32 | 33 | #[allow(dead_code)] 34 | fn dupfixeddb(env: &lmdb::Environment) -> lmdb::Database { 35 | lmdb::Database::open(env, Some("example"), &lmdb::DatabaseOptions::new( 36 | lmdb::db::CREATE | lmdb::db::DUPSORT | lmdb::db::DUPFIXED)).unwrap() 37 | } 38 | 39 | #[allow(dead_code)] 40 | fn defdb(env: &lmdb::Environment) -> lmdb::Database { 41 | lmdb::Database::open(env, None, &lmdb::DatabaseOptions::defaults()) 42 | .unwrap() 43 | } 44 | -------------------------------------------------------------------------------- /src/ffi2.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 FullContact, Inc 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | // Things missing from liblmdb-sys 10 | // This module is private to lmdb-zero. 11 | 12 | use libc::{c_char, c_int, c_uint, size_t}; 13 | 14 | use ffi; 15 | 16 | pub const MDB_BAD_DBI: c_int = -30780; 17 | pub const MDB_CP_COMPACT: c_uint = 1; 18 | 19 | extern "C" { 20 | pub fn mdb_env_copy2(env: *mut ffi::MDB_env, path: *const c_char, 21 | flags: c_uint) -> c_int; 22 | pub fn mdb_env_copyfd2(env: *mut ffi::MDB_env, fd: ffi::mdb_filehandle_t, 23 | flags: c_uint) -> c_int; 24 | pub fn mdb_txn_id(txn: *mut ffi::MDB_txn) -> size_t; 25 | } 26 | -------------------------------------------------------------------------------- /src/iter.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 FullContact, Inc 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use std::iter::Iterator; 10 | use std::ops::{Deref, DerefMut}; 11 | 12 | use cursor::Cursor; 13 | use error::Result; 14 | use tx::ConstAccessor; 15 | use traits::*; 16 | 17 | /// A mutable value which is either owned or borrowed from an owning context. 18 | /// 19 | /// This is very different from `Cow` in that one can mutate the shared 20 | /// reference but cannot take ownership. 21 | #[derive(Debug)] 22 | #[allow(missing_docs)] 23 | pub enum MaybeOwned<'a, T : 'a> { 24 | Owned(T), 25 | Borrowed(&'a mut T), 26 | } 27 | 28 | impl<'a, T : 'a> Deref for MaybeOwned<'a, T> { 29 | type Target = T; 30 | 31 | fn deref(&self) -> &T { 32 | match *self { 33 | MaybeOwned::Owned(ref t) => t, 34 | MaybeOwned::Borrowed(ref t) => *t, 35 | } 36 | } 37 | } 38 | 39 | impl<'a, T : 'a> DerefMut for MaybeOwned<'a, T> { 40 | fn deref_mut(&mut self) -> &mut T { 41 | match *self { 42 | MaybeOwned::Owned(ref mut t) => t, 43 | // `ref mut` is necessary so we can borrow the field out of the 44 | // enum. 45 | MaybeOwned::Borrowed(ref mut t) => *t, 46 | } 47 | } 48 | } 49 | 50 | /// An iterator over items returned by successive calls of some function on 51 | /// `Cursor` until a `NOTFOUND` error is returned. 52 | /// 53 | /// Special handling is afforded the first item in the iterator, since the 54 | /// simple act of positioning the cursor produces the first item. 55 | pub struct CursorIter<'a, 'access: 'a, 'txn: 'access, 'db: 'txn, T> { 56 | cursor: MaybeOwned<'a, Cursor<'txn,'db>>, 57 | access: &'access ConstAccessor<'txn>, 58 | head: Option, 59 | next: fn (&mut Cursor<'txn,'db>, &'access ConstAccessor<'txn>) 60 | -> Result, 61 | } 62 | 63 | impl<'a, 'access: 'a, 'txn: 'access, 'db: 'txn, T> 64 | CursorIter<'a, 'access, 'txn, 'db, T> { 65 | /// Creates a cursor iterator from the given cursor and accessor. 66 | /// 67 | /// `head` is invoked immediately on `cursor` and `accessor` to position 68 | /// the cursor. The value it returns (if any) will be used as the first 69 | /// value produced by the cursor. 70 | /// 71 | /// Beyond the first item, `next` will be invoked on `cursor` and 72 | /// `accessor` to produce further items. Note that this is a plain function 73 | /// pointer and not a function object so that the type does not need to be 74 | /// encoded in the type of this iterator. 75 | /// 76 | /// ## Example 77 | /// 78 | /// ``` 79 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 80 | /// # fn main() { 81 | /// # let env = create_env(); 82 | /// # let db = dupdb(&env); 83 | /// let txn = lmdb::WriteTransaction::new(&env).unwrap(); 84 | /// { 85 | /// let mut access = txn.access(); 86 | /// let f = lmdb::put::Flags::empty(); 87 | /// access.put(&db, "Fruit", "Apple", f).unwrap(); 88 | /// access.put(&db, "Fruit", "Orange", f).unwrap(); 89 | /// access.put(&db, "Animal", "Badger", f).unwrap(); 90 | /// access.put(&db, "Veggie", "Carrot", f).unwrap(); 91 | /// 92 | /// let mut cursor = txn.cursor(&db).unwrap(); 93 | /// let mut iter = lmdb::CursorIter::new( 94 | /// lmdb::MaybeOwned::Borrowed(&mut cursor), &*access, 95 | /// |c, a| c.first(a), lmdb::Cursor::next::).unwrap(); 96 | /// assert_eq!(("Animal", "Badger"), iter.next().unwrap().unwrap()); 97 | /// assert_eq!(("Fruit", "Apple"), iter.next().unwrap().unwrap()); 98 | /// assert_eq!(("Fruit", "Orange"), iter.next().unwrap().unwrap()); 99 | /// assert_eq!(("Veggie", "Carrot"), iter.next().unwrap().unwrap()); 100 | /// assert!(iter.next().is_none()); 101 | /// } 102 | /// txn.commit().unwrap(); 103 | /// # } 104 | /// ``` 105 | pub fn new, 106 | &'access ConstAccessor<'txn>) 107 | -> Result> 108 | (mut cursor: MaybeOwned<'a, Cursor<'txn,'db>>, 109 | access: &'access ConstAccessor<'txn>, 110 | head: H, 111 | next: fn (&mut Cursor<'txn,'db>, &'access ConstAccessor<'txn>) 112 | -> Result) 113 | -> Result 114 | { 115 | let head_val = try!(head(&mut*cursor, access).to_opt()); 116 | Ok(CursorIter { 117 | cursor: cursor, 118 | access: access, 119 | head: head_val, 120 | next: next, 121 | }) 122 | } 123 | } 124 | 125 | impl<'a, 'access: 'a, 'txn: 'access, 'db: 'txn, T> Iterator 126 | for CursorIter<'a, 'access, 'txn, 'db, T> { 127 | type Item = Result; 128 | 129 | fn next(&mut self) -> Option> { 130 | if let Some(head) = self.head.take() { 131 | Some(Ok(head)) 132 | } else { 133 | match (self.next)(&mut*self.cursor, self.access).to_opt() { 134 | Ok(Some(v)) => Some(Ok(v)), 135 | Ok(None) => None, 136 | Err(err) => Some(Err(err.into())), 137 | } 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 FullContact, Inc 2 | // Copyright 2017 Jason Lingle 3 | // 4 | // Licensed under the Apache License, Version 2.0 or the MIT license 6 | // , at your 7 | // option. This file may not be copied, modified, or distributed 8 | // except according to those terms. 9 | 10 | //! Near-zero-cost, mostly-safe idiomatic bindings to LMDB. 11 | //! 12 | //! This crate provides an interface to LMDB which as much as possible is not 13 | //! abstracted from the model LMDB itself provides, except as necessary to 14 | //! integrate with the borrow checker. This means that you don't get easy 15 | //! iterators, but also that you can do almost anything with LMDB through these 16 | //! bindings as you can through C. 17 | //! 18 | //! # Example 19 | //! 20 | //! ``` 21 | //! extern crate lmdb_zero as lmdb; 22 | //! extern crate tempdir; 23 | //! 24 | //! # fn main() { 25 | //! # let tmp = tempdir::TempDir::new_in(".", "lmdbzero").unwrap(); 26 | //! # example(tmp.path().to_str().unwrap()); 27 | //! # } 28 | //! # 29 | //! fn example(path: &str) { 30 | //! // Create the environment, that is, the file containing the database(s). 31 | //! // This is unsafe because you need to ensure certain things about the 32 | //! // host environment that these bindings can't help you with. 33 | //! let env = unsafe { 34 | //! lmdb::EnvBuilder::new().unwrap().open( 35 | //! path, lmdb::open::Flags::empty(), 0o600).unwrap() 36 | //! }; 37 | //! // Open the default database. 38 | //! let db = lmdb::Database::open( 39 | //! &env, None, &lmdb::DatabaseOptions::defaults()) 40 | //! .unwrap(); 41 | //! { 42 | //! // Write some data in a transaction 43 | //! let txn = lmdb::WriteTransaction::new(&env).unwrap(); 44 | //! // An accessor is used to control memory access. 45 | //! // NB You can only have one live accessor from a particular transaction 46 | //! // at a time. Violating this results in a panic at runtime. 47 | //! { 48 | //! let mut access = txn.access(); 49 | //! access.put(&db, "Germany", "Berlin", lmdb::put::Flags::empty()).unwrap(); 50 | //! access.put(&db, "France", "Paris", lmdb::put::Flags::empty()).unwrap(); 51 | //! access.put(&db, "Latvia", "Rīga", lmdb::put::Flags::empty()).unwrap(); 52 | //! } 53 | //! // Commit the changes so they are visible to later transactions 54 | //! txn.commit().unwrap(); 55 | //! } 56 | //! 57 | //! { 58 | //! // Now let's read the data back 59 | //! let txn = lmdb::ReadTransaction::new(&env).unwrap(); 60 | //! let access = txn.access(); 61 | //! 62 | //! // Get the capital of Latvia. Note that the string is *not* copied; the 63 | //! // reference actually points into the database memory, and is valid 64 | //! // until the transaction is dropped or the accessor is mutated. 65 | //! let capital_of_latvia: &str = access.get(&db, "Latvia").unwrap(); 66 | //! assert_eq!("Rīga", capital_of_latvia); 67 | //! 68 | //! // We can also use cursors to move over the contents of the database. 69 | //! let mut cursor = txn.cursor(&db).unwrap(); 70 | //! assert_eq!(("France", "Paris"), cursor.first(&access).unwrap()); 71 | //! assert_eq!(("Germany", "Berlin"), cursor.next(&access).unwrap()); 72 | //! assert_eq!(("Latvia", "Rīga"), cursor.next(&access).unwrap()); 73 | //! assert!(cursor.next::(&access).is_err()); 74 | //! } 75 | //! } 76 | //! ``` 77 | //! 78 | //! # Anatomy of this crate 79 | //! 80 | //! `Environment` is the top-level structure. It is created with an 81 | //! `EnvBuilder`. An `Environment` is a single file (by default in a 82 | //! subdirectory) which stores the actual data of all the databases it 83 | //! contains. It corresponds to an `MDB_env` in the C API. 84 | //! 85 | //! A `Database` is a single table of key/value pairs within an environment. 86 | //! Each environment has a single anonymous database, and may contain a number 87 | //! of named databases. Note that if you want to use named databases, you need 88 | //! to use `EnvBuilder::set_maxdbs` before creating the `Environment` to make 89 | //! room for the handles. A database can only have one `Database` handle per 90 | //! environment at a time. 91 | //! 92 | //! All accesses to the data within an environment are done through 93 | //! transactions. For this, there are the `ReadTransaction` and 94 | //! `WriteTransaction` structs. Both of these deref to a `ConstTransaction`, 95 | //! which provides most of the read-only functionality and can be used for 96 | //! writing code that can run within either type of transaction. Note that read 97 | //! transactions are far cheaper than write transactions. 98 | //! 99 | //! `ReadTransaction`s can be reused by using `reset()` to turn them into 100 | //! `ResetTransaction`s and then `refresh()` to turn them into fresh 101 | //! `ReadTransaction`s. 102 | //! 103 | //! One unusual property of this crate are the `ConstAccessor` and 104 | //! `WriteAccessor` structs, which are obtained once from a transaction and 105 | //! used to perform actual data manipulation. These are needed to work with the 106 | //! borrow checker: Cursors have a lifetime bound by their transaction and thus 107 | //! borrow it, so we need something else to permit borrowing mutable data. The 108 | //! accessors reflect this borrowing: Reading from the database requires an 109 | //! immutable borrow of the accessor, while writing (which may invalidate 110 | //! pointers) requires a mutable borrow of the accessor, thus causing the 111 | //! borrow checker to ensure that all read accesses are disposed before any 112 | //! write. 113 | //! 114 | //! Finally, the `Cursor` struct can be created from a transaction to permit 115 | //! more flexible access to a database. Each `Cursor` corresponds to a 116 | //! `MDB_cursor`. Accessing data through a cursor requires borrowing 117 | //! appropriately from the accessor of the transaction owning the cursor. 118 | //! 119 | //! If you want to define your own types to store in the database, see the 120 | //! `lmdb_zero::traits` submodule. 121 | //! 122 | //! # Lifetimes and Ownership 123 | //! 124 | //! Lmdb-zero heavily uses lifetime parameters to allow user code to safely 125 | //! retain handles into LMDB without extra runtime overhead. 126 | //! 127 | //! While this makes the library very flexible, it also makes it somewhat 128 | //! harder to use when its types need to be referenced explicitly, for example 129 | //! as struct members. The documentation for each type with lifetime parameters 130 | //! therefore includes a short discussion of how the lifetimes are intended to 131 | //! interact and how best to work with them. 132 | //! 133 | //! It is also possible to opt-out of compile-time lifetime tracking and 134 | //! instead use `Arc` or `Rc` around various handles. In this case, all the 135 | //! lifetime parameters simply become `'static`. See the next section for 136 | //! details. 137 | //! 138 | //! ## Ownership Modes 139 | //! 140 | //! As of version 0.4.0, most APIs which construct a value which holds on to 141 | //! some "parent" value (e.g., creating a `Database` within an `Environment`) 142 | //! accept anything that can be converted into a [`Supercow`](https://docs.rs/supercow/0.1.0/supercow/). 143 | //! Deep understanding of `Supercow` itself is not required to use `lmdb-zero`. 144 | //! The only thing you need to know is that an `Into>` means that 145 | //! you can pass in one of three classes of arguments: 146 | //! 147 | //! - `&T`. This is "borrowed mode". The majority of the documentation in this 148 | //! crate uses borrowed mode. This is zero-overhead and is statically 149 | //! verifiable (i.e., all usage is checked at compile-time), so it is 150 | //! recommended that borrowed mode be used whenever reasonably possible. This 151 | //! mode causes the "child" value to hold a normal reference to the parent, 152 | //! which means that lifetimes must be tracked in the lifetime parameters. But 153 | //! because of this, this mode can be inflexible; for example, you cannot use 154 | //! safe Rust to create a `struct` holding both an `Environment` and its 155 | //! `Database`s using borrowed mode. 156 | //! 157 | //! - `Arc`. This is "shared mode". For `NonSyncSupercow`, `Rc` may also 158 | //! be used. The child will hold the `Arc` or `Rc`, thus ensuring the parent 159 | //! lives at least as long as the child. Because of this, the related lifetime 160 | //! parameters can simply be written as `'static`. It also means that 161 | //! `Arc`/`Rc` references to the child and parent can be placed together in the 162 | //! same struct with safe Rust. This comes at a cost: Constructing values in 163 | //! shared mode incurs allocation; additionally, the ability to statically 164 | //! verify the lifetime of the parent values is lost. 165 | //! 166 | //! - `T`. This is "owned mode". The parent is moved into the child value and 167 | //! owned by the child from thereon. This is most useful when you only ever 168 | //! want one child and don't care about retaining ownership of the parent. As 169 | //! with shared mode, it also allows simply using `'static` as the relevant 170 | //! lifetime parameters. 171 | //! 172 | //! # Major Differences from the LMDB C API 173 | //! 174 | //! Databases cannot be created or destroyed within a transaction due to the 175 | //! awkward memory management semantics. For similar reasons, opening a 176 | //! database more than once is not permitted (though note that LMDB doesn't 177 | //! strictly allow this either --- it just silently returns an existing 178 | //! handle). 179 | //! 180 | //! Access to data within the environment is guarded by transaction-specific 181 | //! "accessors", which must be used in conjunction with the cursor or 182 | //! transaction. This is how these bindings integrate with the borrow checker. 183 | //! 184 | //! APIs which obtain a reference to the owner of an object are not supported. 185 | //! 186 | //! Various APIs which radically change behaviour (including memory semantics) 187 | //! in response to flags are separated into different calls which each express 188 | //! their memory semantics clearly. 189 | //! 190 | //! # Non-Zero Cost 191 | //! 192 | //! There are three general areas where this wrapper adds non-zero-cost 193 | //! abstractions: 194 | //! 195 | //! - Opening and closing databases adds locking overhead, since in LMDB it is 196 | //! unsynchronised. This shouldn't have much impact since one rarely opens 197 | //! and closes databases at a very high rate. 198 | //! 199 | //! - There is additional overhead in tracking what database handles are 200 | //! currently open so that attempts to reopen one can be prevented. 201 | //! 202 | //! - Cursors and transactions track their owners separately. Additionally, 203 | //! when two are used in conjunction, a runtime test is required to ensure 204 | //! that they actually can be used together. This means that the handle 205 | //! values are slightly larger and some function calls have an extra (very 206 | //! predictable) branch if the optimiser does not optimise the branch away 207 | //! entirely. 208 | //! 209 | //! - Using ownership modes other than borrowed (i.e., mundane references) 210 | //! incurs extra allocations in addition to the overhead of inherent in that 211 | //! ownership mode. 212 | //! 213 | //! # Using Zero-Copy 214 | //! 215 | //! This crate is primarily focussed on supporting zero-copy on all operations 216 | //! where this is possible. The examples above demonstrate one aspect of this: 217 | //! the `&str`s returned when querying for items are pointers into the database 218 | //! itself, valid as long as the accessor is. 219 | //! 220 | //! The main traits to look at are `lmdb_zero::traits::AsLmdbBytes` and 221 | //! `lmdb_zero::traits::FromLmdbBytes`, which are used to cast between byte 222 | //! arrays and the types to be stored in the database. 223 | //! `lmdb_zero::traits::FromReservedLmdbBytes` is used if you want to use the 224 | //! `reserve` methods (in which you write the key only to the database and get 225 | //! a pointer to a value to fill in after the fact). If you have a 226 | //! `#[repr(C)]`, `Copy` struct, you can also use `lmdb_zero::traits::LmdbRaw` 227 | //! if you just want to shove the raw struct itself into the database. All of 228 | //! these have caveats which can be found on the struct documentation. 229 | //! 230 | //! Be aware that using zero-copy to save anything more interesting than byte 231 | //! strings means your databases will not be portable to other architectures. 232 | //! This mainly concerns byte-order, but types like `usize` whose size varies 233 | //! by platform can also cause problems. 234 | //! 235 | //! # Notes on Memory Safety 236 | //! 237 | //! It is not possible to use lmdb-zero without at least one unsafe block, 238 | //! because doing anything with a memory-mapped file requires making 239 | //! assumptions about the host environment. Lmdb-zero is not in a position to 240 | //! decide these assumptions, and so they are passed up to the caller. 241 | //! 242 | //! However, if these assumptions are met, it should be impossible to cause 243 | //! memory unsafety (eg, aliasing mutable references; dangling pointers; buffer 244 | //! under/overflows) by use of lmdb-zero's safe API. 245 | //! 246 | //! # Unavailable LMDB APIs 247 | //! 248 | //! - `mdb_env_copy`, `mdb_env_copyfd`: Only the `2`-suffixed versions that 249 | //! take flags are exposed. 250 | //! 251 | //! - `mdb_env_set_userctx`, `mdb_env_get_userctx`: Not generally useful for 252 | //! Rust; unclear how ownership would be expressed; would likely end up forcing 253 | //! an almost-never-used generic arg on `Environment` on everyone. 254 | //! 255 | //! - `mdb_env_set_assert`: Does not seem useful enough to expose. 256 | //! 257 | //! - `mdb_txn_env`, `mdb_cursor_txn`, `mdb_cursor_dbi`: Would allow violating 258 | //! borrow semantics. 259 | //! 260 | //! - `mdb_cmp`, `mdb_dcmp`: Doesn't seem useful; this would basically be a 261 | //! reinterpret cast from the input values to whatever the table comparator 262 | //! expects and then invoking the `Ord` implementation. If the types match, 263 | //! this is strictly inferior to just using `Ord` directly; if they don't, it 264 | //! at best is obfuscating, and at worst completely broken. 265 | //! 266 | //! - `mdb_set_relfunc`, `mdb_set_relctx`: Currently a noop in LMDB. Even if it 267 | //! weren't, it is unlikely that there is any remotely safe or convenient way 268 | //! to provide an interface to it. 269 | //! 270 | //! - `mdb_reader_list`: Doesn't seem useful enough to expose. 271 | 272 | #![deny(missing_docs)] 273 | 274 | extern crate liblmdb_sys as ffi; 275 | extern crate libc; 276 | extern crate supercow; 277 | #[macro_use] extern crate bitflags; 278 | #[cfg(test)] extern crate tempdir; 279 | 280 | use std::ffi::CStr; 281 | 282 | pub use ffi::mdb_mode_t as FileMode; 283 | pub use ffi::mdb_filehandle_t as Fd; 284 | 285 | macro_rules! lmdb_call { 286 | ($x:expr) => { { 287 | let code = $x; 288 | if 0 != code { 289 | return Err($crate::Error::Code(code)); 290 | } 291 | } } 292 | } 293 | 294 | /// Returns the LMDB version as a string. 295 | pub fn version_str() -> &'static str { 296 | let mut major: libc::c_int = 0; 297 | let mut minor: libc::c_int = 0; 298 | let mut rev: libc::c_int = 0; 299 | unsafe { 300 | CStr::from_ptr(ffi::mdb_version(&mut major, &mut minor, &mut rev)) 301 | .to_str().unwrap_or("(invalid)") 302 | } 303 | } 304 | 305 | /// Returns the LMDB version as (major, minor, revision). 306 | pub fn version() -> (i32, i32, i32) { 307 | let mut major: libc::c_int = 0; 308 | let mut minor: libc::c_int = 0; 309 | let mut rev: libc::c_int = 0; 310 | unsafe { 311 | ffi::mdb_version(&mut major, &mut minor, &mut rev); 312 | } 313 | (major as i32, minor as i32, rev as i32) 314 | } 315 | 316 | /// Empty type used to indicate "don't care" when reading values from LMDB. 317 | /// 318 | /// `FromLmdbBytes` is implemented for this type by simply returning its only 319 | /// value without inspecting anything. 320 | pub struct Ignore; 321 | 322 | mod mdb_vals; 323 | mod ffi2; 324 | #[cfg(test)] mod test_helpers; 325 | 326 | pub mod error; 327 | pub use error::{Error, LmdbResultExt, Result}; 328 | 329 | mod env; 330 | pub use env::{open, copy, EnvBuilder, Environment, Stat, EnvInfo}; 331 | 332 | mod dbi; 333 | pub use dbi::{db, Database, DatabaseOptions}; 334 | 335 | pub mod traits; 336 | mod unaligned; 337 | pub use unaligned::{Unaligned, unaligned}; 338 | 339 | mod tx; 340 | pub use tx::{ConstTransaction, ReadTransaction, WriteTransaction}; 341 | pub use tx::ResetTransaction; 342 | pub use tx::{ConstAccessor, WriteAccessor}; 343 | pub use tx::{put, del}; 344 | 345 | mod cursor; 346 | pub use cursor::{StaleCursor, Cursor}; 347 | 348 | mod iter; 349 | pub use iter::{CursorIter, MaybeOwned}; 350 | -------------------------------------------------------------------------------- /src/mdb_vals.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 FullContact, Inc 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | //! Internal helpers for working with `MDB_val`s. 10 | 11 | use std::mem; 12 | use std::slice; 13 | use libc::{self, c_void}; 14 | 15 | use ffi; 16 | 17 | use error::{Error, Result}; 18 | use traits::*; 19 | 20 | pub const EMPTY_VAL: ffi::MDB_val = ffi::MDB_val { 21 | mv_size: 0, 22 | mv_data: 0 as *mut c_void, 23 | }; 24 | 25 | pub fn as_val(v: &V) -> ffi::MDB_val { 26 | let bytes = v.as_lmdb_bytes(); 27 | ffi::MDB_val { 28 | mv_size: bytes.len() as libc::size_t, 29 | mv_data: unsafe { mem::transmute(bytes.as_ptr()) }, 30 | } 31 | } 32 | 33 | pub fn mdb_val_as_bytes<'a,O>(_o: &'a O, val: &ffi::MDB_val) -> &'a[u8] { 34 | debug_assert!(!val.mv_data.is_null(), "MDB_val ptr is NULL, size = {}", 35 | val.mv_size); 36 | 37 | unsafe { 38 | slice::from_raw_parts( 39 | mem::transmute(val.mv_data), val.mv_size as usize) 40 | } 41 | } 42 | 43 | pub fn from_val<'a, O, V : FromLmdbBytes + ?Sized>( 44 | _owner: &'a O, val: &ffi::MDB_val) -> Result<&'a V> 45 | { 46 | let bytes = mdb_val_as_bytes(_owner, val); 47 | V::from_lmdb_bytes(bytes).map_err(|s| Error::ValRejected(s)) 48 | } 49 | 50 | pub unsafe fn from_reserved<'a, O, V : FromReservedLmdbBytes + ?Sized>( 51 | _owner: &'a O, val: &ffi::MDB_val) -> &'a mut V 52 | { 53 | let bytes = slice::from_raw_parts_mut( 54 | mem::transmute(val.mv_data), val.mv_size as usize); 55 | V::from_reserved_lmdb_bytes(bytes) 56 | } 57 | 58 | -------------------------------------------------------------------------------- /src/test_helpers.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 FullContact, Inc 2 | // Copyright 2018 Jason Lingle, Inc 3 | // 4 | // Licensed under the Apache License, Version 2.0 or the MIT license 6 | // , at your 7 | // option. This file may not be copied, modified, or distributed 8 | // except according to those terms. 9 | 10 | use tempdir; 11 | 12 | use dbi; 13 | use env; 14 | 15 | pub fn create_env() -> env::Environment { 16 | unsafe { 17 | let mut builder = env::EnvBuilder::new().unwrap(); 18 | builder.set_maxdbs(2).unwrap(); 19 | builder.open( 20 | tempdir::TempDir::new_in(".", "lmdbzero").unwrap() 21 | .path().to_str().unwrap(), 22 | env::open::Flags::empty(), 0o600).unwrap() 23 | } 24 | } 25 | 26 | #[allow(dead_code)] 27 | pub fn dupdb(env: &env::Environment) -> dbi::Database { 28 | dbi::Database::open(env, Some("example"), &dbi::DatabaseOptions::new( 29 | dbi::db::CREATE | dbi::db::DUPSORT)).unwrap() 30 | } 31 | 32 | #[allow(dead_code)] 33 | pub fn dupfixeddb(env: &env::Environment) -> dbi::Database { 34 | dbi::Database::open(env, Some("example"), &dbi::DatabaseOptions::new( 35 | dbi::db::CREATE | dbi::db::DUPSORT | dbi::db::DUPFIXED)).unwrap() 36 | } 37 | 38 | #[allow(dead_code)] 39 | pub fn defdb(env: &env::Environment) -> dbi::Database { 40 | dbi::Database::open(env, None, &dbi::DatabaseOptions::defaults()) 41 | .unwrap() 42 | } 43 | -------------------------------------------------------------------------------- /src/traits.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 FullContact, Inc 2 | // Copyright 2017 Jason Lingle 3 | // 4 | // Licensed under the Apache License, Version 2.0 or the MIT license 6 | // , at your 7 | // option. This file may not be copied, modified, or distributed 8 | // except according to those terms. 9 | 10 | //! Module containing public traits. 11 | //! 12 | //! This exists as a separate module solely so that it can be wildcard imported 13 | //! where necessary. 14 | 15 | use std::char; 16 | use std::cmp::Ord; 17 | use std::ffi::CStr; 18 | use std::mem; 19 | use std::num::Wrapping; 20 | use std::rc::Rc; 21 | use std::slice; 22 | use std::str; 23 | use std::sync::Arc; 24 | 25 | use supercow::Supercow; 26 | 27 | use ::Ignore; 28 | use cursor::{Cursor, StaleCursor}; 29 | use dbi::Database; 30 | use tx::{ConstTransaction, ReadTransaction, WriteTransaction}; 31 | 32 | pub use error::{self, LmdbResultExt}; 33 | 34 | /// Extension trait for `Rc` and `Arc` that allows up-casting a reference to 35 | /// `ReadTransaction` or `WriteTransaction` to `ConstTransaction`. 36 | pub trait TxExt { 37 | #[allow(missing_docs)] 38 | type Const; 39 | /// Returns `self` as a handle to a `ConstTransaction` rather than the 40 | /// subtype that was passed in. 41 | /// 42 | /// This is still a shared handle to the same transaction. 43 | fn to_const(self) -> Self::Const; 44 | } 45 | 46 | impl<'a> TxExt for Rc> { 47 | type Const = Rc>; 48 | // This is safe, despite appearances. `ReadTransaction` (and below, also 49 | // `WriteTransaction`) are newtypes, which are guaranteed to have exactly 50 | // the same memory representation as the thing they wrap. Further, they 51 | // have no `impl Drop` of their own, so the resulting drop code is exactly 52 | // equal for both `Rc` and `Rc`. 53 | fn to_const(self) -> Self::Const { unsafe { mem::transmute(self) } } 54 | } 55 | impl<'a> TxExt for Rc> { 56 | type Const = Rc>; 57 | fn to_const(self) -> Self::Const { unsafe { mem::transmute(self) } } 58 | } 59 | impl<'a> TxExt for Arc> { 60 | type Const = Arc>; 61 | fn to_const(self) -> Self::Const { unsafe { mem::transmute(self) } } 62 | } 63 | impl<'a> TxExt for Arc> { 64 | type Const = Arc>; 65 | fn to_const(self) -> Self::Const { unsafe { mem::transmute(self) } } 66 | } 67 | 68 | /// Types of transaction references which can be used to construct `Cursor`s. 69 | /// 70 | /// In most cases this is simply used as an extension trait (see the examples 71 | /// on `Cursor`). However, it can also be used to abstract over things that can 72 | /// be used to create `Cursor`s if so desired. 73 | /// 74 | /// Implementations are provided for references to the three general 75 | /// transaction types, as well as `Rc` and `Arc` directly wrapping either 76 | /// concrete transaction type with a `'static` lifetime. 77 | pub trait CreateCursor<'txn> { 78 | /// Create a cursor using `self` as the reference to the containing 79 | /// transaction and `db` as the database the cursor will read from and 80 | /// write into. 81 | fn cursor<'db, DB>(&self, db: DB) 82 | -> error::Result> 83 | where DB : Into>>; 84 | } 85 | 86 | impl<'txn, 'env: 'txn> CreateCursor<'txn> for &'txn ConstTransaction<'env> { 87 | #[inline] 88 | fn cursor<'db, DB>(&self, db: DB) 89 | -> error::Result> 90 | where DB : Into>> { 91 | ConstTransaction::cursor(*self, db) 92 | } 93 | } 94 | 95 | impl<'txn, 'env: 'txn> CreateCursor<'txn> for &'txn ReadTransaction<'env> { 96 | #[inline] 97 | fn cursor<'db, DB>(&self, db: DB) 98 | -> error::Result> 99 | where DB : Into>> { 100 | ConstTransaction::cursor(*self, db) 101 | } 102 | } 103 | 104 | impl<'txn, 'env: 'txn> CreateCursor<'txn> for &'txn WriteTransaction<'env> { 105 | #[inline] 106 | fn cursor<'db, DB>(&self, db: DB) 107 | -> error::Result> 108 | where DB : Into>> { 109 | ConstTransaction::cursor(*self, db) 110 | } 111 | } 112 | 113 | impl<'txn> CreateCursor<'txn> for Rc> { 114 | #[inline] 115 | fn cursor<'db, DB>(&self, db: DB) 116 | -> error::Result> 117 | where DB : Into>> { 118 | Cursor::construct(Supercow::shared(self.clone().to_const()), 119 | db.into()) 120 | } 121 | } 122 | 123 | impl<'txn> CreateCursor<'txn> for Arc> { 124 | #[inline] 125 | fn cursor<'db, DB>(&self, db: DB) 126 | -> error::Result> 127 | where DB : Into>> { 128 | Cursor::construct(Supercow::shared(self.clone().to_const()), 129 | db.into()) 130 | } 131 | } 132 | 133 | impl<'txn> CreateCursor<'txn> for Rc> { 134 | #[inline] 135 | fn cursor<'db, DB>(&self, db: DB) 136 | -> error::Result> 137 | where DB : Into>> { 138 | Cursor::construct(Supercow::shared(self.clone().to_const()), 139 | db.into()) 140 | } 141 | } 142 | 143 | impl<'txn> CreateCursor<'txn> for Arc> { 144 | #[inline] 145 | fn cursor<'db, DB>(&self, db: DB) 146 | -> error::Result> 147 | where DB : Into>> { 148 | Cursor::construct(Supercow::shared(self.clone().to_const()), 149 | db.into()) 150 | } 151 | } 152 | 153 | /// Types of transaction references which can be used to renew `StaleCursor`s 154 | /// into functional `Cursor`s. 155 | /// 156 | /// In most cases this is simply used as an extension trait (see the examples 157 | /// on `Cursor`). However, it can also be used to abstract over things that can 158 | /// be used to create `Cursor`s if so desired. 159 | /// 160 | /// Implementations are provided for normal references to `ReadTransaction` as 161 | /// well as `Rc` and `Arc`. The latter two require the transaction's inner 162 | /// lifetime to be `'static`. 163 | pub trait AssocCursor<'txn> { 164 | /// Associates a saved read-only with this transaction. 165 | /// 166 | /// The cursor will be rebound to this transaction, but will continue using 167 | /// the same database that it was previously. 168 | fn assoc_cursor<'db>(&self, cursor: StaleCursor<'db>) 169 | -> error::Result>; 170 | } 171 | 172 | impl<'txn,'env: 'txn> AssocCursor<'txn> for &'txn ReadTransaction<'env> { 173 | fn assoc_cursor<'db>(&self, cursor: StaleCursor<'db>) 174 | -> error::Result> { 175 | ReadTransaction::assoc_cursor(*self, cursor) 176 | } 177 | } 178 | 179 | impl<'txn> AssocCursor<'txn> for Rc> { 180 | fn assoc_cursor<'db>(&self, cursor: StaleCursor<'db>) 181 | -> error::Result> { 182 | Cursor::from_stale(cursor, Supercow::shared(self.clone().to_const())) 183 | } 184 | } 185 | 186 | impl<'txn> AssocCursor<'txn> for Arc> { 187 | fn assoc_cursor<'db>(&self, cursor: StaleCursor<'db>) 188 | -> error::Result> { 189 | Cursor::from_stale(cursor, Supercow::shared(self.clone().to_const())) 190 | } 191 | } 192 | 193 | /// Translates a value into a byte slice to be stored in LMDB. 194 | /// 195 | /// This is similar to `AsRef<[u8]>`, but is separate since there are 196 | /// things one may wish to store in LMDB but not have implicitly coerce to 197 | /// `&[u8]` in other contexts. 198 | /// 199 | /// Blanket impls are provided for `LmdbRaw` and for slices of `LmdbRaw` 200 | /// values. Ideally there'd be one for anything `AsRef<[u8]>`, but in Rust 201 | /// 1.10 that's not possible due to coherence rules barring having 202 | /// blanket implementations for both `LmdbRaw` and `AsRef<[u8]>`, so 203 | /// currently it is provided only for `&str` and `&Vec`. 204 | /// 205 | /// _This is not a general-purpose serialisation mechanism._ There is no 206 | /// way to use this trait to store values in a format other than how they 207 | /// are natively represented in memory. Doing this requires serialisation 208 | /// into a byte array before passing it onto lmdb-zero. 209 | pub trait AsLmdbBytes { 210 | /// Casts the given reference to a byte slice appropriate for storage 211 | /// in LMDB. 212 | fn as_lmdb_bytes(&self) -> &[u8]; 213 | } 214 | 215 | /// Inverts `AsLmdbBytes`, producing a reference to a structure inside a 216 | /// byte array. 217 | /// 218 | /// Blanket implementations are provided for `LmdbRaw` and slices of 219 | /// `LmdbRaw` things. 220 | /// 221 | /// _This is not a general-purpose deserialisation mechanism._ There is no 222 | /// way to use this trait to read values in any format other than how they 223 | /// are natively represented in memory. The only control is that outright 224 | /// invalid values can be rejected so as to avoid undefined behaviour from, 225 | /// eg, constructing `&str`s with malformed content. Reading values not in 226 | /// native format requires extracting the byte slice and using a separate 227 | /// deserialisation mechanism. 228 | pub trait FromLmdbBytes { 229 | /// Given a byte slice, return an instance of `Self` described, or 230 | /// `Err` with an error message if the given byte slice is not an 231 | /// appropriate value. 232 | fn from_lmdb_bytes(&[u8]) -> Result<&Self, String>; 233 | } 234 | 235 | /// Like `FromLmdbBytes`, but can be used with `put_reserve()` calls. 236 | /// 237 | /// A blanket implementation is provided for anything which is `LmdbRaw`. 238 | pub trait FromReservedLmdbBytes { 239 | /// Given a mutable byte slice containing arbitrary data, return an 240 | /// instance of `Self`. 241 | /// 242 | /// This is not allowed to fail, since there is no control over the 243 | /// original content of the slice. 244 | /// 245 | /// ## Unsafety 246 | /// 247 | /// This function is allowed to blindly assume that the byte slice is 248 | /// an appropriate size. 249 | unsafe fn from_reserved_lmdb_bytes(&mut [u8]) -> &mut Self; 250 | } 251 | 252 | /// Marker trait indicating a value is to be stored in LMDB by simply 253 | /// copying it in. 254 | /// 255 | /// This trait implies that one wants integers and such in native byte order 256 | /// and doesn't care about inter-ABI portability of the values. There are a lot 257 | /// of safety implications as well. 258 | /// 259 | /// Implementing this trait provides blanket implementations of 260 | /// `AsLmdbBytes`, `FromLmdbBytes`, and `FromReservedLmdbBytes`. 261 | /// 262 | /// See also [`LmdbRawIfUnaligned`](trait.LmdbRawIfUnaligned.html) for types 263 | /// that become `LmdbRaw` when wrapped with 264 | /// [`Unaligned`](../struct.Unaligned.html). In particular, all integer and 265 | /// floating-point types are `LmdbRawIfUnaligned`, except for `u8` and `i8` 266 | /// which are also `LmdbRaw`. 267 | /// 268 | /// ## Alignment 269 | /// 270 | /// The `FromLmdbBytes` conversion fails if the alignment of the input data 271 | /// does not satisfy the alignment of the type. This means that behaviour will 272 | /// be unpredictable if the required alignment of the struct is greater than 1, 273 | /// as conversions will pass or fail depending on where LMDB decides to place 274 | /// the value. 275 | /// 276 | /// If you run into this issue, there are several ways to work around it. 277 | /// 278 | /// ### Use `Unaligned` 279 | /// 280 | /// Instead of directly reading and writing the bare type, wrap it in 281 | /// `lmdb_zero::Unaligned`. This adds no overhead in and of itself and removes 282 | /// the alignment restriction, but heavily restricts what can be done with a 283 | /// reference without copying it. 284 | /// 285 | /// This is almost always the best option if your type fits in a register or 286 | /// two. 287 | /// 288 | /// ### Make your structure `#[repr(C, packed)]` 289 | /// 290 | /// If this is a problem, you can make your structure `#[repr(packed)]` to give 291 | /// it an alignment of 1 (but see also below about padding). 292 | /// 293 | /// Note that it is possible to produce unsafe code using this approach even 294 | /// without the use of `unsafe`. See [this rust 295 | /// bug](https://github.com/rust-lang/rust/issues/27060). 296 | /// 297 | /// ### Do it yourself 298 | /// 299 | /// If you have unusual requirements, your best bet is to implement 300 | /// `FromLmdbBytes` and friends manually as needed. 301 | /// 302 | /// ## Unsafety 303 | /// 304 | /// If the tagged type contains pointers of any kind, they will be stored in 305 | /// and retrieved from the database, which has serious ramifications, 306 | /// especially when the `FromReservedLmdbBytes` implementation is used. If the 307 | /// type contains Rust references, this will almost certainly lead to undefined 308 | /// behaviour. 309 | /// 310 | /// Behaviour is undefined if there exist bit patterns of the same size of the 311 | /// type which are not valid instances of that type unless the client code can 312 | /// somehow guarantee that such bit patterns do not occur. If this is a 313 | /// problem, implement `AsLmdbBytes` and `FromLmdbBytes` manually and check for 314 | /// validity. Of particular note, `bool` and essentially all `enum` types are 315 | /// not sensible for use with `LmdbRaw` (directly or within composites) because 316 | /// they are only valid for particular bit patterns. 317 | /// 318 | /// ## Warnings about inner padding 319 | /// 320 | /// Use of this trait on a struct that is not `#[repr(packed)]` makes it 321 | /// possible to observe the normally unobservable padding bytes inserted into 322 | /// the structure to satisfy type alignment. 323 | /// 324 | /// When simply using these structures as values in a non-`DUPSORT` database, 325 | /// this simply means some arbitrary extra bytes get written with the values. 326 | /// This is not going to be a problem unless you plan on sharing the databases 327 | /// with untrusted third parties (which could leak sensitive information) or do 328 | /// unusual things with type punning. 329 | /// 330 | /// However, in any context where values need to be compared (ie, keys, and 331 | /// values in `DUPSORT` databases), these padding bytes now count towards the 332 | /// comparison by default. Since the padding contains unpredictable values, you 333 | /// can easily end up with multiple "identical" keys that differ in their 334 | /// padding bytes, fail to find values you know are in the database because of 335 | /// differences in padding values, etc. 336 | /// 337 | /// One way to deal with both problems is to use `#[repr(packed)]` (in addition 338 | /// to `#[repr(C)]` which keeps the field order defined across Rust versions), 339 | /// which simply eliminates the padding bytes altogether. Note that due to a 340 | /// bug in the Rust compiler, [packed structures can lead to undefined 341 | /// behaviour in "safe Rust"](https://github.com/rust-lang/rust/issues/27060). 342 | /// Until that issue is fixed, you should be careful about using 343 | /// `#[repr(packed)]` for this purpose unless all fields in the struct have the 344 | /// same size or you understand the ABI(s) you care about well enough to know 345 | /// whether misalignment will cause issues. 346 | /// 347 | /// You can alternatively opt to live with the padding bytes, but additionally 348 | /// implement `LmdbOrdKey` on the type, and then use 349 | /// `DatabaseOptions::sort_keys_as` or `DatabaseOptions::sort_values_as` 350 | /// appropriately to use the generated comparison function. As a result, the 351 | /// padding bytes will be ignored for comparison purposes, but will nontheless 352 | /// be written into the database and thus remain visible to puns and could leak 353 | /// information. 354 | /// 355 | /// ## Example 356 | /// 357 | /// ``` 358 | /// use lmdb_zero::traits::*; 359 | /// 360 | /// #[repr(C)] 361 | /// #[derive(Clone,Copy,Debug)] 362 | /// struct MyStruct { 363 | /// foo: i16, 364 | /// // See warning about alignment/padding above! 365 | /// // On AMD64, for example, we get 6 padding bytes here. 366 | /// bar: u64, 367 | /// } 368 | /// unsafe impl LmdbRaw for MyStruct { } 369 | /// ``` 370 | pub unsafe trait LmdbRaw : Copy + Sized { 371 | /// Returns the name of this type to report in error messages. 372 | /// 373 | /// If not implemented, defaults to `"?"`. 374 | fn reported_type() -> String { 375 | "?".to_owned() 376 | } 377 | } 378 | 379 | /// Marker trait for types where `Unaligned` is `LmdbRaw`. 380 | /// 381 | /// This has all the implications as `LmdbRaw`, except that blanket 382 | /// implementations around the bare type are not available. This forces the 383 | /// client code to wrap the type in `Unaligned` to explicitly handle possible 384 | /// misalignment. 385 | /// 386 | /// All integer and floating-point types have this trait. 387 | /// 388 | /// Note that `LmdbRawIfUnaligned` is not blanket-implemented for fixed-size 389 | /// arrays, because currently doing so would preclude a blanket implementation 390 | /// of `LmdbRaw` for fixed-size arrays. Since the latter is generally more 391 | /// useful and is more consistent since variable-length slices can only 392 | /// usefully interact with `LmdbRaw`, that approach was chosen. 393 | /// 394 | /// All `LmdbRaw` types are `LmdbRawIfUnaligned`. 395 | pub unsafe trait LmdbRawIfUnaligned : Copy + Sized { 396 | /// Returns the name of this type to report in error messages. 397 | /// 398 | /// If not implemented, defaults to `"?"`. 399 | fn reported_type() -> String { 400 | "?".to_owned() 401 | } 402 | } 403 | 404 | unsafe impl LmdbRawIfUnaligned for T { 405 | fn reported_type() -> String { 406 | ::reported_type() 407 | } 408 | } 409 | 410 | /// Trait describing a value which can be used as an LMDB key by having LMDB 411 | /// call into the value's `Ord` implementation. 412 | /// 413 | /// ## Unsafety 414 | /// 415 | /// Behaviour is undefined if the `FromLmdbBytes` or `Ord` implementations 416 | /// panic. 417 | pub unsafe trait LmdbOrdKey : FromLmdbBytes + Ord { 418 | /// Returns whether the default LMDB byte-by-byte comparison is correct for 419 | /// valid values of this type. 420 | /// 421 | /// Generally, one should not specifically use 422 | /// `DatabaseOptions::sort_values_as` and so forth for types where this is 423 | /// the case. This function exists to support generic code wishing to avoid 424 | /// the conversion overhead when the types happen to already be 425 | /// byte-comparable. 426 | /// 427 | /// Note that if this returns true, that does _not_ mean that byte 428 | /// comparison is exactly equivalent to `Ord`-based comparison. For 429 | /// example, invalid `str` instances sort before valid ones and are equal 430 | /// to each other, but byte comparison will intermingle them. Because of 431 | /// this, `DatabaseOptions::sort_values_as` and similar do not make 432 | /// decisions based on this value; it is the client code's responsibility 433 | /// to use this if so desired. 434 | /// 435 | /// ## Example 436 | /// ``` 437 | /// # use lmdb_zero::traits::LmdbOrdKey; 438 | /// assert!(::ordered_by_bytes()); 439 | /// assert!(!::ordered_by_bytes()); 440 | /// assert!(::ordered_by_bytes()); 441 | /// assert!(<[u8] as LmdbOrdKey>::ordered_by_bytes()); 442 | /// assert!(!<[i8] as LmdbOrdKey>::ordered_by_bytes()); 443 | /// ``` 444 | fn ordered_by_bytes() -> bool { false } 445 | 446 | /// Returns whether LMDB will correctly handle this value with the 447 | /// `INTEGERKEY` or `INTEGERDUP` flags. 448 | /// 449 | /// There's generally no reason to use `sort_keys_as` and so forth with 450 | /// values where this is true instead of using the appropriate flags. This 451 | /// function exists to support generic code which wants to make such 452 | /// decisions automatically. 453 | fn ordered_as_integer() -> bool { false } 454 | } 455 | 456 | /// Marker trait for types where `Unaligned` is `LmdbOrdKey`. 457 | /// 458 | /// All `LmdbOrdKey + LmdbRaw` are `LmdbOrdKeyIfUnaligned`. 459 | /// 460 | /// ## Unsafety 461 | /// 462 | /// Behaviour is undefined if the `FromLmdbBytes` or `Ord` implementations 463 | /// panic. 464 | pub unsafe trait LmdbOrdKeyIfUnaligned : LmdbRawIfUnaligned + Ord { 465 | /// Like `LmdbOrdKey::ordered_by_bytes()` 466 | fn ordered_by_bytes() -> bool { false } 467 | /// Like `LmdbOrdKey::ordered_as_integer()` 468 | fn ordered_as_integer() -> bool { false } 469 | } 470 | 471 | unsafe impl LmdbOrdKeyIfUnaligned for T { 472 | fn ordered_by_bytes() -> bool { 473 | ::ordered_by_bytes() 474 | } 475 | 476 | fn ordered_as_integer() -> bool { 477 | ::ordered_as_integer() 478 | } 479 | } 480 | 481 | macro_rules! raw { 482 | ($typ:ident) => { 483 | unsafe impl LmdbRawIfUnaligned for $typ { 484 | fn reported_type() -> String { 485 | stringify!($typ).to_owned() 486 | } 487 | } 488 | impl AsLmdbBytes for $typ { 489 | fn as_lmdb_bytes(&self) -> &[u8] { 490 | unsafe { 491 | slice::from_raw_parts( 492 | self as *const $typ as *const u8, 493 | mem::size_of::<$typ>()) 494 | } 495 | } 496 | } 497 | impl AsLmdbBytes for [$typ] { 498 | fn as_lmdb_bytes(&self) -> &[u8] { 499 | unsafe { 500 | slice::from_raw_parts( 501 | self.as_ptr() as *const u8, 502 | self.len() * mem::size_of::<$typ>()) 503 | } 504 | } 505 | } 506 | }; 507 | 508 | ($typ:ident, Ord) => { 509 | raw!($typ); 510 | unsafe impl LmdbOrdKeyIfUnaligned for $typ { } 511 | }; 512 | 513 | ($typ:ident, Int) => { 514 | raw!($typ); 515 | unsafe impl LmdbOrdKeyIfUnaligned for $typ { 516 | fn ordered_as_integer() -> bool { true } 517 | } 518 | }; 519 | } 520 | 521 | unsafe impl LmdbRaw for u8 { 522 | fn reported_type() -> String { 523 | "u8".to_owned() 524 | } 525 | } 526 | unsafe impl LmdbOrdKey for u8 { 527 | fn ordered_by_bytes() -> bool { true } 528 | } 529 | unsafe impl LmdbRaw for i8 { 530 | fn reported_type() -> String { 531 | "i8".to_owned() 532 | } 533 | } 534 | unsafe impl LmdbOrdKey for i8 { } 535 | raw!(u16, Ord); 536 | raw!(i16, Ord); 537 | raw!(u32, Int); 538 | raw!(i32, Ord); 539 | raw!(u64, Ord); 540 | raw!(i64, Ord); 541 | raw!(f32); 542 | raw!(f64); 543 | 544 | macro_rules! raw_array { 545 | ($n:expr) => { 546 | unsafe impl LmdbRaw for [V;$n] { 547 | fn reported_type() -> String { 548 | format!("[{};{}]", V::reported_type(), $n) 549 | } 550 | } 551 | unsafe impl LmdbOrdKey for [V;$n] { 552 | fn ordered_by_bytes() -> bool { V::ordered_by_bytes() } 553 | } 554 | } 555 | } 556 | raw_array!(0); 557 | raw_array!(1); 558 | raw_array!(2); 559 | raw_array!(3); 560 | raw_array!(4); 561 | raw_array!(5); 562 | raw_array!(6); 563 | raw_array!(7); 564 | raw_array!(8); 565 | raw_array!(9); 566 | raw_array!(10); 567 | raw_array!(11); 568 | raw_array!(12); 569 | raw_array!(13); 570 | raw_array!(14); 571 | raw_array!(15); 572 | raw_array!(16); 573 | raw_array!(17); 574 | raw_array!(18); 575 | raw_array!(19); 576 | raw_array!(20); 577 | raw_array!(21); 578 | raw_array!(22); 579 | raw_array!(23); 580 | raw_array!(24); 581 | raw_array!(25); 582 | raw_array!(26); 583 | raw_array!(27); 584 | raw_array!(28); 585 | raw_array!(29); 586 | raw_array!(30); 587 | raw_array!(31); 588 | raw_array!(32); 589 | 590 | unsafe impl LmdbRawIfUnaligned for Wrapping { 591 | fn reported_type() -> String { 592 | format!("Wrapping<{}>", V::reported_type()) 593 | } 594 | } 595 | unsafe impl LmdbOrdKeyIfUnaligned for Wrapping { 596 | fn ordered_by_bytes() -> bool { V::ordered_by_bytes() } 597 | } 598 | 599 | unsafe impl LmdbRaw for () { } 600 | 601 | impl AsLmdbBytes for V { 602 | fn as_lmdb_bytes(&self) -> &[u8] { 603 | unsafe { 604 | slice::from_raw_parts( 605 | self as *const V as *const u8, mem::size_of::()) 606 | } 607 | } 608 | } 609 | 610 | impl FromLmdbBytes for V { 611 | fn from_lmdb_bytes(bytes: &[u8]) -> Result<&Self, String> { 612 | let size = mem::size_of::(); 613 | let align = mem::align_of::(); 614 | 615 | if bytes.len() != size { 616 | return Err( 617 | format!("Type {} is size {}, but byte array has size {}", 618 | V::reported_type(), size, bytes.len())); 619 | } 620 | 621 | let misalign = (bytes.as_ptr() as usize) % align; 622 | if 0 != misalign { 623 | return Err( 624 | format!("Type {} requires alignment {}, but byte array \ 625 | at {:08x} is misaligned by {} bytes \ 626 | (see https://docs.rs/lmdb-zero/{}/\ 627 | lmdb_zero/traits/trait.LmdbRaw.html#alignment)", 628 | V::reported_type(), align, 629 | (bytes.as_ptr() as usize), misalign, 630 | env!("CARGO_PKG_VERSION"))); 631 | } 632 | 633 | Ok(unsafe { 634 | mem::transmute(bytes.as_ptr()) 635 | }) 636 | } 637 | } 638 | 639 | impl FromReservedLmdbBytes for V { 640 | unsafe fn from_reserved_lmdb_bytes(bytes: &mut [u8]) -> &mut Self { 641 | mem::transmute(bytes.as_ptr()) 642 | } 643 | } 644 | 645 | impl AsLmdbBytes for [V] { 646 | fn as_lmdb_bytes(&self) -> &[u8] { 647 | unsafe { 648 | slice::from_raw_parts( 649 | self.as_ptr() as *const u8, 650 | self.len() * mem::size_of::()) 651 | } 652 | } 653 | } 654 | 655 | impl FromLmdbBytes for [V] { 656 | fn from_lmdb_bytes(bytes: &[u8]) -> Result<&Self, String> { 657 | let size = mem::size_of::(); 658 | let align = mem::align_of::(); 659 | 660 | let size_mod = bytes.len() % size; 661 | if 0 != size_mod { 662 | return Err( 663 | format!("Type [{}] must have a size which is a multiple \ 664 | of {}, but byte array has size {} ({} trailing bytes)", 665 | V::reported_type(), size, bytes.len(), size_mod)); 666 | } 667 | 668 | let misalign = (bytes.as_ptr() as usize) % align; 669 | if 0 != misalign { 670 | return Err( 671 | format!("Type [{}] requires alignment {}, but byte array \ 672 | at {:08x} is misaligned by {} bytes \ 673 | (see https://docs.rs/lmdb-zero/{}/\ 674 | lmdb_zero/traits/trait.LmdbRaw.html#alignment)", 675 | V::reported_type(), align, 676 | (bytes.as_ptr() as usize), misalign, 677 | env!("CARGO_PKG_VERSION"))); 678 | } 679 | 680 | unsafe { 681 | Ok(slice::from_raw_parts( 682 | bytes.as_ptr() as *const V, 683 | bytes.len() / size)) 684 | } 685 | } 686 | } 687 | 688 | impl FromReservedLmdbBytes for [V] { 689 | unsafe fn from_reserved_lmdb_bytes(bytes: &mut [u8]) -> &mut Self { 690 | slice::from_raw_parts_mut( 691 | bytes.as_ptr() as *mut V, 692 | bytes.len() / mem::size_of::()) 693 | } 694 | } 695 | 696 | unsafe impl LmdbOrdKey for [V] { 697 | fn ordered_by_bytes() -> bool { V::ordered_by_bytes() } 698 | } 699 | 700 | impl AsLmdbBytes for CStr { 701 | /// Returns the raw content of the `CStr`, including the trailing NUL. 702 | fn as_lmdb_bytes(&self) -> &[u8] { 703 | self.to_bytes_with_nul() 704 | } 705 | } 706 | 707 | impl FromLmdbBytes for CStr { 708 | /// Directly converts the byte slice into a `CStr`, including a 709 | /// required trailing NUL. 710 | fn from_lmdb_bytes(bytes: &[u8]) -> Result<&Self, String> { 711 | CStr::from_bytes_with_nul(bytes).map_err( 712 | |_| "NUL byte in CString value".to_owned()) 713 | } 714 | } 715 | 716 | unsafe impl LmdbOrdKey for CStr { 717 | fn ordered_by_bytes() -> bool { true } 718 | } 719 | 720 | impl AsLmdbBytes for str { 721 | fn as_lmdb_bytes(&self) -> &[u8] { 722 | self.as_bytes() 723 | } 724 | } 725 | 726 | impl FromLmdbBytes for str { 727 | fn from_lmdb_bytes(bytes: &[u8]) -> Result<&str, String> { 728 | str::from_utf8(bytes).map_err( 729 | |_| "String is not valid UTF-8".to_owned()) 730 | } 731 | } 732 | 733 | unsafe impl LmdbOrdKey for str { 734 | fn ordered_by_bytes() -> bool { true } 735 | } 736 | 737 | impl AsLmdbBytes for Vec { 738 | fn as_lmdb_bytes(&self) -> &[u8] { 739 | &self[..].as_lmdb_bytes() 740 | } 741 | } 742 | 743 | static IGNORE: Ignore = Ignore; 744 | 745 | impl FromLmdbBytes for Ignore { 746 | fn from_lmdb_bytes(_: &[u8]) -> Result<&Ignore, String> { 747 | Ok(&IGNORE) 748 | } 749 | } 750 | 751 | impl AsLmdbBytes for char { 752 | fn as_lmdb_bytes(&self) -> &[u8] { 753 | unsafe { 754 | slice::from_raw_parts( 755 | self as *const char as *const u8, 756 | mem::size_of::()) 757 | } 758 | } 759 | } 760 | 761 | impl AsLmdbBytes for [char] { 762 | fn as_lmdb_bytes(&self) -> &[u8] { 763 | unsafe { 764 | slice::from_raw_parts( 765 | self.as_ptr() as *const u8, 766 | self.len() * mem::size_of::()) 767 | } 768 | } 769 | } 770 | -------------------------------------------------------------------------------- /src/tx.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 FullContact, Inc 2 | // Copyright 2017 Jason Lingle 3 | // 4 | // Licensed under the Apache License, Version 2.0 or the MIT license 6 | // , at your 7 | // option. This file may not be copied, modified, or distributed 8 | // except according to those terms. 9 | 10 | use std::cell::Cell; 11 | use std::mem; 12 | use std::ops::{Deref, DerefMut}; 13 | use std::ptr; 14 | use libc::c_uint; 15 | 16 | use ffi; 17 | use ffi2; 18 | use supercow::{Supercow, NonSyncSupercow}; 19 | 20 | use env::{self, Environment, Stat}; 21 | use dbi::{db, Database}; 22 | use error::{Error, Result}; 23 | use mdb_vals::*; 24 | use traits::*; 25 | use cursor::{self, Cursor, StaleCursor}; 26 | 27 | /// Flags used when calling the various `put` functions. 28 | pub mod put { 29 | use ffi; 30 | use libc; 31 | 32 | bitflags! { 33 | /// Flags used when calling the various `put` functions. 34 | /// 35 | /// Note that `RESERVE` and `MULTIPLE` are not exposed in these flags 36 | /// because their memory ownership and/or parameter semantics are 37 | /// different. `CURRENT` is expressed separately on the cursor 38 | /// functions. 39 | pub struct Flags : libc::c_uint { 40 | /// Enter the new key/data pair only if it does not already appear 41 | /// in the database. This flag may only be specified if the 42 | /// database was opened with `DUPSORT`. The function will return 43 | /// `KEYEXIST` if the key/data pair already appears in the 44 | /// database. 45 | /// 46 | /// ## Example 47 | /// 48 | /// ``` 49 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 50 | /// # fn main() { 51 | /// # let env = create_env(); 52 | /// let db = lmdb::Database::open( 53 | /// &env, Some("reversed"), 54 | /// &lmdb::DatabaseOptions::create_multimap_unsized::()) 55 | /// .unwrap(); 56 | /// let txn = lmdb::WriteTransaction::new(&env).unwrap(); 57 | /// { 58 | /// let mut access = txn.access(); 59 | /// access.put(&db, "Fruit", "Apple", lmdb::put::Flags::empty()).unwrap(); 60 | /// access.put(&db, "Fruit", "Orange", lmdb::put::Flags::empty()).unwrap(); 61 | /// // Duplicate, but that's OK by default 62 | /// access.put(&db, "Fruit", "Apple", lmdb::put::Flags::empty()).unwrap(); 63 | /// // `NODUPDATA` blocks adding an identical item 64 | /// assert!(access.put(&db, "Fruit", "Apple", lmdb::put::NODUPDATA).is_err()); 65 | /// // But doesn't affect pairs not already present 66 | /// access.put(&db, "Fruit", "Durian", lmdb::put::NODUPDATA).unwrap(); 67 | /// } 68 | /// txn.commit().unwrap(); 69 | /// # } 70 | /// ``` 71 | /// 72 | /// When used on a cursor, the cursor is positioned at the 73 | /// conflicting key/value pair if this results in a `KEYEXIST` 74 | /// error. 75 | /// 76 | /// ``` 77 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 78 | /// # fn main() { 79 | /// # let env = create_env(); 80 | /// let db = lmdb::Database::open( 81 | /// &env, Some("reversed"), 82 | /// &lmdb::DatabaseOptions::create_multimap_unsized::()) 83 | /// .unwrap(); 84 | /// let txn = lmdb::WriteTransaction::new(&env).unwrap(); 85 | /// { 86 | /// let mut access = txn.access(); 87 | /// access.put(&db, "Fruit", "Apple", lmdb::put::Flags::empty()).unwrap(); 88 | /// access.put(&db, "Fruit", "Orange", lmdb::put::Flags::empty()).unwrap(); 89 | /// access.put(&db, "Fruit", "Durian", lmdb::put::Flags::empty()).unwrap(); 90 | /// 91 | /// let mut cursor = txn.cursor(&db).unwrap(); 92 | /// assert_eq!(Err(lmdb::Error::Code(lmdb::error::KEYEXIST)), 93 | /// cursor.put(&mut access, "Fruit", "Durian", 94 | /// lmdb::put::NODUPDATA)); 95 | /// assert_eq!(("Fruit", "Durian"), cursor.get_current(&access).unwrap()); 96 | /// } 97 | /// txn.commit().unwrap(); 98 | /// # } 99 | /// ``` 100 | const NODUPDATA = ffi::MDB_NODUPDATA; 101 | /// Enter the new key/data pair only if the key does not already 102 | /// appear in the database. The function will return `KEYEXIST` if 103 | /// the key already appears in the database, even if the database 104 | /// supports duplicates (`DUPSORT`). 105 | /// 106 | /// ## Examples 107 | /// 108 | /// ### In a 1:1 database 109 | /// 110 | /// ``` 111 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 112 | /// # fn main() { 113 | /// # let env = create_env(); 114 | /// let db = lmdb::Database::open( 115 | /// &env, None, &lmdb::DatabaseOptions::defaults()) 116 | /// .unwrap(); 117 | /// let txn = lmdb::WriteTransaction::new(&env).unwrap(); 118 | /// { 119 | /// let mut access = txn.access(); 120 | /// access.put(&db, "Fruit", "Apple", lmdb::put::Flags::empty()).unwrap(); 121 | /// // By default, collisions overwrite the old value 122 | /// access.put(&db, "Fruit", "Orange", lmdb::put::Flags::empty()).unwrap(); 123 | /// assert_eq!("Orange", access.get::(&db, "Fruit").unwrap()); 124 | /// // But `NOOVERWRITE` prevents that 125 | /// assert!(access.put(&db, "Fruit", "Durian", lmdb::put::NOOVERWRITE).is_err()); 126 | /// assert_eq!("Orange", access.get::(&db, "Fruit").unwrap()); 127 | /// } 128 | /// txn.commit().unwrap(); 129 | /// # } 130 | /// ``` 131 | /// 132 | /// ### In a `DUPSORT` database 133 | /// 134 | /// ``` 135 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 136 | /// # fn main() { 137 | /// # let env = create_env(); 138 | /// let db = lmdb::Database::open( 139 | /// &env, Some("reversed"), 140 | /// &lmdb::DatabaseOptions::create_multimap_unsized::()) 141 | /// .unwrap(); 142 | /// let txn = lmdb::WriteTransaction::new(&env).unwrap(); 143 | /// { 144 | /// let mut access = txn.access(); 145 | /// // Ordinarily, we can add multiple items per key 146 | /// access.put(&db, "Fruit", "Apple", lmdb::put::Flags::empty()).unwrap(); 147 | /// access.put(&db, "Fruit", "Orange", lmdb::put::Flags::empty()).unwrap(); 148 | /// let mut cursor = txn.cursor(&db).unwrap(); 149 | /// cursor.seek_k::(&access, "Fruit").unwrap(); 150 | /// assert_eq!(2, cursor.count().unwrap()); 151 | /// 152 | /// // But this can be prevented with `NOOVERWRITE` 153 | /// access.put(&db, "Veggie", "Carrot", lmdb::put::NOOVERWRITE).unwrap(); 154 | /// assert!(access.put(&db, "Veggie", "Squash", lmdb::put::NOOVERWRITE).is_err()); 155 | /// cursor.seek_k::(&access, "Veggie").unwrap(); 156 | /// assert_eq!(1, cursor.count().unwrap()); 157 | /// } 158 | /// txn.commit().unwrap(); 159 | /// # } 160 | /// ``` 161 | // TODO: "The data parameter will be set to point to the existing 162 | // item." We should provide functionality to support that. 163 | const NOOVERWRITE = ffi::MDB_NOOVERWRITE; 164 | /// Append the given key/data pair to the end of the database. This 165 | /// option allows fast bulk loading when keys are already known to 166 | /// be in the correct order. Loading unsorted keys with this flag 167 | /// will cause a `KEYEXIST` error. 168 | /// 169 | /// ## Example 170 | /// 171 | /// ``` 172 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 173 | /// # fn main() { 174 | /// # let env = create_env(); 175 | /// let db = lmdb::Database::open( 176 | /// &env, None, &lmdb::DatabaseOptions::defaults()) 177 | /// .unwrap(); 178 | /// let txn = lmdb::WriteTransaction::new(&env).unwrap(); 179 | /// { 180 | /// let mut access = txn.access(); 181 | /// // Load values in ascending order 182 | /// access.put(&db, "France", "Paris", lmdb::put::APPEND).unwrap(); 183 | /// access.put(&db, "Germany", "Berlin", lmdb::put::APPEND).unwrap(); 184 | /// access.put(&db, "Latvia", "Rīga", lmdb::put::APPEND).unwrap(); 185 | /// // Error if you violate ordering 186 | /// assert!(access.put(&db, "Armenia", "Yerevan", lmdb::put::APPEND) 187 | /// .is_err()); 188 | /// } 189 | /// txn.commit().unwrap(); 190 | /// # } 191 | /// ``` 192 | const APPEND = ffi::MDB_APPEND; 193 | /// As with `APPEND` above, but for sorted dup data. 194 | const APPENDDUP = ffi::MDB_APPENDDUP; 195 | } 196 | } 197 | } 198 | 199 | /// Flags used when deleting items. 200 | pub mod del { 201 | use ffi; 202 | use libc; 203 | 204 | bitflags! { 205 | /// Flags used when deleting items via cursors. 206 | pub struct Flags : libc::c_uint { 207 | /// Delete all of the data items for the current key instead of 208 | /// just the current item. This flag may only be specified if the 209 | /// database was opened with `DUPSORT`. 210 | /// 211 | /// ## Example 212 | /// 213 | /// ``` 214 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 215 | /// # fn main() { 216 | /// # let env = create_env(); 217 | /// let db = lmdb::Database::open( 218 | /// &env, Some("reversed"), 219 | /// &lmdb::DatabaseOptions::create_multimap_unsized::()) 220 | /// .unwrap(); 221 | /// let txn = lmdb::WriteTransaction::new(&env).unwrap(); 222 | /// { 223 | /// let mut access = txn.access(); 224 | /// let f = lmdb::put::Flags::empty(); 225 | /// access.put(&db, "Fruit", "Apple", f).unwrap(); 226 | /// access.put(&db, "Fruit", "Orange", f).unwrap(); 227 | /// access.put(&db, "Fruit", "Durian", f).unwrap(); 228 | /// 229 | /// let mut cursor = txn.cursor(&db).unwrap(); 230 | /// cursor.seek_kv("Fruit", "Durian").unwrap(); 231 | /// // By default, only the current item is deleted. 232 | /// cursor.del(&mut access, lmdb::del::Flags::empty()).unwrap(); 233 | /// cursor.seek_k::(&access, "Fruit").unwrap(); 234 | /// assert_eq!(2, cursor.count().unwrap()); 235 | /// // But with `NODUPDATA`, they will all go away 236 | /// cursor.del(&mut access, lmdb::del::NODUPDATA).unwrap(); 237 | /// assert!(cursor.seek_k::(&access, "Fruit").is_err()); 238 | /// } 239 | /// txn.commit().unwrap(); 240 | /// # } 241 | /// ``` 242 | const NODUPDATA = ffi::MDB_NODUPDATA; 243 | } 244 | } 245 | } 246 | 247 | // This is internal, but used by other parts of the library 248 | #[derive(Debug)] 249 | pub struct TxHandle(pub *mut ffi::MDB_txn); 250 | 251 | impl Drop for TxHandle { 252 | fn drop(&mut self) { 253 | if !self.0.is_null() { 254 | unsafe { 255 | ffi::mdb_txn_abort(self.0); 256 | } 257 | self.0 = ptr::null_mut(); 258 | } 259 | } 260 | } 261 | 262 | impl TxHandle { 263 | pub unsafe fn commit(&mut self) -> Result<()> { 264 | let txn_p = mem::replace(&mut self.0, ptr::null_mut()); 265 | lmdb_call!(ffi::mdb_txn_commit(txn_p)); 266 | Ok(()) 267 | } 268 | } 269 | 270 | /// Base functionality for an LMDB transaction. 271 | /// 272 | /// The type is "const" in a similar usage to the modifier in C: One cannot use 273 | /// it to make any modifications, but also cannot rely on it actually being 274 | /// read-only. `ConstTransaction`s are used to write code that can operate in 275 | /// either kind of transaction. 276 | /// 277 | /// Unlike most other LMDB wrappers, transactions here are (indirectly) the 278 | /// things in control of accessing data behind cursors. This is in order to 279 | /// correctly express memory semantics: Moving a cursor does not invalidate 280 | /// memory obtained from the cursor; however, any mutation through the same 281 | /// transaction does. We therefore model accesses to data in the environment as 282 | /// borrows of the transaction and the database themselves (possibly mutable on 283 | /// the latter), which allows the borrow checker to ensure that all references 284 | /// are dropped before doing a structural modification. 285 | /// 286 | /// Note that due to limitations in the Rust borrow checker, one actually needs 287 | /// to use the `*Accessor` structs to access data. Any transaction will yield 288 | /// at most one accessor, which is implemented with a runtime check that should 289 | /// in the vast majority of cases get optimised out. 290 | /// 291 | /// Mutability of a transaction reference does not indicate mutability of the 292 | /// underlying database, but rather exclusivity for enforcement of child 293 | /// transaction semantics. 294 | /// 295 | /// ## Ownership 296 | /// 297 | /// Transactions support all three ownership modes (but owned mode is not 298 | /// useful). See `ReadTransaction` and `WriteTransaction` for details. 299 | /// 300 | /// ## Lifetime 301 | /// 302 | /// A `ConstTransaction` must be strictly outlived by its `Environment`. 303 | /// 304 | /// `'env` is covariant: given two lifetimes `'x` and `'y` where `'x: 'y`, a 305 | /// `&ConstTransaction<'x>` will implicitly coerce to `&ConstTransaction<'y>`. 306 | /// 307 | /// ```rust,norun 308 | /// # #![allow(dead_code)] 309 | /// # extern crate lmdb_zero as lmdb; 310 | /// # fn main() { } 311 | /// # 312 | /// fn convariance<'x, 'y>(db: &lmdb::ConstTransaction<'x>) 313 | /// where 'x: 'y { 314 | /// let _db2: &lmdb::ConstTransaction<'y> = db; 315 | /// } 316 | /// ``` 317 | /// 318 | /// Because of this property, if you need to hold onto an 319 | /// `&lmdb::ConstTransaction` and must explicitly name both lifetimes, 320 | /// it is usually best to use the same lifetime for both the reference and the 321 | /// parameter, eg `&'x lmdb::ConstTransaction<'x>`. 322 | #[derive(Debug)] 323 | pub struct ConstTransaction<'env> { 324 | env: NonSyncSupercow<'env, Environment>, 325 | tx: TxHandle, 326 | has_yielded_accessor: Cell, 327 | } 328 | 329 | /// A read-only LMDB transaction. 330 | /// 331 | /// In addition to all operations valid on `ConstTransaction`, a 332 | /// `ReadTransaction` can additionally operate on cursors with a lifetime 333 | /// scoped to the environment instead of the transaction. 334 | /// 335 | /// ## Ownership 336 | /// 337 | /// `ReadTransaction`s can be created with all three ownership modes (but owned 338 | /// mode is not useful). 339 | /// 340 | /// ### Example — Shared mode 341 | /// 342 | /// ``` 343 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 344 | /// use std::sync::Arc; 345 | /// 346 | /// # fn main() { 347 | /// let env = Arc::new(create_env()); 348 | /// let db = Arc::new(lmdb::Database::open( 349 | /// env.clone(), None, &lmdb::DatabaseOptions::defaults()).unwrap()); 350 | /// 351 | /// // Type and lifetime annotated explicitly for clarity 352 | /// let txn: lmdb::ReadTransaction<'static> = lmdb::ReadTransaction::new( 353 | /// env.clone()).unwrap(); 354 | /// 355 | /// // Do stuff with `txn`... 356 | /// # drop(txn); drop(db); 357 | /// # } 358 | /// ``` 359 | /// 360 | /// ## Lifetime 361 | /// 362 | /// All notes for `ConstTransaction` apply. 363 | #[derive(Debug)] 364 | // This MUST be a newtype struct and MUST NOT `impl Drop` 365 | pub struct ReadTransaction<'env>(ConstTransaction<'env>); 366 | /// A read-write LMDB transaction. 367 | /// 368 | /// In addition to all operations valid on `ConstTransaction`, it is also 369 | /// possible to perform writes to the underlying databases. 370 | /// 371 | /// 372 | /// ## Ownership 373 | /// 374 | /// `WriteTransaction`s can be created with all three ownership modes (but 375 | /// owned mode is not useful). 376 | /// 377 | /// ### Example — Shared mode 378 | /// 379 | /// ``` 380 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 381 | /// use std::sync::Arc; 382 | /// 383 | /// # fn main() { 384 | /// let env = Arc::new(create_env()); 385 | /// let db = Arc::new(lmdb::Database::open( 386 | /// env.clone(), None, &lmdb::DatabaseOptions::defaults()).unwrap()); 387 | /// 388 | /// // Type and lifetime annotated explicitly for clarity 389 | /// let txn: lmdb::WriteTransaction<'static> = lmdb::WriteTransaction::new( 390 | /// env.clone()).unwrap(); 391 | /// 392 | /// // Do stuff with `txn`... 393 | /// 394 | /// txn.commit().unwrap(); 395 | /// # } 396 | /// ``` 397 | /// 398 | /// ## Lifetime 399 | /// 400 | /// All notes for `ConstTransaction` apply. 401 | #[derive(Debug)] 402 | // This MUST be a newtype struct and MUST NOT `impl Drop` 403 | pub struct WriteTransaction<'env>(ConstTransaction<'env>); 404 | 405 | /// A read-only LMDB transaction that has been reset. 406 | /// 407 | /// It can be renewed by calling `ResetTransaction::renew()`. 408 | /// 409 | /// ## Lifetime 410 | /// 411 | /// All notes for `ReadTransaction` apply. 412 | #[derive(Debug)] 413 | pub struct ResetTransaction<'env>(ReadTransaction<'env>); 414 | 415 | /// A read-only data accessor obtained from a `ConstTransaction`. 416 | /// 417 | /// There is no corresponding `ReadAccessor`, since there are no additional 418 | /// operations one can do with a known-read-only accessor. 419 | /// 420 | /// ## Lifetime 421 | /// 422 | /// A `ConstAccessor` must be strictly outlived by its parent transaction. The 423 | /// parent transaction cannot be destroyed (committed, etc) until the borrow 424 | /// from the accessor ends. This in many cases requires adding an extra scope 425 | /// (with bare `{ }` braces) in which to obtain the accessor, as can be seen in 426 | /// many of the examples. 427 | /// 428 | /// The lifitem of a reference to a `ConstAccessor` dictates the lifetime of 429 | /// the data accessed via the accessor. 430 | /// 431 | /// The `'txn` lifetime parameter is covariant. That is, given two lifetimes 432 | /// `'x` and `'y` where `'x: 'y`, a `&ConstAccessor<'x>` can be implicitly 433 | /// coerced into a `&ConstAccessor<'y>`. 434 | /// 435 | /// ```rust,norun 436 | /// # #![allow(dead_code)] 437 | /// # extern crate lmdb_zero as lmdb; 438 | /// # fn main() { } 439 | /// # 440 | /// fn convariance<'x, 'y>(db: &lmdb::ConstAccessor<'x>) 441 | /// where 'x: 'y { 442 | /// let _db2: &lmdb::ConstAccessor<'y> = db; 443 | /// } 444 | /// ``` 445 | /// 446 | /// Because of this property, if you need to hold onto an 447 | /// `&lmdb::ConstAccessor` and must explicitly name both lifetimes, it 448 | /// is usually best to use the same lifetime for both the reference and the 449 | /// parameter, eg `&'x lmdb::ConstAccessor<'x>`. 450 | #[derive(Debug)] 451 | pub struct ConstAccessor<'txn>(&'txn ConstTransaction<'txn>); 452 | 453 | /// ConstAccessor implements Drop trait so that if it gets 454 | /// dropped, a new accessor can be safely obtained 455 | impl<'txn> Drop for ConstAccessor<'txn> { 456 | fn drop(&mut self) { 457 | self.0.has_yielded_accessor.set(false) 458 | } 459 | } 460 | 461 | /// A read-write data accessor obtained from a `WriteTransaction`. 462 | /// 463 | /// All operations that can be performed on `ConstAccessor` can also be 464 | /// performed on `WriteAccessor`. 465 | /// 466 | /// ## Lifetime 467 | /// 468 | /// Nominally, `WriteAccessor` would behave the same as `ConstAccessor`. 469 | /// 470 | /// However, there is never any useful reason to explicitly reference a 471 | /// `&WriteAccessor` (ie, a shared reference). Instead, one talks about a 472 | /// `&mut WriteAccessor`. The unfortunate consequence here is that the `'txn` 473 | /// lifetime ends up being _invariant_; that is, the following code will not 474 | /// compile: 475 | /// 476 | /// ```rust,ignore 477 | /// # #![allow(dead_code)] 478 | /// # extern crate lmdb_zero as lmdb; 479 | /// # fn main() { } 480 | /// # 481 | /// fn convariance<'x, 'y>(db: &mut lmdb::WriteAccessor<'x>) 482 | /// where 'x: 'y { 483 | /// let _db2: &mut lmdb::WriteAccessor<'y> = db; // ERROR! 484 | /// } 485 | /// ``` 486 | /// 487 | /// The compiler's error messages here tend to be unhelpful. In certain cases, 488 | /// it will suggest changing the function declaration above to something like 489 | /// `&'x mut lmdb::WriteAccessor<'x>`. Applying such a fix when it is suggested 490 | /// _will appear to work_. But what happens is that you end up propagating 491 | /// `&'txn mut lmdb::WriteAccessor<'txn>` the whole way up your call stack. 492 | /// Since `'txn` is invariant, it is inferred to be exactly equal to the 493 | /// lifetime of the transaction, and now you've declared that the borrow from 494 | /// the transaction exists for the entire lifetime of the transaction. This 495 | /// means that you cannot actually commit the transaction. 496 | /// 497 | /// Instead, make sure you always have separate type parameters on the `&mut` 498 | /// and the `WriteAccessor` itself. This can usually be accomplished by letting 499 | /// lifetime elision run its course. If you must name both, generally go with 500 | /// `&'access mut WriteAccessor<'txn>`. The `'access` lifetime is the lifetime 501 | /// of any data you obtain via the accessor. 502 | #[derive(Debug)] 503 | pub struct WriteAccessor<'txn>(ConstAccessor<'txn>); 504 | 505 | impl<'env> ConstTransaction<'env> { 506 | fn new<'outer: 'env, E>(env: E, 507 | parent: Option<&'env mut ConstTransaction<'outer>>, 508 | flags: c_uint) -> Result 509 | where E : Into> { 510 | let env : NonSyncSupercow<'env, Environment> = env.into(); 511 | 512 | let mut rawtx: *mut ffi::MDB_txn = ptr::null_mut(); 513 | unsafe { 514 | lmdb_call!(ffi::mdb_txn_begin( 515 | env::env_ptr(&env), parent.map_or(ptr::null_mut(), |p| p.tx.0), 516 | flags, &mut rawtx)); 517 | } 518 | 519 | Ok(ConstTransaction { 520 | env: env, 521 | tx: TxHandle(rawtx), 522 | has_yielded_accessor: Cell::new(false), 523 | }) 524 | } 525 | 526 | /// Returns an accessor used to manipulate data in this transaction. 527 | /// 528 | /// ## Ownership 529 | /// 530 | /// Unlike most other lmdb-zero APIs, accessors do not support shared 531 | /// ownership modes (e.g., where the accessor would hold on to a 532 | /// `Rc`). If you need dynamically-managed lifetime, 533 | /// instead simply drop the accessor and get a new one the next time one is 534 | /// needed. 535 | /// 536 | /// ## Panics 537 | /// 538 | /// Panics if this function has already been called on this transaction and 539 | /// the returned value has not yet been dropped. 540 | /// 541 | /// ## Example 542 | /// 543 | /// ```rust,should_panic 544 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 545 | /// # #[allow(unused_vars)] 546 | /// # fn main() { 547 | /// # let env = create_env(); 548 | /// let txn = lmdb::ReadTransaction::new(&env).unwrap(); 549 | /// // Get access the first time 550 | /// let access = txn.access(); 551 | /// 552 | /// // You can't get the accessor again in the same scope, since this 553 | /// // would create two references to the same logical memory and allow 554 | /// // creating aliased mutable references and so forth. 555 | /// let access2 = txn.access(); // PANIC! 556 | /// # } 557 | /// ``` 558 | #[inline] 559 | pub fn access(&self) -> ConstAccessor { 560 | assert!(!self.has_yielded_accessor.get(), 561 | "Transaction accessor already returned"); 562 | self.has_yielded_accessor.set(true); 563 | ConstAccessor(self) 564 | } 565 | 566 | /// Creates a new cursor scoped to this transaction, bound to the given 567 | /// database. 568 | /// 569 | /// This method is functionally equivalent to the method on `CreateCursor` 570 | /// and exists for convenience and backwards-compatibility. 571 | /// 572 | /// If you have an, e.g., `Rc` and want to get a 573 | /// `Cursor<'static,'db>`, make sure you have the `CreateCursor` trait 574 | /// imported so that the needed alternate implementations of this method 575 | /// are available. 576 | #[inline] 577 | pub fn cursor<'txn, 'db, DB>(&'txn self, db: DB) 578 | -> Result> 579 | where DB : Into>> { 580 | Cursor::construct(Supercow::borrowed(self), db.into()) 581 | } 582 | 583 | /// Returns the internal id of this transaction. 584 | pub fn id(&self) -> usize { 585 | unsafe { 586 | ffi2::mdb_txn_id(self.tx.0) 587 | } 588 | } 589 | 590 | /// Retrieves statistics for a database. 591 | pub fn db_stat(&self, db: &Database) -> Result { 592 | try!(db.assert_same_env(&self.env)); 593 | 594 | unsafe { 595 | let mut raw: ffi::MDB_stat = mem::zeroed(); 596 | lmdb_call!(ffi::mdb_stat(self.tx.0, db.as_raw(), &mut raw)); 597 | Ok(raw.into()) 598 | } 599 | } 600 | 601 | /// Retrieve the DB flags for a database handle. 602 | pub fn db_flags(&self, db: &Database) -> Result { 603 | try!(db.assert_same_env(&self.env)); 604 | 605 | let mut raw: c_uint = 0; 606 | unsafe { 607 | lmdb_call!(ffi::mdb_dbi_flags(self.tx.0, db.as_raw(), &mut raw)); 608 | } 609 | Ok(db::Flags::from_bits_truncate(raw)) 610 | } 611 | 612 | #[inline] 613 | fn assert_sensible_cursor(&self, cursor: &Cursor) 614 | -> Result<()> { 615 | if self as *const ConstTransaction != 616 | cursor::txn_ref(cursor) as *const ConstTransaction 617 | { 618 | Err(Error::Mismatch) 619 | } else { 620 | Ok(()) 621 | } 622 | } 623 | } 624 | 625 | // Internally used by other parts of the crate 626 | #[inline] 627 | pub fn assert_sensible_cursor(access: &ConstAccessor, cursor: &Cursor) 628 | -> Result<()> { 629 | access.0.assert_sensible_cursor(cursor) 630 | } 631 | #[inline] 632 | pub fn assert_same_env(txn: &ConstTransaction, db: &Database) 633 | -> Result<()> { 634 | db.assert_same_env(&txn.env) 635 | } 636 | #[inline] 637 | pub fn assert_in_env(txn: &ConstTransaction, env: &Environment) 638 | -> Result<()> { 639 | if env as *const Environment != &*txn.env as *const Environment { 640 | Err(Error::Mismatch) 641 | } else { 642 | Ok(()) 643 | } 644 | } 645 | #[inline] 646 | pub fn txptr(txn: &ConstTransaction) -> *mut ffi::MDB_txn { 647 | txn.tx.0 648 | } 649 | 650 | impl<'env> Deref for ReadTransaction<'env> { 651 | type Target = ConstTransaction<'env>; 652 | 653 | fn deref(&self) -> &ConstTransaction<'env> { 654 | &self.0 655 | } 656 | } 657 | 658 | impl<'env> DerefMut for ReadTransaction<'env> { 659 | fn deref_mut(&mut self) -> &mut ConstTransaction<'env> { 660 | &mut self.0 661 | } 662 | } 663 | 664 | impl<'env> ReadTransaction<'env> { 665 | /// Opens a new, read-only transaction within the given environment. 666 | /// 667 | /// ## Note 668 | /// 669 | /// A transaction and its cursors must only be used by a single thread 670 | /// (enforced by the rust compiler), and a thread may only have a single 671 | /// transaction at a time. If `NOTLS` is in use, this does not apply to 672 | /// read-only transactions. Attempting to open a read-only transaction 673 | /// while the current thread holds a read-write transaction will deadlock. 674 | pub fn new(env: E) -> Result 675 | where E : Into> { 676 | Ok(ReadTransaction(try!(ConstTransaction::new( 677 | env, None, ffi::MDB_RDONLY)))) 678 | } 679 | 680 | /// Dissociates the given cursor from this transaction and its database, 681 | /// returning a `StaleCursor` which can be reused later. 682 | /// 683 | /// This only fails if `cursor` does not belong to this transaction. 684 | /// 685 | /// ## Example 686 | /// 687 | /// ``` 688 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 689 | /// # fn main() { 690 | /// # let env = create_env(); 691 | /// # let db = lmdb::Database::open( 692 | /// # &env, None, &lmdb::DatabaseOptions::defaults()) 693 | /// # .unwrap(); 694 | /// let mut saved_cursor; 695 | /// { 696 | /// let txn = lmdb::ReadTransaction::new(&env).unwrap(); 697 | /// let cursor = txn.cursor(&db).unwrap(); 698 | /// // Do some stuff with `txn` and `cursor` 699 | /// 700 | /// // We don't want to realloc `cursor` next time, so save it away 701 | /// saved_cursor = txn.dissoc_cursor(cursor).unwrap(); 702 | /// } // Read transaction goes away, but our saved cursor remains 703 | /// 704 | /// { 705 | /// let txn = lmdb::ReadTransaction::new(&env).unwrap(); 706 | /// // Rebind the old cursor. It continues operating on `db`. 707 | /// let cursor = txn.assoc_cursor(saved_cursor).unwrap(); 708 | /// // Do stuff with txn, cursor 709 | /// 710 | /// // We can save the cursor away again 711 | /// saved_cursor = txn.dissoc_cursor(cursor).unwrap(); 712 | /// } 713 | /// # } 714 | /// ``` 715 | /// 716 | /// ## Example — Shared ownership mode 717 | /// 718 | /// Cursors can also be dissociated and reassociated with transactions with 719 | /// shared ownership mode. This can also include changing the ownership 720 | /// mode. To be able to use shared ownership mode, make sure that the 721 | /// `AssocCursor` trait is imported or else you will simply borrow the 722 | /// inner transaction instead of taking a copy of the `Rc`, etc. 723 | /// 724 | /// ``` 725 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 726 | /// use std::sync::Arc; 727 | /// 728 | /// use lmdb::traits::{AssocCursor, CreateCursor}; 729 | /// 730 | /// # fn main() { 731 | /// // N.B. Unnecessary type and lifetime annotations included for clarity 732 | /// let env: Arc = Arc::new(create_env()); 733 | /// let db: Arc> = Arc::new(lmdb::Database::open( 734 | /// env.clone(), None, &lmdb::DatabaseOptions::defaults()).unwrap()); 735 | /// 736 | /// let mut saved_cursor: lmdb::StaleCursor<'static>; 737 | /// { 738 | /// // `Arc` is unnecessary in this trivial example, but let's pretend 739 | /// // there was good use for this. 740 | /// let txn: Arc = Arc::new( 741 | /// lmdb::ReadTransaction::new(env.clone()).unwrap()); 742 | /// let cursor: lmdb::Cursor<'static, 'static> = 743 | /// txn.cursor(db.clone()).unwrap(); 744 | /// 745 | /// // Do some stuff with `txn` and `cursor` 746 | /// 747 | /// // We don't want to realloc `cursor` next time, so save it away 748 | /// saved_cursor = txn.dissoc_cursor(cursor).unwrap(); 749 | /// } 750 | /// 751 | /// { 752 | /// let txn: Arc> = 753 | /// Arc::new(lmdb::ReadTransaction::new(env.clone()).unwrap()); 754 | /// // Rebind the old cursor. It continues operating on `db`. 755 | /// let cursor: lmdb::Cursor<'static, 'static> = 756 | /// txn.assoc_cursor(saved_cursor).unwrap(); 757 | /// // Do stuff with txn, cursor 758 | /// 759 | /// // We can save the cursor away again 760 | /// saved_cursor = txn.dissoc_cursor(cursor).unwrap(); 761 | /// } 762 | /// # } 763 | /// ``` 764 | pub fn dissoc_cursor<'txn,'db>(&self, cursor: Cursor<'txn,'db>) 765 | -> Result> 766 | where 'env: 'db { 767 | try!(self.assert_sensible_cursor(&cursor)); 768 | let env = Supercow::clone_non_owned(&self.env) 769 | .expect("Cannot use owned `Environment` with `dissoc_cursor`"); 770 | Ok(cursor::to_stale(cursor, env)) 771 | } 772 | 773 | /// Associates a saved read-only with this transaction. 774 | /// 775 | /// The cursor will be rebound to this transaction, but will continue using 776 | /// the same database that it was previously. 777 | /// 778 | /// This method is functionally equivalent to the method on `AssocCursor` 779 | /// and exists for convenience and backwards-compatibility. 780 | /// 781 | /// If you have an, e.g., `Rc` and want to get a 782 | /// `Cursor<'static,'db>`, make sure you have the `AssocCursor` trait 783 | /// imported so that the needed alternate implementations of this method 784 | /// are available. 785 | pub fn assoc_cursor<'txn,'db>(&'txn self, cursor: StaleCursor<'db>) 786 | -> Result> { 787 | let self_as_const: &'txn ConstTransaction = &*self; 788 | Cursor::from_stale(cursor, 789 | NonSyncSupercow::borrowed(&*self_as_const)) 790 | } 791 | 792 | /// Resets this transaction, releasing most of its resources but allowing 793 | /// it to be quickly renewed if desired. 794 | /// 795 | /// ## Example 796 | /// 797 | /// ``` 798 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 799 | /// # fn main() { 800 | /// # let env = create_env(); 801 | /// let mut saved_txn; 802 | /// { 803 | /// let txn = lmdb::ReadTransaction::new(&env).unwrap(); 804 | /// { 805 | /// let access = txn.access(); 806 | /// // Do stuff with `txn`, `access` 807 | /// } 808 | /// // Save our transaction so we don't have to reallocate it next time, 809 | /// // but we also don't keep locks around and will later move to the 810 | /// // latest version of the environment. 811 | /// saved_txn = txn.reset(); 812 | /// } 813 | /// 814 | /// { 815 | /// // Instead of creating a brand new transaction, renew the one we 816 | /// // saved. 817 | /// let txn = saved_txn.renew().unwrap(); 818 | /// { 819 | /// let access = txn.access(); 820 | /// // Do stuff with `txn`, `access` 821 | /// } 822 | /// 823 | /// // We can save the transaction away again 824 | /// saved_txn = txn.reset(); 825 | /// } 826 | /// # } 827 | /// ``` 828 | pub fn reset(self) -> ResetTransaction<'env> { 829 | unsafe { ffi::mdb_txn_reset(self.0.tx.0); } 830 | ResetTransaction(self) 831 | } 832 | } 833 | 834 | impl<'env> ResetTransaction<'env> { 835 | /// Renews this read-only transaction, making it available for more 836 | /// reading. 837 | pub fn renew(self) -> Result> { 838 | unsafe { lmdb_call!(ffi::mdb_txn_renew((self.0).0.tx.0)); } 839 | Ok(self.0) 840 | } 841 | } 842 | 843 | impl<'env> Deref for WriteTransaction<'env> { 844 | type Target = ConstTransaction<'env>; 845 | 846 | 847 | fn deref(&self) -> &ConstTransaction<'env> { 848 | &self.0 849 | } 850 | } 851 | 852 | impl<'env> DerefMut for WriteTransaction<'env> { 853 | fn deref_mut(&mut self) -> &mut ConstTransaction<'env> { 854 | &mut self.0 855 | } 856 | } 857 | 858 | impl<'env> WriteTransaction<'env> { 859 | /// Creates a new, read-write transaction in the given environment. 860 | /// 861 | /// ## Note 862 | /// 863 | /// A transaction and its cursors must only be used by a single thread 864 | /// (enforced by the rust compiler), and a thread may only have a single 865 | /// read-write transaction at a time (even if `NOTLS` is in use --- trying 866 | /// to start two top-level read-write transactions on the same thread will 867 | /// deadlock). 868 | pub fn new(env: E) -> Result 869 | where E : Into> { 870 | Ok(WriteTransaction(try!(ConstTransaction::new(env, None, 0)))) 871 | } 872 | 873 | /// Opens a new, read-write transaction as a child transaction of the given 874 | /// parent. While the new transaction exists, no operations may be 875 | /// performed on the parent or any of its cursors. (These bindings are 876 | /// actually stricter, and do not permit cursors or other references into 877 | /// the parent to coexist with the child transaction.) 878 | /// 879 | /// After this call, whether or not it succeeds, it is possible to call 880 | /// `access()` on the original transaction again one more time, since the 881 | /// Rust borrow rules guarantee the old accessor was destroyed by the 882 | /// caller already. 883 | /// 884 | /// ## Note 885 | /// 886 | /// A transaction and its cursors must only be used by a single thread 887 | /// (enforced by the rust compiler). 888 | /// 889 | /// ## Example 890 | /// 891 | /// ``` 892 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 893 | /// # fn main() { 894 | /// # let env = create_env(); 895 | /// let db = lmdb::Database::open( 896 | /// &env, None, &lmdb::DatabaseOptions::defaults()).unwrap(); 897 | /// let mut txn = lmdb::WriteTransaction::new(&env).unwrap(); 898 | /// let f = lmdb::put::Flags::empty(); 899 | /// { 900 | /// let mut access = txn.access(); 901 | /// access.put(&db, "Germany", "Berlin", f).unwrap(); 902 | /// access.put(&db, "Latvia", "Rīga", f).unwrap(); 903 | /// access.put(&db, "France", "Paris", f).unwrap(); 904 | /// } 905 | /// 906 | /// { 907 | /// // Open a child transaction and do some more reading and writing. 908 | /// let subtx = txn.child_tx().unwrap(); 909 | /// let mut access = subtx.access(); 910 | /// assert_eq!("Berlin", access.get::(&db, "Germany").unwrap()); 911 | /// access.put(&db, "Germany", "Frankfurt", f).unwrap(); 912 | /// assert_eq!("Frankfurt", access.get::(&db, "Germany").unwrap()); 913 | /// // Don't commit --- let the child transaction abort (roll back) 914 | /// } 915 | /// 916 | /// { 917 | /// let mut access = txn.access(); 918 | /// // Now we can do some more reading and writing on the original 919 | /// // transaction. 920 | /// // The effect of the aborted child transaction are not visible. 921 | /// access.put(&db, "United Kingdom", "London", f).unwrap(); 922 | /// assert_eq!("Berlin", access.get::(&db, "Germany").unwrap()); 923 | /// } 924 | /// 925 | /// { 926 | /// // Another child. 927 | /// let subtx = txn.child_tx().unwrap(); 928 | /// { 929 | /// let mut access = subtx.access(); 930 | /// access.put(&db, "Spain", "Madrid", f).unwrap(); 931 | /// } 932 | /// // Commit this one this time. 933 | /// subtx.commit().unwrap(); 934 | /// } 935 | /// 936 | /// { 937 | /// // Now the changes from the child are visible to this transaction, 938 | /// // but still not outside it. 939 | /// let mut access = txn.access(); 940 | /// assert_eq!("Madrid", access.get::(&db, "Spain").unwrap()); 941 | /// } 942 | /// 943 | /// txn.commit().unwrap(); 944 | /// # } 945 | /// ``` 946 | pub fn child_tx<'a>(&'a mut self) -> Result> 947 | where 'env: 'a { 948 | let env = Supercow::share(&mut self.0.env); 949 | Ok(WriteTransaction(try!(ConstTransaction::new( 950 | env, Some(&mut*self), 0)))) 951 | } 952 | 953 | /// Commits this write transaction. 954 | pub fn commit(mut self) -> Result<()> { 955 | unsafe { 956 | self.0.tx.commit() 957 | } 958 | } 959 | 960 | /// Returns a read/write accessor on this transaction. 961 | /// 962 | /// ## Panics 963 | /// 964 | /// Panics if an accessor has already been obtained from this transaction 965 | /// and not yet dropped. 966 | #[inline] 967 | pub fn access(&self) -> WriteAccessor { 968 | WriteAccessor(self.0.access()) 969 | } 970 | } 971 | 972 | impl<'txn> ConstAccessor<'txn> { 973 | /// Get items from a database. 974 | /// 975 | /// This function retrieves key/data pairs from the database. A reference 976 | /// to the data associated with the given key is returned. If the database 977 | /// supports duplicate keys (`DUPSORT`) then the first data item for the 978 | /// key will be returned. Retrieval of other items requires the use of 979 | /// cursoring. 980 | /// 981 | /// The returned memory is valid until the next mutation through the 982 | /// transaction or the end of the transaction (both are enforced through 983 | /// the borrow checker). 984 | /// 985 | /// ## Errors 986 | /// 987 | /// This call may return errors for reasons other than the key not being 988 | /// found. The easiest way to handle "not found" is generally to use the 989 | /// `to_opt` method on `traits::LmdbResultExt` to promote the value into a 990 | /// `Result>`. Most important of these other errors is the 991 | /// possibility of the key being found, but the value not being convertible 992 | /// to a `&V`. 993 | #[inline] 994 | pub fn get( 995 | &self, db: &Database, key: &K) -> Result<&V> 996 | { 997 | try!(db.assert_same_env(self.env())); 998 | 999 | let mut mv_key = as_val(key); 1000 | let mut out_val = EMPTY_VAL; 1001 | unsafe { 1002 | lmdb_call!(ffi::mdb_get( 1003 | self.txptr(), db.as_raw(), &mut mv_key, &mut out_val)); 1004 | } 1005 | 1006 | from_val(self, &out_val) 1007 | } 1008 | 1009 | fn txptr(&self) -> *mut ffi::MDB_txn { 1010 | self.0.tx.0 1011 | } 1012 | 1013 | fn env(&self) -> &Environment { 1014 | &*self.0.env 1015 | } 1016 | } 1017 | 1018 | impl<'txn> Deref for WriteAccessor<'txn> { 1019 | type Target = ConstAccessor<'txn>; 1020 | 1021 | fn deref(&self) -> &ConstAccessor<'txn> { 1022 | &self.0 1023 | } 1024 | } 1025 | 1026 | impl<'txn> WriteAccessor<'txn> { 1027 | /// Store items into a database. 1028 | /// 1029 | /// This function stores key/data pairs in the database. The default 1030 | /// behavior is to enter the new key/data pair, replacing any previously 1031 | /// existing key if duplicates are disallowed, or adding a duplicate data 1032 | /// item if duplicates are allowed (`DUPSORT`). 1033 | #[inline] 1034 | pub fn put( 1035 | &mut self, db: &Database, key: &K, value: &V, 1036 | flags: put::Flags) -> Result<()> 1037 | { 1038 | try!(db.assert_same_env(self.env())); 1039 | 1040 | let mut mv_key = as_val(key); 1041 | let mut mv_val = as_val(value); 1042 | unsafe { 1043 | lmdb_call!(ffi::mdb_put( 1044 | self.txptr(), db.as_raw(), &mut mv_key, &mut mv_val, 1045 | flags.bits())); 1046 | } 1047 | Ok(()) 1048 | } 1049 | 1050 | /// Store items into a database. 1051 | /// 1052 | /// This function stores key/data pairs in the database. The default 1053 | /// behavior is to enter the new key/data pair, replacing any previously 1054 | /// existing key if duplicates are disallowed, or adding a duplicate data 1055 | /// item if duplicates are allowed (`DUPSORT`). 1056 | /// 1057 | /// Unlike `put()`, this does not take a value. Instead, it reserves space 1058 | /// for the value (equal to the size of `V`) and then returns a mutable 1059 | /// reference to it. Be aware that the `FromReservedLmdbBytes` conversion 1060 | /// will be invoked on whatever memory happens to be at the destination 1061 | /// location. 1062 | /// 1063 | /// ## Example 1064 | /// 1065 | /// ``` 1066 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 1067 | /// #[repr(C, packed)] 1068 | /// #[derive(Clone,Copy,Debug,PartialEq,Eq)] 1069 | /// struct MyStruct { 1070 | /// x: i32, 1071 | /// y: i32, 1072 | /// } 1073 | /// unsafe impl lmdb::traits::LmdbRaw for MyStruct { } 1074 | /// 1075 | /// # fn main() { 1076 | /// # let env = create_env(); 1077 | /// # let db = lmdb::Database::open( 1078 | /// # &env, None, &lmdb::DatabaseOptions::defaults()) 1079 | /// # .unwrap(); 1080 | /// let txn = lmdb::WriteTransaction::new(&env).unwrap(); 1081 | /// { 1082 | /// let mut access = txn.access(); 1083 | /// { 1084 | /// let dst: &mut MyStruct = access.put_reserve( 1085 | /// &db, "foo", lmdb::put::Flags::empty()).unwrap(); 1086 | /// // Writing to `dst` actually writes directly into the database. 1087 | /// dst.x = 42; 1088 | /// dst.y = 56; 1089 | /// // Drop `dst` so we can use `access` again 1090 | /// } 1091 | /// assert_eq!(&MyStruct { x: 42, y: 56 }, 1092 | /// access.get(&db, "foo").unwrap()); 1093 | /// } 1094 | /// txn.commit().unwrap(); 1095 | /// # } 1096 | /// ``` 1097 | #[inline] 1098 | pub fn put_reserve( 1100 | &mut self, db: &Database, key: &K, flags: put::Flags) -> Result<&mut V> 1101 | { 1102 | unsafe { 1103 | self.put_reserve_unsized(db, key, mem::size_of::(), flags) 1104 | } 1105 | } 1106 | 1107 | /// Store items into a database. 1108 | /// 1109 | /// This function stores key/data pairs in the database. The default 1110 | /// behavior is to enter the new key/data pair, replacing any previously 1111 | /// existing key if duplicates are disallowed, or adding a duplicate data 1112 | /// item if duplicates are allowed (`DUPSORT`). 1113 | /// 1114 | /// Unlike `put()`, this does not take a value. Instead, it reserves space 1115 | /// for the value (equal to an array of `count` objects of size `V`) and 1116 | /// then returns a mutable reference to it. Be aware that the content of 1117 | /// the returned slice is simply whatever happens to be in the destination 1118 | /// memory at the time of this call. 1119 | /// 1120 | /// ## Example 1121 | /// 1122 | /// ``` 1123 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 1124 | /// # fn main() { 1125 | /// # let env = create_env(); 1126 | /// # let db = lmdb::Database::open( 1127 | /// # &env, None, &lmdb::DatabaseOptions::defaults()) 1128 | /// # .unwrap(); 1129 | /// let txn = lmdb::WriteTransaction::new(&env).unwrap(); 1130 | /// { 1131 | /// let mut access = txn.access(); 1132 | /// { 1133 | /// let bytes: &mut [u8] = access.put_reserve_array( 1134 | /// &db, "foo", 4, lmdb::put::Flags::empty()).unwrap(); 1135 | /// // More realistically, one could zero-copy data from a file/socket 1136 | /// // into `bytes`, for example. 1137 | /// bytes[0] = b'b'; bytes[1] = b'y'; 1138 | /// bytes[2] = b't'; bytes[3] = b'e'; 1139 | /// } 1140 | /// assert_eq!("byte", access.get::(&db, "foo").unwrap()); 1141 | /// } 1142 | /// txn.commit().unwrap(); 1143 | /// # } 1144 | /// ``` 1145 | #[inline] 1146 | pub fn put_reserve_array( 1147 | &mut self, db: &Database, key: &K, count: usize, flags: put::Flags) 1148 | -> Result<&mut [V]> 1149 | { 1150 | unsafe { 1151 | self.put_reserve_unsized( 1152 | db, key, mem::size_of::() * count, flags) 1153 | } 1154 | } 1155 | 1156 | /// Store items into a database. 1157 | /// 1158 | /// This function stores key/data pairs in the database. The default 1159 | /// behavior is to enter the new key/data pair, replacing any previously 1160 | /// existing key if duplicates are disallowed, or adding a duplicate data 1161 | /// item if duplicates are allowed (`DUPSORT`). 1162 | /// 1163 | /// Unlike `put()`, this does not take a value. Instead, it reserves space 1164 | /// equal to `size` bytes for the value and then returns a mutable 1165 | /// reference to it. Be aware that the `FromReservedLmdbBytes` conversion 1166 | /// will be invoked on whatever memory happens to be at the destination 1167 | /// location. 1168 | /// 1169 | /// ## Unsafety 1170 | /// 1171 | /// The caller must ensure that `size` is a valid size for `V`. 1172 | #[inline] 1173 | pub unsafe fn put_reserve_unsized( 1175 | &mut self, db: &Database, key: &K, size: usize, flags: put::Flags) 1176 | -> Result<&mut V> 1177 | { 1178 | try!(db.assert_same_env(self.env())); 1179 | 1180 | let mut mv_key = as_val(key); 1181 | let mut out_val = EMPTY_VAL; 1182 | out_val.mv_size = size; 1183 | lmdb_call!(ffi::mdb_put( 1184 | self.txptr(), db.as_raw(), &mut mv_key, &mut out_val, 1185 | flags.bits() | ffi::MDB_RESERVE)); 1186 | 1187 | Ok(from_reserved(self, &out_val)) 1188 | } 1189 | 1190 | /// Delete items from a database by key. 1191 | /// 1192 | /// This function removes key/data pairs from the database. All values 1193 | /// whose key matches `key` are deleted, including in the case of 1194 | /// `DUPSORT`. This function will return `NOTFOUND` if the specified 1195 | /// key is not in the database. 1196 | /// 1197 | /// ## Example 1198 | /// 1199 | /// ``` 1200 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 1201 | /// # fn main() { 1202 | /// # let env = create_env(); 1203 | /// let db = lmdb::Database::open( 1204 | /// &env, Some("example"), 1205 | /// &lmdb::DatabaseOptions::create_multimap_unsized::()) 1206 | /// .unwrap(); 1207 | /// let txn = lmdb::WriteTransaction::new(&env).unwrap(); 1208 | /// { 1209 | /// let mut access = txn.access(); 1210 | /// access.put(&db, "Fruit", "Apple", lmdb::put::Flags::empty()).unwrap(); 1211 | /// access.put(&db, "Fruit", "Orange", lmdb::put::Flags::empty()).unwrap(); 1212 | /// assert_eq!("Apple", access.get::(&db, "Fruit").unwrap()); 1213 | /// access.del_key(&db, "Fruit").unwrap(); 1214 | /// assert!(access.get::(&db, "Fruit").is_err()); 1215 | /// } 1216 | /// txn.commit().unwrap(); 1217 | /// # } 1218 | /// ``` 1219 | #[inline] 1220 | pub fn del_key( 1221 | &mut self, db: &Database, key: &K) -> Result<()> 1222 | { 1223 | try!(db.assert_same_env(self.env())); 1224 | 1225 | let mut mv_key = as_val(key); 1226 | unsafe { 1227 | lmdb_call!(ffi::mdb_del( 1228 | self.txptr(), db.as_raw(), &mut mv_key, ptr::null_mut())); 1229 | } 1230 | 1231 | Ok(()) 1232 | } 1233 | 1234 | /// Delete items from a database by key and value. 1235 | /// 1236 | /// This function removes key/data pairs from the database. If the database 1237 | /// does not support sorted duplicate data items (`DUPSORT`) the `val` 1238 | /// parameter is ignored and this call behaves like `del()`. Otherwise, if 1239 | /// the data item matching both `key` and `val` will be deleted. This 1240 | /// function will return `NOTFOUND` if the specified key/data pair is not 1241 | /// in the database. 1242 | /// 1243 | /// ## Example 1244 | /// 1245 | /// ``` 1246 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 1247 | /// # fn main() { 1248 | /// # let env = create_env(); 1249 | /// let db = lmdb::Database::open( 1250 | /// &env, Some("example"), 1251 | /// &lmdb::DatabaseOptions::create_multimap_unsized::()) 1252 | /// .unwrap(); 1253 | /// let txn = lmdb::WriteTransaction::new(&env).unwrap(); 1254 | /// { 1255 | /// let mut access = txn.access(); 1256 | /// access.put(&db, "Fruit", "Apple", lmdb::put::Flags::empty()).unwrap(); 1257 | /// access.put(&db, "Fruit", "Orange", lmdb::put::Flags::empty()).unwrap(); 1258 | /// assert_eq!("Apple", access.get::(&db, "Fruit").unwrap()); 1259 | /// access.del_item(&db, "Fruit", "Apple").unwrap(); 1260 | /// assert_eq!("Orange", access.get::(&db, "Fruit").unwrap()); 1261 | /// } 1262 | /// txn.commit().unwrap(); 1263 | /// # } 1264 | /// ``` 1265 | #[inline] 1266 | pub fn del_item( 1267 | &mut self, db: &Database, key: &K, val: &V) -> Result<()> 1268 | { 1269 | try!(db.assert_same_env(self.env())); 1270 | 1271 | let mut mv_key = as_val(key); 1272 | let mut mv_val = as_val(val); 1273 | unsafe { 1274 | lmdb_call!(ffi::mdb_del( 1275 | self.txptr(), db.as_raw(), &mut mv_key, &mut mv_val)); 1276 | } 1277 | 1278 | Ok(()) 1279 | } 1280 | 1281 | /// Completely clears the content of the given database. 1282 | /// 1283 | /// ## Example 1284 | /// 1285 | /// ``` 1286 | /// # include!(concat!(env!("CARGO_MANIFEST_DIR"),"/src/example_helpers.rs")); 1287 | /// # fn main() { 1288 | /// # let env = create_env(); 1289 | /// # let db = lmdb::Database::open( 1290 | /// # &env, None, &lmdb::DatabaseOptions::defaults()) 1291 | /// # .unwrap(); 1292 | /// let txn = lmdb::WriteTransaction::new(&env).unwrap(); 1293 | /// { 1294 | /// let mut access = txn.access(); 1295 | /// let f = lmdb::put::Flags::empty(); 1296 | /// access.put(&db, "Germany", "Berlin", f).unwrap(); 1297 | /// access.put(&db, "France", "Paris", f).unwrap(); 1298 | /// access.put(&db, "Latvia", "Rīga", f).unwrap(); 1299 | /// assert_eq!(3, txn.db_stat(&db).unwrap().entries); 1300 | /// 1301 | /// access.clear_db(&db).unwrap(); 1302 | /// assert_eq!(0, txn.db_stat(&db).unwrap().entries); 1303 | /// } 1304 | /// txn.commit().unwrap(); 1305 | /// # } 1306 | /// ``` 1307 | pub fn clear_db(&mut self, db: &Database) -> Result<()> { 1308 | try!(db.assert_same_env(self.env())); 1309 | unsafe { 1310 | lmdb_call!(ffi::mdb_drop(self.txptr(), db.as_raw(), 0)); 1311 | } 1312 | Ok(()) 1313 | } 1314 | } 1315 | -------------------------------------------------------------------------------- /src/unaligned.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 FullContact, Inc 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use std::cmp; 10 | use std::fmt; 11 | use std::hash::{Hash, Hasher}; 12 | use std::mem; 13 | use std::ops; 14 | 15 | use traits::*; 16 | 17 | /// Wrapper for arbitrary `Copy` types which lifts their alignment 18 | /// restrictions. 19 | /// 20 | /// This allows using values which have non-byte alignment but are otherwise 21 | /// LMDB-safe (as defined by `LmdbRaw`) to be used with it. It obviously does 22 | /// not make `T` itself packed, so the same discussion with respect to padding 23 | /// in the `LmdbRaw` documentation applies here as well. 24 | /// 25 | /// There is no way to get a reference to the contained value, as Rust 26 | /// currently has no way to express that the reference may be misaligned. (See 27 | /// also [https://github.com/rust-lang/rust/issues/27060](https://github.com/rust-lang/rust/issues/27060).) 28 | /// 29 | /// ### Example 30 | /// 31 | /// ``` 32 | /// use lmdb_zero as lmdb; 33 | /// use lmdb_zero::Unaligned as U; 34 | /// 35 | /// fn get_a_u64(env: &lmdb::Environment, db: &lmdb::Database, 36 | /// key: &str) -> u64 { 37 | /// let tx = lmdb::ReadTransaction::new(env).unwrap(); 38 | /// let access = tx.access(); 39 | /// access.get::>(db, key).unwrap().get() 40 | /// } 41 | /// ``` 42 | #[repr(packed)] 43 | pub struct Unaligned(T); 44 | 45 | impl Clone for Unaligned { 46 | fn clone(&self) -> Self { 47 | Unaligned(self.0) 48 | } 49 | } 50 | 51 | impl Copy for Unaligned { } 52 | 53 | unsafe impl LmdbRaw for Unaligned { 54 | fn reported_type() -> String { 55 | format!("Unaligned<{}>", T::reported_type()) 56 | } 57 | } 58 | 59 | unsafe impl LmdbOrdKey 60 | for Unaligned { 61 | fn ordered_by_bytes() -> bool { T::ordered_by_bytes() } 62 | fn ordered_as_integer() -> bool { T::ordered_as_integer() } 63 | } 64 | 65 | impl Unaligned { 66 | /// Wraps `t` in an `Unaligned` marker. 67 | pub fn new(t: T) -> Self { 68 | Unaligned(t) 69 | } 70 | 71 | /// Returns `t` as if it were wrapped by `Unaligned`. 72 | /// 73 | /// This is safe because any `&T` _is_ a valid `&Unaligned`. 74 | pub fn of_ref(t: &T) -> &Self { 75 | unsafe { mem::transmute(t) } 76 | } 77 | 78 | /// Returns `t` as if it were wrapped by `Unaligned`. 79 | /// 80 | /// This is safe because any `&T` _is_ a valid `&Unaligned`. 81 | pub fn of_mut(t: &mut T) -> &mut Self { 82 | unsafe { mem::transmute(t) } 83 | } 84 | 85 | /// Extracts the contained value. 86 | /// 87 | /// This is safe as the compiler has visibility into the fact that the 88 | /// contained value is misaligned and can copy appropriately. 89 | pub fn get(&self) -> T { self.0 } 90 | 91 | /// Replaces the contained value. 92 | pub fn set(&mut self, t: T) { self.0 = t; } 93 | } 94 | 95 | /// Synonym for `Unaligned::of_ref()`. 96 | pub fn unaligned(t: &T) -> &Unaligned { 97 | Unaligned::of_ref(t) 98 | } 99 | 100 | // Since a future rust version may bar taking a reference to a member of a 101 | // packed structure (and it's not entirely safe right now), manually implement 102 | // everything to copy to a local variable and then delegate. 103 | 104 | macro_rules! deleg_fmt { 105 | ($tr:ident) => { 106 | impl fmt::$tr for Unaligned where T : fmt::$tr { 107 | fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { 108 | let inner = self.0; 109 | inner.fmt(fmt) 110 | } 111 | } 112 | } 113 | } 114 | 115 | deleg_fmt!(Binary); 116 | deleg_fmt!(Debug); 117 | deleg_fmt!(Display); 118 | deleg_fmt!(LowerExp); 119 | deleg_fmt!(LowerHex); 120 | deleg_fmt!(Octal); 121 | deleg_fmt!(Pointer); 122 | deleg_fmt!(UpperExp); 123 | deleg_fmt!(UpperHex); 124 | 125 | impl> 126 | cmp::PartialEq> for Unaligned { 127 | fn eq(&self, other: &Self) -> bool { 128 | let (lhs, rhs) = (self.0, other.0); 129 | lhs.eq(&rhs) 130 | } 131 | } 132 | impl cmp::Eq for Unaligned { } 133 | impl> 134 | cmp::PartialOrd> for Unaligned { 135 | fn partial_cmp(&self, other: &Self) -> Option { 136 | let (lhs, rhs) = (self.0, other.0); 137 | lhs.partial_cmp(&rhs) 138 | } 139 | fn lt(&self, other: &Self) -> bool { 140 | let (lhs, rhs) = (self.0, other.0); 141 | lhs.lt(&rhs) 142 | } 143 | fn le(&self, other: &Self) -> bool { 144 | let (lhs, rhs) = (self.0, other.0); 145 | lhs.le(&rhs) 146 | } 147 | fn gt(&self, other: &Self) -> bool { 148 | let (lhs, rhs) = (self.0, other.0); 149 | lhs.gt(&rhs) 150 | } 151 | fn ge(&self, other: &Self) -> bool { 152 | let (lhs, rhs) = (self.0, other.0); 153 | lhs.ge(&rhs) 154 | } 155 | } 156 | impl cmp::Ord for Unaligned { 157 | fn cmp(&self, other: &Self) -> cmp::Ordering { 158 | let (lhs, rhs) = (self.0, other.0); 159 | lhs.cmp(&rhs) 160 | } 161 | } 162 | 163 | impl Hash for Unaligned { 164 | fn hash(&self, state: &mut H) { 165 | let v = self.0; 166 | v.hash(state) 167 | } 168 | } 169 | 170 | macro_rules! binop { 171 | ($tr:ident, $meth:ident) => { 172 | impl> 173 | ops::$tr> for Unaligned 174 | where T::Output : LmdbRawIfUnaligned { 175 | type Output = Unaligned; 176 | fn $meth(self, rhs: Self) -> Self::Output { 177 | let (lhs, rhs) = (self.0, rhs.0); 178 | Unaligned(lhs.$meth(rhs)) 179 | } 180 | } 181 | } 182 | } 183 | 184 | macro_rules! binopeq { 185 | ($tr:ident, $meth:ident) => { 186 | impl> 187 | ops::$tr> for Unaligned { 188 | fn $meth(&mut self, rhs: Self) { 189 | let (mut lhs, rhs) = (self.0, rhs.0); 190 | lhs.$meth(rhs); 191 | self.0 = lhs; 192 | } 193 | } 194 | } 195 | } 196 | 197 | binop!(Add, add); 198 | binop!(BitAnd, bitand); 199 | binop!(BitOr, bitor); 200 | binop!(BitXor, bitxor); 201 | binop!(Div, div); 202 | binop!(Mul, mul); 203 | binop!(Rem, rem); 204 | binop!(Shl, shl); 205 | binop!(Shr, shr); 206 | binop!(Sub, sub); 207 | 208 | binopeq!(AddAssign, add_assign); 209 | binopeq!(BitAndAssign, bitand_assign); 210 | binopeq!(BitOrAssign, bitor_assign); 211 | binopeq!(BitXorAssign, bitxor_assign); 212 | binopeq!(DivAssign, div_assign); 213 | binopeq!(MulAssign, mul_assign); 214 | binopeq!(RemAssign, rem_assign); 215 | binopeq!(ShlAssign, shl_assign); 216 | binopeq!(ShrAssign, shr_assign); 217 | binopeq!(SubAssign, sub_assign); 218 | --------------------------------------------------------------------------------