зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1681957 - Migrate gecko-fuzz-targets away from rkv 0.10. r=vporof
Differential Revision: https://phabricator.services.mozilla.com/D99453
This commit is contained in:
Родитель
556f400fb6
Коммит
5b6e237eec
|
@ -525,7 +525,7 @@ dependencies = [
|
|||
"nserror",
|
||||
"nsstring",
|
||||
"rental",
|
||||
"rkv 0.16.1",
|
||||
"rkv",
|
||||
"rust_cascade",
|
||||
"sha2",
|
||||
"storage_variant",
|
||||
|
@ -1712,7 +1712,7 @@ dependencies = [
|
|||
"lazy_static",
|
||||
"libc",
|
||||
"lmdb-rkv",
|
||||
"rkv 0.10.4",
|
||||
"rkv",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
|
@ -2088,7 +2088,7 @@ dependencies = [
|
|||
"flate2",
|
||||
"log",
|
||||
"once_cell",
|
||||
"rkv 0.16.1",
|
||||
"rkv",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"uuid",
|
||||
|
@ -2655,7 +2655,7 @@ dependencies = [
|
|||
"moz_task",
|
||||
"nserror",
|
||||
"nsstring",
|
||||
"rkv 0.16.1",
|
||||
"rkv",
|
||||
"storage_variant",
|
||||
"tempfile",
|
||||
"thin-vec",
|
||||
|
@ -4246,26 +4246,6 @@ version = "0.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c2b29d87cfbdce39849012bb5020fff88b8f01f4f5b55846a0b6ef360774eae"
|
||||
|
||||
[[package]]
|
||||
name = "rkv"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30a3dbc1f4971372545ed4175f23ef206c81e5874cd574d153646e7ee78f6793"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"bincode",
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"failure",
|
||||
"lazy_static",
|
||||
"lmdb-rkv",
|
||||
"ordered-float",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"url",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rkv"
|
||||
version = "0.16.1"
|
||||
|
@ -6066,7 +6046,7 @@ dependencies = [
|
|||
"nserror",
|
||||
"nsstring",
|
||||
"once_cell",
|
||||
"rkv 0.16.1",
|
||||
"rkv",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
"xpcom",
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{"files":{"CODE_OF_CONDUCT.md":"902d5357af363426631d907e641e220b3ec89039164743f8442b3f120479b7cf","Cargo.lock":"a53a3ee5e3aa691db3a5580e6d623cd0762dba8d081eb6de893211a7791669f9","Cargo.toml":"0bb154d5139e53f095919e033abd41a72ea22db685a7a052c96087a09819d479","LICENSE":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","README.md":"c75566740e7f581fe4cbd44ad82cabad5f2665bd72a4519c17a7f98fc69a5703","examples/README.md":"143767fc145bf167ce269a65138cb3f7086cb715b8bc4f73626da82966e646f4","examples/iterator.rs":"ddc3997e394a30ad82d78d2675a48c4617353f88b89bb9a3df5a3804d59b8ef9","examples/simple-store.rs":"cae63e39f2f98ee6ac2f387dcb02d6b929828a74f32f7d18d69c7fc9c3cce765","run-all-examples.sh":"7f9d11d01017f77e1c9d26e3e82dfca8c6930deaec85e864458e33a7fa267de0","src/bin/dump.rs":"da8543848e57893902751f4c4745e835b9c86263da2344af18d5717014f645f5","src/bin/rand.rs":"3da924fa0f1a118f606e2b94aee3a0553d9ebdbd17ee0152b85148adbf521bba","src/env.rs":"5deac6b35e49da1d47d7c852ed2e30ef96b6d15998fe7a79479cec64697626fc","src/error.rs":"f2cbab99691f36c98c24d297de3a303de258ddd3a06e2f54cb5efce20eb3740b","src/lib.rs":"4fe4e7d6a912a850b709ed23e372acd4f214890066322b4720376f7772bb776e","src/manager.rs":"ff2d76056e3a7200035b2e75c5bc2159f337e59c076dddd2476e3094b6ae3741","src/migrate.rs":"674cee0d027fc2eed3b09cebe686c837a97725099c967d8c2f49d19e793e6bfd","src/readwrite.rs":"fde695333e4845f4f53d63da6281f585919e2a3ac5cfe00d173cc139bc822763","src/store.rs":"409d13b1ea0d1254dae947ecbce50e741fb71c3ca118a78803b734336dce6a8f","src/store/integer.rs":"f386474c971f671c9b316a16ebff5b586be6837c886f443753ae13277a7e0070","src/store/integermulti.rs":"1a0912f97619297da31cc8c146e38941b88539d2857df81191a49c8dbd18625d","src/store/multi.rs":"2dec01c2202a2c9069cced4e1e42906b01d0b85df25d17e0ea810c05fa8395d0","src/store/single.rs":"c55c3600714f5ed9e820b16c2335ae00a0071174e0a32b9df89a34182a4b908c","src/value.rs":"7fae77a8291b951591e557ec694bfdadc9eb78557dad36a970cfcdcfb83fd238","tests/integer-store.rs":"f7e06c71b0dead2323c7c61fc8bcbffbdd3a4796eebf6138db9cce3dbba716a3","tests/manager.rs":"97ec61145dc227f4f5fbcb6449c096bbe5b9a09db4e61ff4491c0443fe9adf26","tests/multi-integer-store.rs":"83295b0135c502321304aa06b05d5a9eeab41b1438ed7ddf2cb1a3613dfef4d9","tests/test_txn.rs":"f486d8bd485398e49ae64eac59ca3b44dfa7f8340aab17483cd3e9864fadd88b"},"package":"30a3dbc1f4971372545ed4175f23ef206c81e5874cd574d153646e7ee78f6793"}
|
|
@ -1,15 +0,0 @@
|
|||
# Community Participation Guidelines
|
||||
|
||||
This repository is governed by Mozilla's code of conduct and etiquette guidelines.
|
||||
For more details, please read the
|
||||
[Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/).
|
||||
|
||||
## How to Report
|
||||
For more information on how to report violations of the Community Participation Guidelines, please read our '[How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/)' page.
|
||||
|
||||
<!--
|
||||
## Project Specific Etiquette
|
||||
|
||||
In some cases, there will be additional project etiquette i.e.: (https://bugzilla.mozilla.org/page.cgi?id=etiquette.html).
|
||||
Please update for your project.
|
||||
-->
|
|
@ -1,418 +0,0 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad235dabf00f36301792cfe82499880ba54c6486be094d1047b02bacb67c14e8"
|
||||
dependencies = [
|
||||
"backtrace-sys",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace-sys"
|
||||
version = "0.1.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e17b52e737c40a7d75abca20b29a19a0eb7ba9fc72c5a72dd282a0a3c2c0dc35"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "failure"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8529c2421efa3066a5cbd8063d2244603824daccb6936b079010bb2aa89464b"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"failure_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure_derive"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "030a733c8287d6213886dd487564ff5c8f6aae10278b3588ed177f9d18f8d231"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
|
||||
dependencies = [
|
||||
"matches",
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018"
|
||||
|
||||
[[package]]
|
||||
name = "lmdb-rkv"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "447a296f7aca299cfbb50f4e4f3d49451549af655fb7215d7f8c0c3d64bad42b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"libc",
|
||||
"lmdb-rkv-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lmdb-rkv-sys"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b27470ac25167b3afdfb6af8fcd3bc1be67de50ffbdaf4073378cfded6ae24a5"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matches"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ordered-float"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_hc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rkv"
|
||||
version = "0.10.4"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"bincode",
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"failure",
|
||||
"lazy_static",
|
||||
"lmdb-rkv",
|
||||
"ordered-float",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"tempfile",
|
||||
"url",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"rand",
|
||||
"redox_syscall",
|
||||
"remove_dir_all",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
|
||||
dependencies = [
|
||||
"matches",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb"
|
||||
dependencies = [
|
||||
"idna",
|
||||
"matches",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.9.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
@ -1,75 +0,0 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "rkv"
|
||||
version = "0.10.4"
|
||||
authors = ["Richard Newman <rnewman@twinql.com>", "Nan Jiang <najiang@mozilla.com>", "Myk Melez <myk@mykzilla.org>", "Victor Porof <vporof@mozilla.com>"]
|
||||
exclude = ["/tests/envs/*"]
|
||||
description = "a simple, humane, typed Rust interface to LMDB"
|
||||
homepage = "https://github.com/mozilla/rkv"
|
||||
documentation = "https://docs.rs/rkv"
|
||||
readme = "README.md"
|
||||
keywords = ["lmdb", "database", "storage"]
|
||||
categories = ["database"]
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/mozilla/rkv"
|
||||
[dependencies.arrayref]
|
||||
version = "0.3"
|
||||
|
||||
[dependencies.bincode]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.bitflags]
|
||||
version = "1"
|
||||
|
||||
[dependencies.byteorder]
|
||||
version = "1"
|
||||
|
||||
[dependencies.failure]
|
||||
version = "0.1"
|
||||
features = ["derive"]
|
||||
default_features = false
|
||||
|
||||
[dependencies.lazy_static]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.lmdb-rkv]
|
||||
version = "0.14"
|
||||
|
||||
[dependencies.ordered-float]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.serde_derive]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.url]
|
||||
version = "2.0"
|
||||
|
||||
[dependencies.uuid]
|
||||
version = "0.8"
|
||||
[dev-dependencies.byteorder]
|
||||
version = "1"
|
||||
|
||||
[dev-dependencies.tempfile]
|
||||
version = "3"
|
||||
|
||||
[features]
|
||||
backtrace = ["failure/backtrace", "failure/std"]
|
||||
default = []
|
||||
with-asan = ["lmdb-rkv/with-asan"]
|
||||
with-fuzzer = ["lmdb-rkv/with-fuzzer"]
|
||||
with-fuzzer-no-link = ["lmdb-rkv/with-fuzzer-no-link"]
|
|
@ -1,202 +0,0 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -1,91 +0,0 @@
|
|||
# rkv
|
||||
|
||||
[![Travis CI Build Status](https://travis-ci.org/mozilla/rkv.svg?branch=master)](https://travis-ci.org/mozilla/rkv)
|
||||
[![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/lk936u5y5bi6qafb/branch/master?svg=true)](https://ci.appveyor.com/project/mykmelez/rkv/branch/master)
|
||||
[![Documentation](https://docs.rs/rkv/badge.svg)](https://docs.rs/rkv/)
|
||||
[![Crate](https://img.shields.io/crates/v/rkv.svg)](https://crates.io/crates/rkv)
|
||||
|
||||
The [rkv Rust crate](https://crates.io/crates/rkv) is a simple, humane, typed key-value storage solution. It supports multiple backend engines with varying guarantees, such as [LMDB](http://www.lmdb.tech/doc/) for performance, or "SafeMode" for reliability.
|
||||
|
||||
This master branch only supports the LMDB backend. We're looking into supporting multiple backends, starting with "SafeMode" in the [feature branch](https://github.com/mozilla/rkv/tree/safe-mode).
|
||||
|
||||
## ⚠️ Warning ⚠️
|
||||
|
||||
The LMDB backend is currently unstable and crash-prone. We're attempting to fix these crashes in bugs [1538539](https://bugzilla.mozilla.org/show_bug.cgi?id=1538539), [1538541](https://bugzilla.mozilla.org/show_bug.cgi?id=1538541) and [1550174](https://bugzilla.mozilla.org/show_bug.cgi?id=1550174).
|
||||
|
||||
To use rkv in production/release environments at Mozilla, you may do so with the "SafeMode" backend, for example:
|
||||
|
||||
```toml
|
||||
rkv = { git = "https://github.com/mozilla/rkv", branch="safe-mode", default-features = false }
|
||||
```
|
||||
|
||||
```rust
|
||||
use rkv::{Manager, Rkv};
|
||||
use rkv::backend::{SafeMode, SafeModeEnvironment};
|
||||
|
||||
let mut manager = Manager::<SafeModeEnvironment>::singleton().write().unwrap();
|
||||
let shared_rkv = manager.get_or_create(path, Rkv::new::<SafeMode>).unwrap();
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
Instead of a branch, we suggest using a specific `rev` instead. For example, `4a1cc23906865626fa715fd99d98620169d3fd7b` is the latest stable version for "safe-mode".
|
||||
|
||||
The "SafeMode` backend performs well, with two caveats: the entire database is stored in memory, and write transactions are synchronously written to disk on commit.
|
||||
|
||||
In the future, it will be advisable to switch to a different backend with better performance guarantees. We're working on either fixing the LMDB crashes, or offering more choices of backend engines (e.g. SQLite).
|
||||
|
||||
## Use
|
||||
|
||||
Comprehensive information about using rkv is available in its [online documentation](https://docs.rs/rkv/), which can also be generated for local consumption:
|
||||
|
||||
```sh
|
||||
cargo doc --open
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
Build this project as you would build other Rust crates:
|
||||
|
||||
```sh
|
||||
cargo build
|
||||
```
|
||||
|
||||
### Features
|
||||
|
||||
There are several features that you can opt-in and out of when using rkv:
|
||||
|
||||
By default, `db-dup-sort` and `db-int-key` features offer high level database APIs which allow multiple values per key, and optimizations around integer-based keys respectively. Opt out of these default features when specifying the rkv dependency in your Cargo.toml file to disable them; doing so avoids a certain amount of overhead required to support them.
|
||||
|
||||
If you specify the `backtrace` feature, backtraces will be enabled in "failure"
|
||||
errors. This feature is disabled by default.
|
||||
|
||||
To aid fuzzing efforts, `with-asan`, `with-fuzzer`, and `with-fuzzer-no-link` configure the build scripts responsible with compiling the underlying backing engines (e.g. LMDB) to build with these LLMV features enabled. Please refer to the official LLVM/Clang documentation on them for more informatiuon. These features are also disabled by default.
|
||||
|
||||
## Test
|
||||
|
||||
Test this project as you would test other Rust crates:
|
||||
|
||||
```sh
|
||||
cargo test
|
||||
```
|
||||
|
||||
The project includes unit and doc tests embedded in the `src/` files, integration tests in the `tests/` subdirectory, and usage examples in the `examples/` subdirectory. To ensure your changes don't break examples, also run them via the run-all-examples.sh shell script:
|
||||
|
||||
```sh
|
||||
./run-all-examples.sh
|
||||
```
|
||||
|
||||
Note: the test fixtures in the `tests/envs/` subdirectory aren't included in the package published to crates.io, so you must clone this repository in order to run the tests that depend on those fixtures or use the `rand` and `dump` executables to recreate them.
|
||||
|
||||
## Contribute
|
||||
|
||||
Of the various open source archetypes described in [A Framework for Purposeful Open Source](https://medium.com/mozilla-open-innovation/whats-your-open-source-strategy-here-are-10-answers-383221b3f9d3), the rkv project most closely resembles the Specialty Library, and we welcome contributions. Please report problems or ask questions using this repo's GitHub [issue tracker](https://github.com/mozilla/rkv/issues) and submit [pull requests](https://github.com/mozilla/rkv/pulls) for code and documentation changes.
|
||||
|
||||
rkv relies on the latest [rustfmt](https://github.com/rust-lang-nursery/rustfmt) for code formatting, so please make sure your pull request passes the rustfmt before submitting it for review. See rustfmt's [quick start](https://github.com/rust-lang-nursery/rustfmt#quick-start) for installation details.
|
||||
|
||||
We follow Mozilla's [Community Participation Guidelines](https://www.mozilla.org/en-US/about/governance/policies/participation/) while contributing to this project.
|
||||
|
||||
## License
|
||||
|
||||
The rkv source code is licensed under the Apache License, Version 2.0, as described in the [LICENSE](https://github.com/mozilla/rkv/blob/master/LICENSE) file.
|
|
@ -1,11 +0,0 @@
|
|||
## Examples of how to use rkv
|
||||
|
||||
All examples can be executed with:
|
||||
|
||||
```
|
||||
cargo run --example $name
|
||||
```
|
||||
|
||||
* [`simple-store`](simple-store.rs) - a simple key/value store that showcases the basic usage of rkv.
|
||||
|
||||
* [`iterator`](iterator.rs) - a demo that showcases the basic usage of iterators in rkv.
|
|
@ -1,75 +0,0 @@
|
|||
// Any copyright is dedicated to the Public Domain.
|
||||
// http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
//! A demo that showcases the basic usage of iterators in rkv.
|
||||
//!
|
||||
//! You can test this out by running:
|
||||
//!
|
||||
//! cargo run --example iterator
|
||||
|
||||
use rkv::{
|
||||
Manager,
|
||||
Rkv,
|
||||
SingleStore,
|
||||
StoreError,
|
||||
StoreOptions,
|
||||
Value,
|
||||
};
|
||||
use tempfile::Builder;
|
||||
|
||||
use std::fs;
|
||||
use std::str;
|
||||
|
||||
fn main() {
|
||||
let root = Builder::new().prefix("iterator").tempdir().unwrap();
|
||||
fs::create_dir_all(root.path()).unwrap();
|
||||
let p = root.path();
|
||||
|
||||
let created_arc = Manager::singleton().write().unwrap().get_or_create(p, Rkv::new).unwrap();
|
||||
let k = created_arc.read().unwrap();
|
||||
let store = k.open_single("store", StoreOptions::create()).unwrap();
|
||||
|
||||
populate_store(&k, store).unwrap();
|
||||
|
||||
let reader = k.read().unwrap();
|
||||
|
||||
println!("Iterating from the beginning...");
|
||||
// Reader::iter_start() iterates from the first item in the store, and
|
||||
// returns the (key, value) tuples in order.
|
||||
let mut iter = store.iter_start(&reader).unwrap();
|
||||
while let Some(Ok((country, city))) = iter.next() {
|
||||
println!("{}, {:?}", str::from_utf8(country).unwrap(), city);
|
||||
}
|
||||
|
||||
println!();
|
||||
println!("Iterating from the given key...");
|
||||
// Reader::iter_from() iterates from the first key equal to or greater
|
||||
// than the given key.
|
||||
let mut iter = store.iter_from(&reader, "Japan").unwrap();
|
||||
while let Some(Ok((country, city))) = iter.next() {
|
||||
println!("{}, {:?}", str::from_utf8(country).unwrap(), city);
|
||||
}
|
||||
|
||||
println!();
|
||||
println!("Iterating from the given prefix...");
|
||||
let mut iter = store.iter_from(&reader, "Un").unwrap();
|
||||
while let Some(Ok((country, city))) = iter.next() {
|
||||
println!("{}, {:?}", str::from_utf8(country).unwrap(), city);
|
||||
}
|
||||
}
|
||||
|
||||
fn populate_store(k: &Rkv, store: SingleStore) -> Result<(), StoreError> {
|
||||
let mut writer = k.write()?;
|
||||
for (country, city) in vec![
|
||||
("Canada", Value::Str("Ottawa")),
|
||||
("United States of America", Value::Str("Washington")),
|
||||
("Germany", Value::Str("Berlin")),
|
||||
("France", Value::Str("Paris")),
|
||||
("Italy", Value::Str("Rome")),
|
||||
("United Kingdom", Value::Str("London")),
|
||||
("Japan", Value::Str("Tokyo")),
|
||||
] {
|
||||
store.put(&mut writer, country, &city)?;
|
||||
}
|
||||
writer.commit()
|
||||
}
|
|
@ -1,183 +0,0 @@
|
|||
// Any copyright is dedicated to the Public Domain.
|
||||
// http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
//! A simple rkv demo that showcases the basic usage (put/get/delete) of rkv.
|
||||
//!
|
||||
//! You can test this out by running:
|
||||
//!
|
||||
//! cargo run --example simple-store
|
||||
|
||||
use rkv::{
|
||||
Manager,
|
||||
MultiStore,
|
||||
Rkv,
|
||||
StoreOptions,
|
||||
Value,
|
||||
Writer,
|
||||
};
|
||||
use tempfile::Builder;
|
||||
|
||||
use std::fs;
|
||||
|
||||
fn getput<'env, 's>(store: MultiStore, writer: &'env mut Writer, ids: &'s mut Vec<String>) {
|
||||
let keys = vec!["str1", "str2", "str3"];
|
||||
// we convert the writer into a cursor so that we can safely read
|
||||
for k in keys.iter() {
|
||||
// this is a multi-valued database, so get returns an iterator
|
||||
let mut iter = store.get(writer, k).unwrap();
|
||||
while let Some(Ok((_key, val))) = iter.next() {
|
||||
if let Value::Str(s) = val.unwrap() {
|
||||
ids.push(s.to_owned());
|
||||
} else {
|
||||
panic!("didn't get a string back!");
|
||||
}
|
||||
}
|
||||
}
|
||||
for id in ids {
|
||||
store.put(writer, &id, &Value::Blob(b"weeeeeee")).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn delete(store: MultiStore, writer: &mut Writer) {
|
||||
let keys = vec!["str1", "str2", "str3"];
|
||||
let vals = vec!["string uno", "string quatro", "string siete"];
|
||||
// we convert the writer into a cursor so that we can safely read
|
||||
for i in 0..keys.len() {
|
||||
store.delete(writer, &keys[i], &Value::Str(vals[i])).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let root = Builder::new().prefix("simple-db").tempdir().unwrap();
|
||||
fs::create_dir_all(root.path()).unwrap();
|
||||
let p = root.path();
|
||||
|
||||
// The manager enforces that each process opens the same lmdb environment at most once
|
||||
let created_arc = Manager::singleton().write().unwrap().get_or_create(p, Rkv::new).unwrap();
|
||||
let k = created_arc.read().unwrap();
|
||||
|
||||
// Creates a store called "store"
|
||||
let store = k.open_single("store", StoreOptions::create()).unwrap();
|
||||
|
||||
let multistore = k.open_multi("multistore", StoreOptions::create()).unwrap();
|
||||
|
||||
println!("Inserting data...");
|
||||
{
|
||||
// Use a writer to mutate the store
|
||||
let mut writer = k.write().unwrap();
|
||||
store.put(&mut writer, "int", &Value::I64(1234)).unwrap();
|
||||
store.put(&mut writer, "uint", &Value::U64(1234_u64)).unwrap();
|
||||
store.put(&mut writer, "float", &Value::F64(1234.0.into())).unwrap();
|
||||
store.put(&mut writer, "instant", &Value::Instant(1_528_318_073_700)).unwrap();
|
||||
store.put(&mut writer, "boolean", &Value::Bool(true)).unwrap();
|
||||
store.put(&mut writer, "string", &Value::Str("héllo, yöu")).unwrap();
|
||||
store.put(&mut writer, "json", &Value::Json(r#"{"foo":"bar", "number": 1}"#)).unwrap();
|
||||
store.put(&mut writer, "blob", &Value::Blob(b"blob")).unwrap();
|
||||
writer.commit().unwrap();
|
||||
}
|
||||
|
||||
println!("Testing getput");
|
||||
{
|
||||
let mut ids = Vec::new();
|
||||
let mut writer = k.write().unwrap();
|
||||
multistore.put(&mut writer, "str1", &Value::Str("string uno")).unwrap();
|
||||
multistore.put(&mut writer, "str1", &Value::Str("string dos")).unwrap();
|
||||
multistore.put(&mut writer, "str1", &Value::Str("string tres")).unwrap();
|
||||
multistore.put(&mut writer, "str2", &Value::Str("string quatro")).unwrap();
|
||||
multistore.put(&mut writer, "str2", &Value::Str("string cinco")).unwrap();
|
||||
multistore.put(&mut writer, "str2", &Value::Str("string seis")).unwrap();
|
||||
multistore.put(&mut writer, "str3", &Value::Str("string siete")).unwrap();
|
||||
multistore.put(&mut writer, "str3", &Value::Str("string ocho")).unwrap();
|
||||
multistore.put(&mut writer, "str3", &Value::Str("string nueve")).unwrap();
|
||||
getput(multistore, &mut writer, &mut ids);
|
||||
writer.commit().unwrap();
|
||||
let mut writer = k.write().unwrap();
|
||||
delete(multistore, &mut writer);
|
||||
writer.commit().unwrap();
|
||||
}
|
||||
println!("Looking up keys...");
|
||||
{
|
||||
// Use a reader to query the store
|
||||
let reader = k.read().unwrap();
|
||||
println!("Get int {:?}", store.get(&reader, "int").unwrap());
|
||||
println!("Get uint {:?}", store.get(&reader, "uint").unwrap());
|
||||
println!("Get float {:?}", store.get(&reader, "float").unwrap());
|
||||
println!("Get instant {:?}", store.get(&reader, "instant").unwrap());
|
||||
println!("Get boolean {:?}", store.get(&reader, "boolean").unwrap());
|
||||
println!("Get string {:?}", store.get(&reader, "string").unwrap());
|
||||
println!("Get json {:?}", store.get(&reader, "json").unwrap());
|
||||
println!("Get blob {:?}", store.get(&reader, "blob").unwrap());
|
||||
println!("Get non-existent {:?}", store.get(&reader, "non-existent").unwrap());
|
||||
}
|
||||
|
||||
println!("Looking up keys via Writer.get()...");
|
||||
{
|
||||
let mut writer = k.write().unwrap();
|
||||
store.put(&mut writer, "foo", &Value::Str("bar")).unwrap();
|
||||
store.put(&mut writer, "bar", &Value::Str("baz")).unwrap();
|
||||
store.delete(&mut writer, "foo").unwrap();
|
||||
println!("It should be None! ({:?})", store.get(&writer, "foo").unwrap());
|
||||
println!("Get bar ({:?})", store.get(&writer, "bar").unwrap());
|
||||
writer.commit().unwrap();
|
||||
let reader = k.read().expect("reader");
|
||||
println!("It should be None! ({:?})", store.get(&reader, "foo").unwrap());
|
||||
println!("Get bar {:?}", store.get(&reader, "bar").unwrap());
|
||||
}
|
||||
|
||||
println!("Aborting transaction...");
|
||||
{
|
||||
// Aborting a write transaction rollbacks the change(s)
|
||||
let mut writer = k.write().unwrap();
|
||||
store.put(&mut writer, "foo", &Value::Str("bar")).unwrap();
|
||||
writer.abort();
|
||||
|
||||
let reader = k.read().expect("reader");
|
||||
println!("It should be None! ({:?})", store.get(&reader, "foo").unwrap());
|
||||
// Explicitly aborting a transaction is not required unless an early
|
||||
// abort is desired, since both read and write transactions will
|
||||
// implicitly be aborted once they go out of scope.
|
||||
}
|
||||
|
||||
println!("Deleting keys...");
|
||||
{
|
||||
// Deleting a key/value also requires a write transaction
|
||||
let mut writer = k.write().unwrap();
|
||||
store.put(&mut writer, "foo", &Value::Str("bar")).unwrap();
|
||||
store.delete(&mut writer, "foo").unwrap();
|
||||
println!("It should be None! ({:?})", store.get(&writer, "foo").unwrap());
|
||||
writer.commit().unwrap();
|
||||
|
||||
// Committing a transaction consumes the writer, preventing you
|
||||
// from reusing it by failing and reporting a compile-time error.
|
||||
// This line would report error[E0382]: use of moved value: `writer`.
|
||||
// store.put(&mut writer, "baz", &Value::Str("buz")).unwrap();
|
||||
}
|
||||
|
||||
println!("Clearing store...");
|
||||
{
|
||||
// Clearing a store deletes all the entries in that store
|
||||
let mut writer = k.write().unwrap();
|
||||
store.put(&mut writer, "foo", &Value::Str("bar")).unwrap();
|
||||
store.put(&mut writer, "bar", &Value::Str("baz")).unwrap();
|
||||
store.clear(&mut writer).unwrap();
|
||||
writer.commit().unwrap();
|
||||
|
||||
let reader = k.read().expect("reader");
|
||||
println!("It should be None! ({:?})", store.get(&reader, "foo").unwrap());
|
||||
println!("It should be None! ({:?})", store.get(&reader, "bar").unwrap());
|
||||
}
|
||||
|
||||
println!("Write and read on multiple stores...");
|
||||
{
|
||||
let another_store = k.open_single("another_store", StoreOptions::create()).unwrap();
|
||||
let mut writer = k.write().unwrap();
|
||||
store.put(&mut writer, "foo", &Value::Str("bar")).unwrap();
|
||||
another_store.put(&mut writer, "foo", &Value::Str("baz")).unwrap();
|
||||
writer.commit().unwrap();
|
||||
|
||||
let reader = k.read().unwrap();
|
||||
println!("Get from store value: {:?}", store.get(&reader, "foo").unwrap());
|
||||
println!("Get from another store value: {:?}", another_store.get(&reader, "foo").unwrap());
|
||||
}
|
||||
println!("Environment statistics: btree depth = {}", k.stat().unwrap().depth());
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
cargo build --examples
|
||||
|
||||
for file in examples/*; do
|
||||
filename=$(basename ${file})
|
||||
extension=${filename##*.}
|
||||
example_name=${filename%.*}
|
||||
if [[ "${extension}" = "rs" ]]; then
|
||||
cargo run --example ${example_name}
|
||||
fi
|
||||
done
|
|
@ -1,55 +0,0 @@
|
|||
// Copyright 2018-2019 Mozilla
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
// this file except in compliance with the License. You may obtain a copy of the
|
||||
// License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
extern crate rkv;
|
||||
|
||||
use rkv::{
|
||||
error::MigrateError,
|
||||
migrate::Migrator,
|
||||
};
|
||||
use std::{
|
||||
env::args,
|
||||
io,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
fn main() -> Result<(), MigrateError> {
|
||||
let mut cli_args = args();
|
||||
let mut db_name = None;
|
||||
let mut env_path = None;
|
||||
|
||||
// The first arg is the name of the program, which we can ignore.
|
||||
cli_args.next();
|
||||
|
||||
while let Some(arg) = cli_args.next() {
|
||||
if &arg[0..1] == "-" {
|
||||
match &arg[1..] {
|
||||
"s" => {
|
||||
db_name = match cli_args.next() {
|
||||
None => return Err("-s must be followed by database name".into()),
|
||||
Some(str) => Some(str),
|
||||
};
|
||||
},
|
||||
str => return Err(format!("arg -{} not recognized", str).into()),
|
||||
}
|
||||
} else {
|
||||
if env_path.is_some() {
|
||||
return Err("must provide only one path to the LMDB environment".into());
|
||||
}
|
||||
env_path = Some(arg);
|
||||
}
|
||||
}
|
||||
|
||||
let env_path = env_path.ok_or("must provide a path to the LMDB environment")?;
|
||||
let mut migrator: Migrator = Migrator::new(Path::new(&env_path))?;
|
||||
migrator.dump(db_name.as_ref().map(String::as_str), io::stdout()).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
// Copyright 2018-2019 Mozilla
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
// this file except in compliance with the License. You may obtain a copy of the
|
||||
// License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
//! A command-line utility to create an LMDB environment containing random data.
|
||||
//! It requires one flag, `-s path/to/environment`, which specifies the location
|
||||
//! where the tool should create the environment. Optionally, you may specify
|
||||
//! the number of key/value pairs to create via the `-n <number>` flag
|
||||
//! (for which the default value is 50).
|
||||
|
||||
extern crate rkv;
|
||||
|
||||
use rkv::{
|
||||
Rkv,
|
||||
SingleStore,
|
||||
StoreOptions,
|
||||
Value,
|
||||
};
|
||||
use std::{
|
||||
env::args,
|
||||
fs::{
|
||||
create_dir_all,
|
||||
File,
|
||||
},
|
||||
io::Read,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let mut args = args();
|
||||
let mut database = None;
|
||||
let mut path = None;
|
||||
let mut num_pairs = 50;
|
||||
|
||||
// The first arg is the name of the program, which we can ignore.
|
||||
args.next();
|
||||
|
||||
while let Some(arg) = args.next() {
|
||||
if &arg[0..1] == "-" {
|
||||
match &arg[1..] {
|
||||
"s" => {
|
||||
database = match args.next() {
|
||||
None => panic!("-s must be followed by database arg"),
|
||||
Some(str) => Some(str),
|
||||
};
|
||||
},
|
||||
"n" => {
|
||||
num_pairs = match args.next() {
|
||||
None => panic!("-s must be followed by number of pairs"),
|
||||
Some(str) => str.parse().expect("number"),
|
||||
};
|
||||
},
|
||||
str => panic!("arg -{} not recognized", str),
|
||||
}
|
||||
} else {
|
||||
if path.is_some() {
|
||||
panic!("must provide only one path to the LMDB environment");
|
||||
}
|
||||
path = Some(arg);
|
||||
}
|
||||
}
|
||||
|
||||
if path.is_none() {
|
||||
panic!("must provide a path to the LMDB environment");
|
||||
}
|
||||
let path = path.unwrap();
|
||||
|
||||
create_dir_all(&path).expect("dir created");
|
||||
|
||||
let mut builder = Rkv::environment_builder();
|
||||
builder.set_max_dbs(2);
|
||||
// Allocate enough map to accommodate the largest random collection.
|
||||
// We currently do this by allocating twice the maximum possible size
|
||||
// of the pairs (assuming maximum key and value sizes).
|
||||
builder.set_map_size((511 + 65535) * num_pairs * 2);
|
||||
let rkv = Rkv::from_env(Path::new(&path), builder).expect("Rkv");
|
||||
let store: SingleStore =
|
||||
rkv.open_single(database.as_ref().map(|x| x.as_str()), StoreOptions::create()).expect("opened");
|
||||
let mut writer = rkv.write().expect("writer");
|
||||
|
||||
// Generate random values for the number of keys and key/value lengths.
|
||||
// On Linux, "Just use /dev/urandom!" <https://www.2uo.de/myths-about-urandom/>.
|
||||
// On macOS it doesn't matter (/dev/random and /dev/urandom are identical).
|
||||
let mut random = File::open("/dev/urandom").unwrap();
|
||||
let mut nums = [0u8; 4];
|
||||
random.read_exact(&mut nums).unwrap();
|
||||
|
||||
// Generate 0–255 pairs.
|
||||
for _ in 0..num_pairs {
|
||||
// Generate key and value lengths. The key must be 1–511 bytes long.
|
||||
// The value length can be 0 and is essentially unbounded; we generate
|
||||
// value lengths of 0–0xffff (65535).
|
||||
// NB: the modulus method for generating a random number within a range
|
||||
// introduces distribution skew, but we don't need it to be perfect.
|
||||
let key_len = ((u16::from(nums[0]) + (u16::from(nums[1]) << 8)) % 511 + 1) as usize;
|
||||
let value_len = (u16::from(nums[2]) + (u16::from(nums[3]) << 8)) as usize;
|
||||
|
||||
let mut key: Vec<u8> = vec![0; key_len];
|
||||
random.read_exact(&mut key[0..key_len]).unwrap();
|
||||
|
||||
let mut value: Vec<u8> = vec![0; value_len];
|
||||
random.read_exact(&mut value[0..value_len]).unwrap();
|
||||
|
||||
store.put(&mut writer, key, &Value::Blob(&value)).expect("wrote");
|
||||
}
|
||||
|
||||
writer.commit().expect("committed");
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,190 +0,0 @@
|
|||
// Copyright 2018-2019 Mozilla
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
// this file except in compliance with the License. You may obtain a copy of the
|
||||
// License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use bincode;
|
||||
use failure::Fail;
|
||||
use lmdb;
|
||||
|
||||
use crate::value::Type;
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum DataError {
|
||||
#[fail(display = "unknown type tag: {}", _0)]
|
||||
UnknownType(u8),
|
||||
|
||||
#[fail(display = "unexpected type tag: expected {}, got {}", expected, actual)]
|
||||
UnexpectedType {
|
||||
expected: Type,
|
||||
actual: Type,
|
||||
},
|
||||
|
||||
#[fail(display = "empty data; expected tag")]
|
||||
Empty,
|
||||
|
||||
#[fail(display = "invalid value for type {}: {}", value_type, err)]
|
||||
DecodingError {
|
||||
value_type: Type,
|
||||
err: Box<bincode::ErrorKind>,
|
||||
},
|
||||
|
||||
#[fail(display = "couldn't encode value: {}", _0)]
|
||||
EncodingError(Box<bincode::ErrorKind>),
|
||||
|
||||
#[fail(display = "invalid uuid bytes")]
|
||||
InvalidUuid,
|
||||
}
|
||||
|
||||
impl From<Box<bincode::ErrorKind>> for DataError {
|
||||
fn from(e: Box<bincode::ErrorKind>) -> DataError {
|
||||
DataError::EncodingError(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum StoreError {
|
||||
#[fail(display = "I/O error: {:?}", _0)]
|
||||
IoError(::std::io::Error),
|
||||
|
||||
#[fail(display = "directory does not exist or not a directory: {:?}", _0)]
|
||||
DirectoryDoesNotExistError(PathBuf),
|
||||
|
||||
#[fail(display = "data error: {:?}", _0)]
|
||||
DataError(DataError),
|
||||
|
||||
#[fail(display = "lmdb error: {}", _0)]
|
||||
LmdbError(lmdb::Error),
|
||||
|
||||
#[fail(display = "read transaction already exists in thread {:?}", _0)]
|
||||
ReadTransactionAlreadyExists(::std::thread::ThreadId),
|
||||
|
||||
#[fail(display = "attempted to open DB during transaction in thread {:?}", _0)]
|
||||
OpenAttemptedDuringTransaction(::std::thread::ThreadId),
|
||||
}
|
||||
|
||||
impl StoreError {
|
||||
pub fn open_during_transaction() -> StoreError {
|
||||
StoreError::OpenAttemptedDuringTransaction(::std::thread::current().id())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<lmdb::Error> for StoreError {
|
||||
fn from(e: lmdb::Error) -> StoreError {
|
||||
match e {
|
||||
lmdb::Error::BadRslot => StoreError::ReadTransactionAlreadyExists(::std::thread::current().id()),
|
||||
e => StoreError::LmdbError(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DataError> for StoreError {
|
||||
fn from(e: DataError) -> StoreError {
|
||||
StoreError::DataError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<::std::io::Error> for StoreError {
|
||||
fn from(e: ::std::io::Error) -> StoreError {
|
||||
StoreError::IoError(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum MigrateError {
|
||||
#[fail(display = "database not found: {:?}", _0)]
|
||||
DatabaseNotFound(String),
|
||||
|
||||
#[fail(display = "{}", _0)]
|
||||
FromString(String),
|
||||
|
||||
#[fail(display = "couldn't determine bit depth")]
|
||||
IndeterminateBitDepth,
|
||||
|
||||
#[fail(display = "I/O error: {:?}", _0)]
|
||||
IoError(::std::io::Error),
|
||||
|
||||
#[fail(display = "invalid DatabaseFlags bits")]
|
||||
InvalidDatabaseBits,
|
||||
|
||||
#[fail(display = "invalid data version")]
|
||||
InvalidDataVersion,
|
||||
|
||||
#[fail(display = "invalid magic number")]
|
||||
InvalidMagicNum,
|
||||
|
||||
#[fail(display = "invalid NodeFlags bits")]
|
||||
InvalidNodeBits,
|
||||
|
||||
#[fail(display = "invalid PageFlags bits")]
|
||||
InvalidPageBits,
|
||||
|
||||
#[fail(display = "invalid page number")]
|
||||
InvalidPageNum,
|
||||
|
||||
#[fail(display = "lmdb error: {}", _0)]
|
||||
LmdbError(lmdb::Error),
|
||||
|
||||
#[fail(display = "string conversion error")]
|
||||
StringConversionError,
|
||||
|
||||
#[fail(display = "TryFromInt error: {:?}", _0)]
|
||||
TryFromIntError(::std::num::TryFromIntError),
|
||||
|
||||
#[fail(display = "unexpected Page variant")]
|
||||
UnexpectedPageVariant,
|
||||
|
||||
#[fail(display = "unexpected PageHeader variant")]
|
||||
UnexpectedPageHeaderVariant,
|
||||
|
||||
#[fail(display = "unsupported PageHeader variant")]
|
||||
UnsupportedPageHeaderVariant,
|
||||
|
||||
#[fail(display = "UTF8 error: {:?}", _0)]
|
||||
Utf8Error(::std::str::Utf8Error),
|
||||
}
|
||||
|
||||
impl From<::std::io::Error> for MigrateError {
|
||||
fn from(e: ::std::io::Error) -> MigrateError {
|
||||
MigrateError::IoError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<::std::str::Utf8Error> for MigrateError {
|
||||
fn from(e: ::std::str::Utf8Error) -> MigrateError {
|
||||
MigrateError::Utf8Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<::std::num::TryFromIntError> for MigrateError {
|
||||
fn from(e: ::std::num::TryFromIntError) -> MigrateError {
|
||||
MigrateError::TryFromIntError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for MigrateError {
|
||||
fn from(e: &str) -> MigrateError {
|
||||
MigrateError::FromString(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for MigrateError {
|
||||
fn from(e: String) -> MigrateError {
|
||||
MigrateError::FromString(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<lmdb::Error> for MigrateError {
|
||||
fn from(e: lmdb::Error) -> MigrateError {
|
||||
match e {
|
||||
e => MigrateError::LmdbError(e),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,260 +0,0 @@
|
|||
// Copyright 2018-2019 Mozilla
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
// this file except in compliance with the License. You may obtain a copy of the
|
||||
// License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
//! a simple, humane, typed Rust interface to [LMDB](http://www.lmdb.tech/doc/)
|
||||
//!
|
||||
//! It aims to achieve the following:
|
||||
//!
|
||||
//! - Avoid LMDB's sharp edges (e.g., obscure error codes for common situations).
|
||||
//! - Report errors via [failure](https://docs.rs/failure/).
|
||||
//! - Correctly restrict access to one handle per process via a [Manager](struct.Manager.html).
|
||||
//! - Use Rust's type system to make single-typed key stores (including LMDB's own integer-keyed stores)
|
||||
//! safe and ergonomic.
|
||||
//! - Encode and decode values via [bincode](https://docs.rs/bincode/)/[serde](https://docs.rs/serde/)
|
||||
//! and type tags, achieving platform-independent storage and input/output flexibility.
|
||||
//!
|
||||
//! It exposes these primary abstractions:
|
||||
//!
|
||||
//! - [Manager](struct.Manager.html): a singleton that controls access to LMDB environments
|
||||
//! - [Rkv](struct.Rkv.html): an LMDB environment that contains a set of key/value databases
|
||||
//! - [SingleStore](store/single/struct.SingleStore.html): an LMDB database that contains a set of key/value pairs
|
||||
//!
|
||||
//! Keys can be anything that implements `AsRef<[u8]>` or integers
|
||||
//! (when accessing an [IntegerStore](store/integer/struct.IntegerStore.html)).
|
||||
//! Values can be any of the types defined by the [Value](value/enum.Value.html) enum, including:
|
||||
//!
|
||||
//! - booleans (`Value::Bool`)
|
||||
//! - integers (`Value::I64`, `Value::U64`)
|
||||
//! - floats (`Value::F64`)
|
||||
//! - strings (`Value::Str`)
|
||||
//! - blobs (`Value::Blob`)
|
||||
//!
|
||||
//! See [Value](value/enum.Value.html) for the complete list of supported types.
|
||||
//!
|
||||
//! ## Basic Usage
|
||||
//! ```
|
||||
//! use rkv::{Manager, Rkv, SingleStore, Value, StoreOptions};
|
||||
//! use std::fs;
|
||||
//! use tempfile::Builder;
|
||||
//!
|
||||
//! // First determine the path to the environment, which is represented
|
||||
//! // on disk as a directory containing two files:
|
||||
//! //
|
||||
//! // * a data file containing the key/value stores
|
||||
//! // * a lock file containing metadata about current transactions
|
||||
//! //
|
||||
//! // In this example, we use the `tempfile` crate to create the directory.
|
||||
//! //
|
||||
//! let root = Builder::new().prefix("simple-db").tempdir().unwrap();
|
||||
//! fs::create_dir_all(root.path()).unwrap();
|
||||
//! let path = root.path();
|
||||
//!
|
||||
//! // The Manager enforces that each process opens the same environment
|
||||
//! // at most once by caching a handle to each environment that it opens.
|
||||
//! // Use it to retrieve the handle to an opened environment—or create one
|
||||
//! // if it hasn't already been opened:
|
||||
//! let created_arc = Manager::singleton().write().unwrap().get_or_create(path, Rkv::new).unwrap();
|
||||
//! let env = created_arc.read().unwrap();
|
||||
//!
|
||||
//! // Then you can use the environment handle to get a handle to a datastore:
|
||||
//! let store: SingleStore = env.open_single("mydb", StoreOptions::create()).unwrap();
|
||||
//!
|
||||
//! {
|
||||
//! // Use a write transaction to mutate the store via a `Writer`.
|
||||
//! // There can be only one writer for a given environment, so opening
|
||||
//! // a second one will block until the first completes.
|
||||
//! let mut writer = env.write().unwrap();
|
||||
//!
|
||||
//! // Keys are `AsRef<[u8]>`, while values are `Value` enum instances.
|
||||
//! // Use the `Blob` variant to store arbitrary collections of bytes.
|
||||
//! // Putting data returns a `Result<(), StoreError>`, where StoreError
|
||||
//! // is an enum identifying the reason for a failure.
|
||||
//! store.put(&mut writer, "int", &Value::I64(1234)).unwrap();
|
||||
//! store.put(&mut writer, "uint", &Value::U64(1234_u64)).unwrap();
|
||||
//! store.put(&mut writer, "float", &Value::F64(1234.0.into())).unwrap();
|
||||
//! store.put(&mut writer, "instant", &Value::Instant(1528318073700)).unwrap();
|
||||
//! store.put(&mut writer, "boolean", &Value::Bool(true)).unwrap();
|
||||
//! store.put(&mut writer, "string", &Value::Str("Héllo, wörld!")).unwrap();
|
||||
//! store.put(&mut writer, "json", &Value::Json(r#"{"foo":"bar", "number": 1}"#)).unwrap();
|
||||
//! store.put(&mut writer, "blob", &Value::Blob(b"blob")).unwrap();
|
||||
//!
|
||||
//! // You must commit a write transaction before the writer goes out
|
||||
//! // of scope, or the transaction will abort and the data won't persist.
|
||||
//! writer.commit().unwrap();
|
||||
//! }
|
||||
//!
|
||||
//! {
|
||||
//! // Use a read transaction to query the store via a `Reader`.
|
||||
//! // There can be multiple concurrent readers for a store, and readers
|
||||
//! // never block on a writer nor other readers.
|
||||
//! let reader = env.read().expect("reader");
|
||||
//!
|
||||
//! // Keys are `AsRef<u8>`, and the return value is `Result<Option<Value>, StoreError>`.
|
||||
//! println!("Get int {:?}", store.get(&reader, "int").unwrap());
|
||||
//! println!("Get uint {:?}", store.get(&reader, "uint").unwrap());
|
||||
//! println!("Get float {:?}", store.get(&reader, "float").unwrap());
|
||||
//! println!("Get instant {:?}", store.get(&reader, "instant").unwrap());
|
||||
//! println!("Get boolean {:?}", store.get(&reader, "boolean").unwrap());
|
||||
//! println!("Get string {:?}", store.get(&reader, "string").unwrap());
|
||||
//! println!("Get json {:?}", store.get(&reader, "json").unwrap());
|
||||
//! println!("Get blob {:?}", store.get(&reader, "blob").unwrap());
|
||||
//!
|
||||
//! // Retrieving a non-existent value returns `Ok(None)`.
|
||||
//! println!("Get non-existent value {:?}", store.get(&reader, "non-existent").unwrap());
|
||||
//!
|
||||
//! // A read transaction will automatically close once the reader
|
||||
//! // goes out of scope, so isn't necessary to close it explicitly,
|
||||
//! // although you can do so by calling `Reader.abort()`.
|
||||
//! }
|
||||
//!
|
||||
//! {
|
||||
//! // Aborting a write transaction rolls back the change(s).
|
||||
//! let mut writer = env.write().unwrap();
|
||||
//! store.put(&mut writer, "foo", &Value::Str("bar")).unwrap();
|
||||
//! writer.abort();
|
||||
//! let reader = env.read().expect("reader");
|
||||
//! println!("It should be None! ({:?})", store.get(&reader, "foo").unwrap());
|
||||
//! }
|
||||
//!
|
||||
//! {
|
||||
//! // Explicitly aborting a transaction is not required unless an early
|
||||
//! // abort is desired, since both read and write transactions will
|
||||
//! // implicitly be aborted once they go out of scope.
|
||||
//! {
|
||||
//! let mut writer = env.write().unwrap();
|
||||
//! store.put(&mut writer, "foo", &Value::Str("bar")).unwrap();
|
||||
//! }
|
||||
//! let reader = env.read().expect("reader");
|
||||
//! println!("It should be None! ({:?})", store.get(&reader, "foo").unwrap());
|
||||
//! }
|
||||
//!
|
||||
//! {
|
||||
//! // Deleting a key/value pair also requires a write transaction.
|
||||
//! let mut writer = env.write().unwrap();
|
||||
//! store.put(&mut writer, "foo", &Value::Str("bar")).unwrap();
|
||||
//! store.put(&mut writer, "bar", &Value::Str("baz")).unwrap();
|
||||
//! store.delete(&mut writer, "foo").unwrap();
|
||||
//!
|
||||
//! // A write transaction also supports reading, and the version of the
|
||||
//! // store that it reads includes the changes it has made regardless of
|
||||
//! // the commit state of that transaction.
|
||||
|
||||
//! // In the code above, "foo" and "bar" were put into the store,
|
||||
//! // then "foo" was deleted so only "bar" will return a result when the
|
||||
//! // database is queried via the writer.
|
||||
//! println!("It should be None! ({:?})", store.get(&writer, "foo").unwrap());
|
||||
//! println!("Get bar ({:?})", store.get(&writer, "bar").unwrap());
|
||||
//!
|
||||
//! // But a reader won't see that change until the write transaction
|
||||
//! // is committed.
|
||||
//! {
|
||||
//! let reader = env.read().expect("reader");
|
||||
//! println!("Get foo {:?}", store.get(&reader, "foo").unwrap());
|
||||
//! println!("Get bar {:?}", store.get(&reader, "bar").unwrap());
|
||||
//! }
|
||||
//! writer.commit().unwrap();
|
||||
//! {
|
||||
//! let reader = env.read().expect("reader");
|
||||
//! println!("It should be None! ({:?})", store.get(&reader, "foo").unwrap());
|
||||
//! println!("Get bar {:?}", store.get(&reader, "bar").unwrap());
|
||||
//! }
|
||||
//!
|
||||
//! // Committing a transaction consumes the writer, preventing you
|
||||
//! // from reusing it by failing at compile time with an error.
|
||||
//! // This line would report error[E0382]: borrow of moved value: `writer`.
|
||||
//! // store.put(&mut writer, "baz", &Value::Str("buz")).unwrap();
|
||||
//! }
|
||||
//!
|
||||
//! {
|
||||
//! // Clearing all the entries in the store with a write transaction.
|
||||
//! {
|
||||
//! let mut writer = env.write().unwrap();
|
||||
//! store.put(&mut writer, "foo", &Value::Str("bar")).unwrap();
|
||||
//! store.put(&mut writer, "bar", &Value::Str("baz")).unwrap();
|
||||
//! writer.commit().unwrap();
|
||||
//! }
|
||||
//!
|
||||
//! {
|
||||
//! let mut writer = env.write().unwrap();
|
||||
//! store.clear(&mut writer).unwrap();
|
||||
//! writer.commit().unwrap();
|
||||
//! }
|
||||
//!
|
||||
//! {
|
||||
//! let reader = env.read().expect("reader");
|
||||
//! println!("It should be None! ({:?})", store.get(&reader, "foo").unwrap());
|
||||
//! println!("It should be None! ({:?})", store.get(&reader, "bar").unwrap());
|
||||
//! }
|
||||
//!
|
||||
//! }
|
||||
//!
|
||||
//! ```
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
pub use lmdb::{
|
||||
DatabaseFlags,
|
||||
EnvironmentBuilder,
|
||||
EnvironmentFlags,
|
||||
WriteFlags,
|
||||
};
|
||||
|
||||
mod env;
|
||||
pub mod error;
|
||||
mod manager;
|
||||
pub mod migrate;
|
||||
mod readwrite;
|
||||
pub mod store;
|
||||
pub mod value;
|
||||
|
||||
pub use lmdb::{
|
||||
Cursor,
|
||||
Database,
|
||||
Info,
|
||||
Iter as LmdbIter,
|
||||
RoCursor,
|
||||
Stat,
|
||||
};
|
||||
|
||||
pub use self::readwrite::{
|
||||
Readable,
|
||||
Reader,
|
||||
Writer,
|
||||
};
|
||||
pub use self::store::integer::{
|
||||
IntegerStore,
|
||||
PrimitiveInt,
|
||||
};
|
||||
pub use self::store::integermulti::MultiIntegerStore;
|
||||
pub use self::store::multi::MultiStore;
|
||||
pub use self::store::single::SingleStore;
|
||||
pub use self::store::Options as StoreOptions;
|
||||
|
||||
pub use self::env::Rkv;
|
||||
|
||||
pub use self::error::{
|
||||
DataError,
|
||||
StoreError,
|
||||
};
|
||||
|
||||
pub use self::manager::Manager;
|
||||
|
||||
pub use self::value::{
|
||||
OwnedValue,
|
||||
Value,
|
||||
};
|
||||
|
||||
fn read_transform(val: Result<&[u8], lmdb::Error>) -> Result<Option<Value>, StoreError> {
|
||||
match val {
|
||||
Ok(bytes) => Value::from_tagged_slice(bytes).map(Some).map_err(StoreError::DataError),
|
||||
Err(lmdb::Error::NotFound) => Ok(None),
|
||||
Err(e) => Err(StoreError::LmdbError(e)),
|
||||
}
|
||||
}
|
|
@ -1,196 +0,0 @@
|
|||
// Copyright 2018-2019 Mozilla
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
// this file except in compliance with the License. You may obtain a copy of the
|
||||
// License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use std::io::{
|
||||
self,
|
||||
Error,
|
||||
ErrorKind,
|
||||
};
|
||||
|
||||
use std::collections::btree_map::Entry;
|
||||
|
||||
use std::os::raw::c_uint;
|
||||
|
||||
use std::path::{
|
||||
Path,
|
||||
PathBuf,
|
||||
};
|
||||
|
||||
use std::sync::{
|
||||
Arc,
|
||||
RwLock,
|
||||
};
|
||||
|
||||
use url::Url;
|
||||
|
||||
use crate::error::StoreError;
|
||||
|
||||
use crate::Rkv;
|
||||
|
||||
lazy_static! {
|
||||
/// A process is only permitted to have one open handle to each Rkv environment.
|
||||
/// This manager exists to enforce that constraint: don't open environments directly.
|
||||
static ref MANAGER: RwLock<Manager> = RwLock::new(Manager::new());
|
||||
}
|
||||
|
||||
// Workaround the UNC path on Windows, see https://github.com/rust-lang/rust/issues/42869.
|
||||
// Otherwise, `Env::from_env()` will panic with error_no(123).
|
||||
fn canonicalize_path<'p, P>(path: P) -> io::Result<PathBuf>
|
||||
where
|
||||
P: Into<&'p Path>,
|
||||
{
|
||||
let canonical = path.into().canonicalize()?;
|
||||
if cfg!(target_os = "windows") {
|
||||
let url = Url::from_file_path(&canonical).map_err(|_e| Error::new(ErrorKind::Other, "URL passing error"))?;
|
||||
return url.to_file_path().map_err(|_e| Error::new(ErrorKind::Other, "path canonicalization error"));
|
||||
}
|
||||
Ok(canonical)
|
||||
}
|
||||
|
||||
/// A process is only permitted to have one open handle to each Rkv environment.
|
||||
/// This manager exists to enforce that constraint: don't open environments directly.
|
||||
pub struct Manager {
|
||||
environments: BTreeMap<PathBuf, Arc<RwLock<Rkv>>>,
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
fn new() -> Manager {
|
||||
Manager {
|
||||
environments: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn singleton() -> &'static RwLock<Manager> {
|
||||
&*MANAGER
|
||||
}
|
||||
|
||||
/// Return the open env at `path`, returning `None` if it has not already been opened.
|
||||
pub fn get<'p, P>(&self, path: P) -> Result<Option<Arc<RwLock<Rkv>>>, ::std::io::Error>
|
||||
where
|
||||
P: Into<&'p Path>,
|
||||
{
|
||||
let canonical = canonicalize_path(path)?;
|
||||
Ok(self.environments.get(&canonical).cloned())
|
||||
}
|
||||
|
||||
/// Return the open env at `path`, or create it by calling `f`.
|
||||
pub fn get_or_create<'p, F, P>(&mut self, path: P, f: F) -> Result<Arc<RwLock<Rkv>>, StoreError>
|
||||
where
|
||||
F: FnOnce(&Path) -> Result<Rkv, StoreError>,
|
||||
P: Into<&'p Path>,
|
||||
{
|
||||
let canonical = canonicalize_path(path)?;
|
||||
Ok(match self.environments.entry(canonical) {
|
||||
Entry::Occupied(e) => e.get().clone(),
|
||||
Entry::Vacant(e) => {
|
||||
let k = Arc::new(RwLock::new(f(e.key().as_path())?));
|
||||
e.insert(k).clone()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the open env at `path` with capacity `capacity`,
|
||||
/// or create it by calling `f`.
|
||||
pub fn get_or_create_with_capacity<'p, F, P>(
|
||||
&mut self,
|
||||
path: P,
|
||||
capacity: c_uint,
|
||||
f: F,
|
||||
) -> Result<Arc<RwLock<Rkv>>, StoreError>
|
||||
where
|
||||
F: FnOnce(&Path, c_uint) -> Result<Rkv, StoreError>,
|
||||
P: Into<&'p Path>,
|
||||
{
|
||||
let canonical = canonicalize_path(path)?;
|
||||
Ok(match self.environments.entry(canonical) {
|
||||
Entry::Occupied(e) => e.get().clone(),
|
||||
Entry::Vacant(e) => {
|
||||
let k = Arc::new(RwLock::new(f(e.key().as_path(), capacity)?));
|
||||
e.insert(k).clone()
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::fs;
|
||||
use tempfile::Builder;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Test that the manager will return the same Rkv instance each time for each path.
|
||||
#[test]
|
||||
fn test_same() {
|
||||
let root = Builder::new().prefix("test_same").tempdir().expect("tempdir");
|
||||
fs::create_dir_all(root.path()).expect("dir created");
|
||||
|
||||
let mut manager = Manager::new();
|
||||
|
||||
let p = root.path();
|
||||
assert!(manager.get(p).expect("success").is_none());
|
||||
|
||||
let created_arc = manager.get_or_create(p, Rkv::new).expect("created");
|
||||
let fetched_arc = manager.get(p).expect("success").expect("existed");
|
||||
assert!(Arc::ptr_eq(&created_arc, &fetched_arc));
|
||||
}
|
||||
|
||||
/// Test that one can mutate managed Rkv instances in surprising ways.
|
||||
#[test]
|
||||
fn test_mutate_managed_rkv() {
|
||||
let mut manager = Manager::new();
|
||||
|
||||
let root1 = Builder::new().prefix("test_mutate_managed_rkv_1").tempdir().expect("tempdir");
|
||||
fs::create_dir_all(root1.path()).expect("dir created");
|
||||
let path1 = root1.path();
|
||||
let arc = manager.get_or_create(path1, Rkv::new).expect("created");
|
||||
|
||||
// Arc<RwLock<>> has interior mutability, so we can replace arc's Rkv
|
||||
// instance with a new instance that has a different path.
|
||||
let root2 = Builder::new().prefix("test_mutate_managed_rkv_2").tempdir().expect("tempdir");
|
||||
fs::create_dir_all(root2.path()).expect("dir created");
|
||||
let path2 = root2.path();
|
||||
{
|
||||
let mut rkv = arc.write().expect("guard");
|
||||
let rkv2 = Rkv::new(path2).expect("Rkv");
|
||||
*rkv = rkv2;
|
||||
}
|
||||
|
||||
// arc now has a different internal Rkv with path2, but it's still
|
||||
// mapped to path1 in manager, so its pointer is equal to a new Arc
|
||||
// for path1.
|
||||
let path1_arc = manager.get(path1).expect("success").expect("existed");
|
||||
assert!(Arc::ptr_eq(&path1_arc, &arc));
|
||||
|
||||
// Meanwhile, a new Arc for path2 has a different pointer, even though
|
||||
// its Rkv's path is the same as arc's current path.
|
||||
let path2_arc = manager.get_or_create(path2, Rkv::new).expect("success");
|
||||
assert!(!Arc::ptr_eq(&path2_arc, &arc));
|
||||
}
|
||||
|
||||
/// Test that the manager will return the same Rkv instance each time for each path.
|
||||
#[test]
|
||||
fn test_same_with_capacity() {
|
||||
let root = Builder::new().prefix("test_same").tempdir().expect("tempdir");
|
||||
fs::create_dir_all(root.path()).expect("dir created");
|
||||
|
||||
let mut manager = Manager::new();
|
||||
|
||||
let p = root.path();
|
||||
assert!(manager.get(p).expect("success").is_none());
|
||||
|
||||
let created_arc = manager.get_or_create_with_capacity(p, 10, Rkv::with_capacity).expect("created");
|
||||
let fetched_arc = manager.get(p).expect("success").expect("existed");
|
||||
assert!(Arc::ptr_eq(&created_arc, &fetched_arc));
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,95 +0,0 @@
|
|||
// Copyright 2018-2019 Mozilla
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
// this file except in compliance with the License. You may obtain a copy of the
|
||||
// License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
use lmdb::{
|
||||
Database,
|
||||
RoCursor,
|
||||
RoTransaction,
|
||||
RwTransaction,
|
||||
Transaction,
|
||||
WriteFlags,
|
||||
};
|
||||
|
||||
use crate::error::StoreError;
|
||||
use crate::read_transform;
|
||||
use crate::value::Value;
|
||||
|
||||
pub struct Reader<'env>(pub RoTransaction<'env>);
|
||||
pub struct Writer<'env>(pub RwTransaction<'env>);
|
||||
|
||||
pub trait Readable {
|
||||
fn get<K: AsRef<[u8]>>(&self, db: Database, k: &K) -> Result<Option<Value>, StoreError>;
|
||||
fn open_ro_cursor(&self, db: Database) -> Result<RoCursor, StoreError>;
|
||||
}
|
||||
|
||||
impl<'env> Readable for Reader<'env> {
|
||||
fn get<K: AsRef<[u8]>>(&self, db: Database, k: &K) -> Result<Option<Value>, StoreError> {
|
||||
let bytes = self.0.get(db, &k);
|
||||
read_transform(bytes)
|
||||
}
|
||||
|
||||
fn open_ro_cursor(&self, db: Database) -> Result<RoCursor, StoreError> {
|
||||
self.0.open_ro_cursor(db).map_err(StoreError::LmdbError)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'env> Reader<'env> {
|
||||
pub(crate) fn new(txn: RoTransaction) -> Reader {
|
||||
Reader(txn)
|
||||
}
|
||||
|
||||
pub fn abort(self) {
|
||||
self.0.abort();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'env> Readable for Writer<'env> {
|
||||
fn get<K: AsRef<[u8]>>(&self, db: Database, k: &K) -> Result<Option<Value>, StoreError> {
|
||||
let bytes = self.0.get(db, &k);
|
||||
read_transform(bytes)
|
||||
}
|
||||
|
||||
fn open_ro_cursor(&self, db: Database) -> Result<RoCursor, StoreError> {
|
||||
self.0.open_ro_cursor(db).map_err(StoreError::LmdbError)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'env> Writer<'env> {
|
||||
pub(crate) fn new(txn: RwTransaction) -> Writer {
|
||||
Writer(txn)
|
||||
}
|
||||
|
||||
pub fn commit(self) -> Result<(), StoreError> {
|
||||
self.0.commit().map_err(StoreError::LmdbError)
|
||||
}
|
||||
|
||||
pub fn abort(self) {
|
||||
self.0.abort();
|
||||
}
|
||||
|
||||
pub(crate) fn put<K: AsRef<[u8]>>(
|
||||
&mut self,
|
||||
db: Database,
|
||||
k: &K,
|
||||
v: &Value,
|
||||
flags: WriteFlags,
|
||||
) -> Result<(), StoreError> {
|
||||
// TODO: don't allocate twice.
|
||||
self.0.put(db, &k, &v.to_bytes()?, flags).map_err(StoreError::LmdbError)
|
||||
}
|
||||
|
||||
pub(crate) fn delete<K: AsRef<[u8]>>(&mut self, db: Database, k: &K, v: Option<&[u8]>) -> Result<(), StoreError> {
|
||||
self.0.del(db, &k, v).map_err(StoreError::LmdbError)
|
||||
}
|
||||
|
||||
pub(crate) fn clear(&mut self, db: Database) -> Result<(), StoreError> {
|
||||
self.0.clear_db(db).map_err(StoreError::LmdbError)
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
pub mod integer;
|
||||
pub mod integermulti;
|
||||
pub mod multi;
|
||||
pub mod single;
|
||||
|
||||
use lmdb::DatabaseFlags;
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone)]
|
||||
pub struct Options {
|
||||
pub create: bool,
|
||||
pub flags: DatabaseFlags,
|
||||
}
|
||||
|
||||
impl Options {
|
||||
pub fn create() -> Options {
|
||||
Options {
|
||||
create: true,
|
||||
flags: DatabaseFlags::empty(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,172 +0,0 @@
|
|||
// Copyright 2018-2019 Mozilla
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
// this file except in compliance with the License. You may obtain a copy of the
|
||||
// License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use bincode::serialize;
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
use lmdb::Database;
|
||||
|
||||
use crate::error::{
|
||||
DataError,
|
||||
StoreError,
|
||||
};
|
||||
|
||||
use crate::readwrite::{
|
||||
Readable,
|
||||
Writer,
|
||||
};
|
||||
|
||||
use crate::value::Value;
|
||||
|
||||
use crate::store::single::SingleStore;
|
||||
|
||||
pub trait EncodableKey {
|
||||
fn to_bytes(&self) -> Result<Vec<u8>, DataError>;
|
||||
}
|
||||
|
||||
pub trait PrimitiveInt: EncodableKey {}
|
||||
|
||||
impl PrimitiveInt for u32 {}
|
||||
|
||||
impl<T> EncodableKey for T
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
fn to_bytes(&self) -> Result<Vec<u8>, DataError> {
|
||||
serialize(self) // TODO: limited key length.
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Key<K> {
|
||||
bytes: Vec<u8>,
|
||||
phantom: PhantomData<K>,
|
||||
}
|
||||
|
||||
impl<K> AsRef<[u8]> for Key<K>
|
||||
where
|
||||
K: EncodableKey,
|
||||
{
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.bytes.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K> Key<K>
|
||||
where
|
||||
K: EncodableKey,
|
||||
{
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub(crate) fn new(k: &K) -> Result<Key<K>, DataError> {
|
||||
Ok(Key {
|
||||
bytes: k.to_bytes()?,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IntegerStore<K>
|
||||
where
|
||||
K: PrimitiveInt,
|
||||
{
|
||||
inner: SingleStore,
|
||||
phantom: PhantomData<K>,
|
||||
}
|
||||
|
||||
impl<K> IntegerStore<K>
|
||||
where
|
||||
K: PrimitiveInt,
|
||||
{
|
||||
pub(crate) fn new(db: Database) -> IntegerStore<K> {
|
||||
IntegerStore {
|
||||
inner: SingleStore::new(db),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get<'env, T: Readable>(&self, reader: &'env T, k: K) -> Result<Option<Value<'env>>, StoreError> {
|
||||
self.inner.get(reader, Key::new(&k)?)
|
||||
}
|
||||
|
||||
pub fn put(&self, writer: &mut Writer, k: K, v: &Value) -> Result<(), StoreError> {
|
||||
self.inner.put(writer, Key::new(&k)?, v)
|
||||
}
|
||||
|
||||
pub fn delete(&self, writer: &mut Writer, k: K) -> Result<(), StoreError> {
|
||||
self.inner.delete(writer, Key::new(&k)?)
|
||||
}
|
||||
|
||||
pub fn clear(&self, writer: &mut Writer) -> Result<(), StoreError> {
|
||||
self.inner.clear(writer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::fs;
|
||||
use tempfile::Builder;
|
||||
|
||||
use super::*;
|
||||
use crate::*;
|
||||
|
||||
#[test]
|
||||
fn test_integer_keys() {
|
||||
let root = Builder::new().prefix("test_integer_keys").tempdir().expect("tempdir");
|
||||
fs::create_dir_all(root.path()).expect("dir created");
|
||||
let k = Rkv::new(root.path()).expect("new succeeded");
|
||||
let s = k.open_integer("s", StoreOptions::create()).expect("open");
|
||||
|
||||
macro_rules! test_integer_keys {
|
||||
($type:ty, $key:expr) => {{
|
||||
let mut writer = k.write().expect("writer");
|
||||
|
||||
s.put(&mut writer, $key, &Value::Str("hello!")).expect("write");
|
||||
assert_eq!(s.get(&writer, $key).expect("read"), Some(Value::Str("hello!")));
|
||||
writer.commit().expect("committed");
|
||||
|
||||
let reader = k.read().expect("reader");
|
||||
assert_eq!(s.get(&reader, $key).expect("read"), Some(Value::Str("hello!")));
|
||||
}};
|
||||
}
|
||||
|
||||
test_integer_keys!(u32, std::u32::MIN);
|
||||
test_integer_keys!(u32, std::u32::MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clear() {
|
||||
let root = Builder::new().prefix("test_integer_clear").tempdir().expect("tempdir");
|
||||
fs::create_dir_all(root.path()).expect("dir created");
|
||||
let k = Rkv::new(root.path()).expect("new succeeded");
|
||||
let s = k.open_integer("s", StoreOptions::create()).expect("open");
|
||||
|
||||
{
|
||||
let mut writer = k.write().expect("writer");
|
||||
s.put(&mut writer, 1, &Value::Str("hello!")).expect("write");
|
||||
s.put(&mut writer, 2, &Value::Str("hello!")).expect("write");
|
||||
s.put(&mut writer, 3, &Value::Str("hello!")).expect("write");
|
||||
writer.commit().expect("committed");
|
||||
}
|
||||
|
||||
{
|
||||
let mut writer = k.write().expect("writer");
|
||||
s.clear(&mut writer).expect("cleared");
|
||||
writer.commit().expect("committed");
|
||||
|
||||
let reader = k.read().expect("reader");
|
||||
assert_eq!(s.get(&reader, 1).expect("read"), None);
|
||||
assert_eq!(s.get(&reader, 2).expect("read"), None);
|
||||
assert_eq!(s.get(&reader, 3).expect("read"), None);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
// Copyright 2018 Mozilla
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
// this file except in compliance with the License. You may obtain a copy of the
|
||||
// License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
use lmdb::{
|
||||
Database,
|
||||
WriteFlags,
|
||||
};
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::error::StoreError;
|
||||
|
||||
use crate::readwrite::{
|
||||
Readable,
|
||||
Writer,
|
||||
};
|
||||
|
||||
use crate::value::Value;
|
||||
|
||||
use crate::store::multi::{
|
||||
Iter,
|
||||
MultiStore,
|
||||
};
|
||||
|
||||
use crate::store::integer::{
|
||||
Key,
|
||||
PrimitiveInt,
|
||||
};
|
||||
|
||||
pub struct MultiIntegerStore<K>
|
||||
where
|
||||
K: PrimitiveInt,
|
||||
{
|
||||
inner: MultiStore,
|
||||
phantom: PhantomData<K>,
|
||||
}
|
||||
|
||||
impl<K> MultiIntegerStore<K>
|
||||
where
|
||||
K: PrimitiveInt,
|
||||
{
|
||||
pub(crate) fn new(db: Database) -> MultiIntegerStore<K> {
|
||||
MultiIntegerStore {
|
||||
inner: MultiStore::new(db),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get<'env, T: Readable>(&self, reader: &'env T, k: K) -> Result<Iter<'env>, StoreError> {
|
||||
self.inner.get(reader, Key::new(&k)?)
|
||||
}
|
||||
|
||||
pub fn get_first<'env, T: Readable>(&self, reader: &'env T, k: K) -> Result<Option<Value<'env>>, StoreError> {
|
||||
self.inner.get_first(reader, Key::new(&k)?)
|
||||
}
|
||||
|
||||
pub fn put(&self, writer: &mut Writer, k: K, v: &Value) -> Result<(), StoreError> {
|
||||
self.inner.put(writer, Key::new(&k)?, v)
|
||||
}
|
||||
|
||||
pub fn put_with_flags(&self, writer: &mut Writer, k: K, v: &Value, flags: WriteFlags) -> Result<(), StoreError> {
|
||||
self.inner.put_with_flags(writer, Key::new(&k)?, v, flags)
|
||||
}
|
||||
|
||||
pub fn delete_all(&self, writer: &mut Writer, k: K) -> Result<(), StoreError> {
|
||||
self.inner.delete_all(writer, Key::new(&k)?)
|
||||
}
|
||||
|
||||
pub fn delete(&self, writer: &mut Writer, k: K, v: &Value) -> Result<(), StoreError> {
|
||||
self.inner.delete(writer, Key::new(&k)?, v)
|
||||
}
|
||||
|
||||
pub fn clear(&self, writer: &mut Writer) -> Result<(), StoreError> {
|
||||
self.inner.clear(writer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
extern crate tempfile;
|
||||
|
||||
use self::tempfile::Builder;
|
||||
use std::fs;
|
||||
|
||||
use super::*;
|
||||
use crate::*;
|
||||
|
||||
#[test]
|
||||
fn test_integer_keys() {
|
||||
let root = Builder::new().prefix("test_integer_keys").tempdir().expect("tempdir");
|
||||
fs::create_dir_all(root.path()).expect("dir created");
|
||||
let k = Rkv::new(root.path()).expect("new succeeded");
|
||||
let s = k.open_multi_integer("s", StoreOptions::create()).expect("open");
|
||||
|
||||
macro_rules! test_integer_keys {
|
||||
($type:ty, $key:expr) => {{
|
||||
let mut writer = k.write().expect("writer");
|
||||
|
||||
s.put(&mut writer, $key, &Value::Str("hello!")).expect("write");
|
||||
assert_eq!(s.get_first(&writer, $key).expect("read"), Some(Value::Str("hello!")));
|
||||
writer.commit().expect("committed");
|
||||
|
||||
let reader = k.read().expect("reader");
|
||||
assert_eq!(s.get_first(&reader, $key).expect("read"), Some(Value::Str("hello!")));
|
||||
}};
|
||||
}
|
||||
|
||||
test_integer_keys!(u32, std::u32::MIN);
|
||||
test_integer_keys!(u32, std::u32::MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clear() {
|
||||
let root = Builder::new().prefix("test_multi_integer_clear").tempdir().expect("tempdir");
|
||||
fs::create_dir_all(root.path()).expect("dir created");
|
||||
let k = Rkv::new(root.path()).expect("new succeeded");
|
||||
let s = k.open_multi_integer("s", StoreOptions::create()).expect("open");
|
||||
|
||||
{
|
||||
let mut writer = k.write().expect("writer");
|
||||
s.put(&mut writer, 1, &Value::Str("hello!")).expect("write");
|
||||
s.put(&mut writer, 1, &Value::Str("hello1!")).expect("write");
|
||||
s.put(&mut writer, 2, &Value::Str("hello!")).expect("write");
|
||||
writer.commit().expect("committed");
|
||||
}
|
||||
|
||||
{
|
||||
let mut writer = k.write().expect("writer");
|
||||
s.clear(&mut writer).expect("cleared");
|
||||
writer.commit().expect("committed");
|
||||
|
||||
let reader = k.read().expect("reader");
|
||||
assert_eq!(s.get_first(&reader, 1).expect("read"), None);
|
||||
assert_eq!(s.get_first(&reader, 2).expect("read"), None);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,151 +0,0 @@
|
|||
// Copyright 2018 Mozilla
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
// this file except in compliance with the License. You may obtain a copy of the
|
||||
// License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
use crate::{
|
||||
error::StoreError,
|
||||
read_transform,
|
||||
readwrite::{
|
||||
Readable,
|
||||
Writer,
|
||||
},
|
||||
value::Value,
|
||||
};
|
||||
use lmdb::{
|
||||
Cursor,
|
||||
Database,
|
||||
Iter as LmdbIter,
|
||||
// IterDup as LmdbIterDup,
|
||||
RoCursor,
|
||||
WriteFlags,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct MultiStore {
|
||||
db: Database,
|
||||
}
|
||||
|
||||
pub struct Iter<'env> {
|
||||
iter: LmdbIter<'env>,
|
||||
cursor: RoCursor<'env>,
|
||||
}
|
||||
|
||||
impl MultiStore {
|
||||
pub(crate) fn new(db: Database) -> MultiStore {
|
||||
MultiStore {
|
||||
db,
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides a cursor to all of the values for the duplicate entries that match this key
|
||||
pub fn get<T: Readable, K: AsRef<[u8]>>(self, reader: &T, k: K) -> Result<Iter, StoreError> {
|
||||
let mut cursor = reader.open_ro_cursor(self.db)?;
|
||||
let iter = cursor.iter_dup_of(k);
|
||||
Ok(Iter {
|
||||
iter,
|
||||
cursor,
|
||||
})
|
||||
}
|
||||
|
||||
/// Provides the first value that matches this key
|
||||
pub fn get_first<T: Readable, K: AsRef<[u8]>>(self, reader: &T, k: K) -> Result<Option<Value>, StoreError> {
|
||||
reader.get(self.db, &k)
|
||||
}
|
||||
|
||||
/// Insert a value at the specified key.
|
||||
/// This put will allow duplicate entries. If you wish to have duplicate entries
|
||||
/// rejected, use the `put_with_flags` function and specify NO_DUP_DATA
|
||||
pub fn put<K: AsRef<[u8]>>(self, writer: &mut Writer, k: K, v: &Value) -> Result<(), StoreError> {
|
||||
writer.put(self.db, &k, v, WriteFlags::empty())
|
||||
}
|
||||
|
||||
pub fn put_with_flags<K: AsRef<[u8]>>(
|
||||
self,
|
||||
writer: &mut Writer,
|
||||
k: K,
|
||||
v: &Value,
|
||||
flags: WriteFlags,
|
||||
) -> Result<(), StoreError> {
|
||||
writer.put(self.db, &k, v, flags)
|
||||
}
|
||||
|
||||
pub fn delete_all<K: AsRef<[u8]>>(self, writer: &mut Writer, k: K) -> Result<(), StoreError> {
|
||||
writer.delete(self.db, &k, None)
|
||||
}
|
||||
|
||||
pub fn delete<K: AsRef<[u8]>>(self, writer: &mut Writer, k: K, v: &Value) -> Result<(), StoreError> {
|
||||
writer.delete(self.db, &k, Some(&v.to_bytes()?))
|
||||
}
|
||||
|
||||
/* TODO - Figure out how to solve the need to have the cursor stick around when
|
||||
* we are producing iterators from MultiIter
|
||||
/// Provides an iterator starting at the lexographically smallest value in the store
|
||||
pub fn iter_start(&self, store: MultiStore) -> Result<MultiIter, StoreError> {
|
||||
let mut cursor = self.tx.open_ro_cursor(store.0).map_err(StoreError::LmdbError)?;
|
||||
|
||||
// We call Cursor.iter() instead of Cursor.iter_start() because
|
||||
// the latter panics at "called `Result::unwrap()` on an `Err` value:
|
||||
// NotFound" when there are no items in the store, whereas the former
|
||||
// returns an iterator that yields no items.
|
||||
//
|
||||
// And since we create the Cursor and don't change its position, we can
|
||||
// be sure that a call to Cursor.iter() will start at the beginning.
|
||||
//
|
||||
let iter = cursor.iter_dup();
|
||||
|
||||
Ok(MultiIter {
|
||||
iter,
|
||||
cursor,
|
||||
})
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn clear(self, writer: &mut Writer) -> Result<(), StoreError> {
|
||||
writer.clear(self.db)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fn read_transform_owned(val: Result<&[u8], lmdb::Error>) -> Result<Option<OwnedValue>, StoreError> {
|
||||
match val {
|
||||
Ok(bytes) => Value::from_tagged_slice(bytes).map(|v| Some(OwnedValue::from(&v))).map_err(StoreError::DataError),
|
||||
Err(lmdb::Error::NotFound) => Ok(None),
|
||||
Err(e) => Err(StoreError::LmdbError(e)),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'env> Iterator for MultiIter<'env> {
|
||||
type Item = Iter<'env>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.iter.next() {
|
||||
None => None,
|
||||
Some(iter) => Some(Iter {
|
||||
iter,
|
||||
cursor,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
impl<'env> Iterator for Iter<'env> {
|
||||
type Item = Result<(&'env [u8], Option<Value<'env>>), StoreError>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.iter.next() {
|
||||
None => None,
|
||||
Some(Ok((key, bytes))) => match read_transform(Ok(bytes)) {
|
||||
Ok(val) => Some(Ok((key, val))),
|
||||
Err(err) => Some(Err(err)),
|
||||
},
|
||||
Some(Err(err)) => Some(Err(StoreError::LmdbError(err))),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
// Copyright 2018 Mozilla
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
// this file except in compliance with the License. You may obtain a copy of the
|
||||
// License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
use crate::{
|
||||
error::StoreError,
|
||||
read_transform,
|
||||
readwrite::{
|
||||
Readable,
|
||||
Writer,
|
||||
},
|
||||
value::Value,
|
||||
};
|
||||
use lmdb::{
|
||||
Cursor,
|
||||
Database,
|
||||
Iter as LmdbIter,
|
||||
RoCursor,
|
||||
WriteFlags,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SingleStore {
|
||||
db: Database,
|
||||
}
|
||||
|
||||
pub struct Iter<'env> {
|
||||
iter: LmdbIter<'env>,
|
||||
cursor: RoCursor<'env>,
|
||||
}
|
||||
|
||||
impl SingleStore {
|
||||
pub(crate) fn new(db: Database) -> SingleStore {
|
||||
SingleStore {
|
||||
db,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get<T: Readable, K: AsRef<[u8]>>(self, reader: &T, k: K) -> Result<Option<Value>, StoreError> {
|
||||
reader.get(self.db, &k)
|
||||
}
|
||||
|
||||
// TODO: flags
|
||||
pub fn put<K: AsRef<[u8]>>(self, writer: &mut Writer, k: K, v: &Value) -> Result<(), StoreError> {
|
||||
writer.put(self.db, &k, v, WriteFlags::empty())
|
||||
}
|
||||
|
||||
pub fn delete<K: AsRef<[u8]>>(self, writer: &mut Writer, k: K) -> Result<(), StoreError> {
|
||||
writer.delete(self.db, &k, None)
|
||||
}
|
||||
|
||||
pub fn iter_start<T: Readable>(self, reader: &T) -> Result<Iter, StoreError> {
|
||||
let mut cursor = reader.open_ro_cursor(self.db)?;
|
||||
|
||||
// We call Cursor.iter() instead of Cursor.iter_start() because
|
||||
// the latter panics at "called `Result::unwrap()` on an `Err` value:
|
||||
// NotFound" when there are no items in the store, whereas the former
|
||||
// returns an iterator that yields no items.
|
||||
//
|
||||
// And since we create the Cursor and don't change its position, we can
|
||||
// be sure that a call to Cursor.iter() will start at the beginning.
|
||||
//
|
||||
let iter = cursor.iter();
|
||||
|
||||
Ok(Iter {
|
||||
iter,
|
||||
cursor,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn iter_from<T: Readable, K: AsRef<[u8]>>(self, reader: &T, k: K) -> Result<Iter, StoreError> {
|
||||
let mut cursor = reader.open_ro_cursor(self.db)?;
|
||||
let iter = cursor.iter_from(k);
|
||||
Ok(Iter {
|
||||
iter,
|
||||
cursor,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn clear(self, writer: &mut Writer) -> Result<(), StoreError> {
|
||||
writer.clear(self.db)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'env> Iterator for Iter<'env> {
|
||||
type Item = Result<(&'env [u8], Option<Value<'env>>), StoreError>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.iter.next() {
|
||||
None => None,
|
||||
Some(Ok((key, bytes))) => match read_transform(Ok(bytes)) {
|
||||
Ok(val) => Some(Ok((key, val))),
|
||||
Err(err) => Some(Err(err)),
|
||||
},
|
||||
Some(Err(err)) => Some(Err(StoreError::LmdbError(err))),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,265 +0,0 @@
|
|||
// Copyright 2018-2019 Mozilla
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
// this file except in compliance with the License. You may obtain a copy of the
|
||||
// License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
use arrayref::array_ref;
|
||||
use bincode::{
|
||||
deserialize,
|
||||
serialize,
|
||||
serialized_size,
|
||||
};
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
use uuid::{
|
||||
Bytes,
|
||||
Uuid,
|
||||
};
|
||||
|
||||
use crate::error::DataError;
|
||||
|
||||
/// We define a set of types, associated with simple integers, to annotate values
|
||||
/// stored in LMDB. This is to avoid an accidental 'cast' from a value of one type
|
||||
/// to another. For this reason we don't simply use `deserialize` from the `bincode`
|
||||
/// crate.
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Type {
|
||||
Bool = 1,
|
||||
U64 = 2,
|
||||
I64 = 3,
|
||||
F64 = 4,
|
||||
Instant = 5, // Millisecond-precision timestamp.
|
||||
Uuid = 6,
|
||||
Str = 7,
|
||||
Json = 8,
|
||||
Blob = 9,
|
||||
}
|
||||
|
||||
/// We use manual tagging, because <https://github.com/serde-rs/serde/issues/610>.
|
||||
impl Type {
|
||||
pub fn from_tag(tag: u8) -> Result<Type, DataError> {
|
||||
Type::from_primitive(tag).ok_or_else(|| DataError::UnknownType(tag))
|
||||
}
|
||||
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub fn to_tag(self) -> u8 {
|
||||
self as u8
|
||||
}
|
||||
|
||||
fn from_primitive(p: u8) -> Option<Type> {
|
||||
match p {
|
||||
1 => Some(Type::Bool),
|
||||
2 => Some(Type::U64),
|
||||
3 => Some(Type::I64),
|
||||
4 => Some(Type::F64),
|
||||
5 => Some(Type::Instant),
|
||||
6 => Some(Type::Uuid),
|
||||
7 => Some(Type::Str),
|
||||
8 => Some(Type::Json),
|
||||
9 => Some(Type::Blob),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for Type {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
|
||||
f.write_str(match *self {
|
||||
Type::Bool => "bool",
|
||||
Type::U64 => "u64",
|
||||
Type::I64 => "i64",
|
||||
Type::F64 => "f64",
|
||||
Type::Instant => "instant",
|
||||
Type::Uuid => "uuid",
|
||||
Type::Str => "str",
|
||||
Type::Json => "json",
|
||||
Type::Blob => "blob",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum Value<'s> {
|
||||
Bool(bool),
|
||||
U64(u64),
|
||||
I64(i64),
|
||||
F64(OrderedFloat<f64>),
|
||||
Instant(i64), // Millisecond-precision timestamp.
|
||||
Uuid(&'s Bytes),
|
||||
Str(&'s str),
|
||||
Json(&'s str),
|
||||
Blob(&'s [u8]),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum OwnedValue {
|
||||
Bool(bool),
|
||||
U64(u64),
|
||||
I64(i64),
|
||||
F64(f64),
|
||||
Instant(i64), // Millisecond-precision timestamp.
|
||||
Uuid(Uuid),
|
||||
Str(String),
|
||||
Json(String), // TODO
|
||||
Blob(Vec<u8>),
|
||||
}
|
||||
|
||||
fn uuid(bytes: &[u8]) -> Result<Value, DataError> {
|
||||
if bytes.len() == 16 {
|
||||
Ok(Value::Uuid(array_ref![bytes, 0, 16]))
|
||||
} else {
|
||||
Err(DataError::InvalidUuid)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> Value<'s> {
|
||||
fn expected_from_tagged_slice(expected: Type, slice: &'s [u8]) -> Result<Value<'s>, DataError> {
|
||||
let (tag, data) = slice.split_first().ok_or(DataError::Empty)?;
|
||||
let t = Type::from_tag(*tag)?;
|
||||
if t == expected {
|
||||
return Err(DataError::UnexpectedType {
|
||||
expected,
|
||||
actual: t,
|
||||
});
|
||||
}
|
||||
Value::from_type_and_data(t, data)
|
||||
}
|
||||
|
||||
pub fn from_tagged_slice(slice: &'s [u8]) -> Result<Value<'s>, DataError> {
|
||||
let (tag, data) = slice.split_first().ok_or(DataError::Empty)?;
|
||||
let t = Type::from_tag(*tag)?;
|
||||
Value::from_type_and_data(t, data)
|
||||
}
|
||||
|
||||
fn from_type_and_data(t: Type, data: &'s [u8]) -> Result<Value<'s>, DataError> {
|
||||
if t == Type::Uuid {
|
||||
return deserialize(data)
|
||||
.map_err(|e| DataError::DecodingError {
|
||||
value_type: t,
|
||||
err: e,
|
||||
})
|
||||
.map(uuid)?;
|
||||
}
|
||||
|
||||
match t {
|
||||
Type::Bool => deserialize(data).map(Value::Bool),
|
||||
Type::U64 => deserialize(data).map(Value::U64),
|
||||
Type::I64 => deserialize(data).map(Value::I64),
|
||||
Type::F64 => deserialize(data).map(OrderedFloat).map(Value::F64),
|
||||
Type::Instant => deserialize(data).map(Value::Instant),
|
||||
Type::Str => deserialize(data).map(Value::Str),
|
||||
Type::Json => deserialize(data).map(Value::Json),
|
||||
Type::Blob => deserialize(data).map(Value::Blob),
|
||||
Type::Uuid => {
|
||||
// Processed above to avoid verbose duplication of error transforms.
|
||||
unreachable!()
|
||||
},
|
||||
}
|
||||
.map_err(|e| DataError::DecodingError {
|
||||
value_type: t,
|
||||
err: e,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>, DataError> {
|
||||
match self {
|
||||
Value::Bool(v) => serialize(&(Type::Bool.to_tag(), *v)),
|
||||
Value::U64(v) => serialize(&(Type::U64.to_tag(), *v)),
|
||||
Value::I64(v) => serialize(&(Type::I64.to_tag(), *v)),
|
||||
Value::F64(v) => serialize(&(Type::F64.to_tag(), v.0)),
|
||||
Value::Instant(v) => serialize(&(Type::Instant.to_tag(), *v)),
|
||||
Value::Str(v) => serialize(&(Type::Str.to_tag(), v)),
|
||||
Value::Json(v) => serialize(&(Type::Json.to_tag(), v)),
|
||||
Value::Blob(v) => serialize(&(Type::Blob.to_tag(), v)),
|
||||
Value::Uuid(v) => serialize(&(Type::Uuid.to_tag(), v)),
|
||||
}
|
||||
.map_err(DataError::EncodingError)
|
||||
}
|
||||
|
||||
pub fn serialized_size(&self) -> Result<u64, DataError> {
|
||||
match self {
|
||||
Value::Bool(v) => serialized_size(&(Type::Bool.to_tag(), *v)),
|
||||
Value::U64(v) => serialized_size(&(Type::U64.to_tag(), *v)),
|
||||
Value::I64(v) => serialized_size(&(Type::I64.to_tag(), *v)),
|
||||
Value::F64(v) => serialized_size(&(Type::F64.to_tag(), v.0)),
|
||||
Value::Instant(v) => serialized_size(&(Type::Instant.to_tag(), *v)),
|
||||
Value::Str(v) => serialized_size(&(Type::Str.to_tag(), v)),
|
||||
Value::Json(v) => serialized_size(&(Type::Json.to_tag(), v)),
|
||||
Value::Blob(v) => serialized_size(&(Type::Blob.to_tag(), v)),
|
||||
Value::Uuid(v) => serialized_size(&(Type::Uuid.to_tag(), v)),
|
||||
}
|
||||
.map_err(DataError::EncodingError)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> From<&'s Value<'s>> for OwnedValue {
|
||||
fn from(value: &Value) -> OwnedValue {
|
||||
match value {
|
||||
Value::Bool(v) => OwnedValue::Bool(*v),
|
||||
Value::U64(v) => OwnedValue::U64(*v),
|
||||
Value::I64(v) => OwnedValue::I64(*v),
|
||||
Value::F64(v) => OwnedValue::F64(**v),
|
||||
Value::Instant(v) => OwnedValue::Instant(*v),
|
||||
Value::Uuid(v) => OwnedValue::Uuid(Uuid::from_bytes(**v)),
|
||||
Value::Str(v) => OwnedValue::Str(v.to_string()),
|
||||
Value::Json(v) => OwnedValue::Json(v.to_string()),
|
||||
Value::Blob(v) => OwnedValue::Blob(v.to_vec()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> From<&'s OwnedValue> for Value<'s> {
|
||||
fn from(value: &OwnedValue) -> Value {
|
||||
match value {
|
||||
OwnedValue::Bool(v) => Value::Bool(*v),
|
||||
OwnedValue::U64(v) => Value::U64(*v),
|
||||
OwnedValue::I64(v) => Value::I64(*v),
|
||||
OwnedValue::F64(v) => Value::F64(OrderedFloat::from(*v)),
|
||||
OwnedValue::Instant(v) => Value::Instant(*v),
|
||||
OwnedValue::Uuid(v) => Value::Uuid(v.as_bytes()),
|
||||
OwnedValue::Str(v) => Value::Str(v),
|
||||
OwnedValue::Json(v) => Value::Json(v),
|
||||
OwnedValue::Blob(v) => Value::Blob(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
#[test]
|
||||
fn test_value_serialized_size() {
|
||||
// | Value enum | tag: 1 byte | value_payload |
|
||||
// |----------------------------------------------------------|
|
||||
// | I64 | 1 | 8 |
|
||||
// | U64 | 1 | 8 |
|
||||
// | Bool | 1 | 1 |
|
||||
// | Instant | 1 | 8 |
|
||||
// | F64 | 1 | 8 |
|
||||
// | Uuid | 1 | 16 |
|
||||
// | Str/Blob/Json | 1 |(8: len + sizeof(payload))|
|
||||
assert_eq!(Value::I64(-1000).serialized_size().unwrap(), 9);
|
||||
assert_eq!(Value::U64(1000u64).serialized_size().unwrap(), 9);
|
||||
assert_eq!(Value::Bool(true).serialized_size().unwrap(), 2);
|
||||
assert_eq!(Value::Instant(1_558_020_865_224).serialized_size().unwrap(), 9);
|
||||
assert_eq!(Value::F64(OrderedFloat(10000.1)).serialized_size().unwrap(), 9);
|
||||
assert_eq!(Value::Str("hello!").serialized_size().unwrap(), 15);
|
||||
assert_eq!(Value::Str("¡Hola").serialized_size().unwrap(), 15);
|
||||
assert_eq!(Value::Blob(b"hello!").serialized_size().unwrap(), 15);
|
||||
assert_eq!(
|
||||
uuid(b"\x9f\xe2\xc4\xe9\x3f\x65\x4f\xdb\xb2\x4c\x02\xb1\x52\x59\x71\x6c")
|
||||
.unwrap()
|
||||
.serialized_size()
|
||||
.unwrap(),
|
||||
17
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
// Copyright 2018-2019 Mozilla
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
// this file except in compliance with the License. You may obtain a copy of the
|
||||
// License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
use rkv::{
|
||||
PrimitiveInt,
|
||||
Rkv,
|
||||
StoreOptions,
|
||||
Value,
|
||||
};
|
||||
use serde_derive::Serialize;
|
||||
use std::fs;
|
||||
use tempfile::Builder;
|
||||
|
||||
#[test]
|
||||
fn test_integer_keys() {
|
||||
let root = Builder::new().prefix("test_integer_keys").tempdir().expect("tempdir");
|
||||
fs::create_dir_all(root.path()).expect("dir created");
|
||||
let k = Rkv::new(root.path()).expect("new succeeded");
|
||||
let s = k.open_integer("s", StoreOptions::create()).expect("open");
|
||||
|
||||
macro_rules! test_integer_keys {
|
||||
($store:expr, $key:expr) => {{
|
||||
let mut writer = k.write().expect("writer");
|
||||
|
||||
$store.put(&mut writer, $key, &Value::Str("hello!")).expect("write");
|
||||
assert_eq!($store.get(&writer, $key).expect("read"), Some(Value::Str("hello!")));
|
||||
writer.commit().expect("committed");
|
||||
|
||||
let reader = k.read().expect("reader");
|
||||
assert_eq!($store.get(&reader, $key).expect("read"), Some(Value::Str("hello!")));
|
||||
}};
|
||||
}
|
||||
|
||||
// The integer module provides only the u32 integer key variant
|
||||
// of IntegerStore, so we can use it without further ado.
|
||||
test_integer_keys!(s, std::u32::MIN);
|
||||
test_integer_keys!(s, std::u32::MAX);
|
||||
|
||||
// If you want to use another integer key variant, you need to implement
|
||||
// a newtype, implement PrimitiveInt, and implement or derive Serialize
|
||||
// for it. Here we do so for the i32 type.
|
||||
|
||||
// DANGER! Doing this enables you to open a store with multiple,
|
||||
// different integer key types, which may result in unexpected behavior.
|
||||
// Make sure you know what you're doing!
|
||||
|
||||
let t = k.open_integer("s", StoreOptions::create()).expect("open");
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct I32(i32);
|
||||
impl PrimitiveInt for I32 {}
|
||||
test_integer_keys!(t, I32(std::i32::MIN));
|
||||
test_integer_keys!(t, I32(std::i32::MAX));
|
||||
|
||||
let u = k.open_integer("s", StoreOptions::create()).expect("open");
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct U16(u16);
|
||||
impl PrimitiveInt for U16 {}
|
||||
test_integer_keys!(u, U16(std::u16::MIN));
|
||||
test_integer_keys!(u, U16(std::u16::MAX));
|
||||
|
||||
let v = k.open_integer("s", StoreOptions::create()).expect("open");
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct U64(u64);
|
||||
impl PrimitiveInt for U64 {}
|
||||
test_integer_keys!(v, U64(std::u64::MIN));
|
||||
test_integer_keys!(v, U64(std::u64::MAX));
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
// Copyright 2018-2019 Mozilla
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
// this file except in compliance with the License. You may obtain a copy of the
|
||||
// License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
use rkv::{
|
||||
Manager,
|
||||
Rkv,
|
||||
};
|
||||
use std::{
|
||||
fs,
|
||||
sync::Arc,
|
||||
};
|
||||
use tempfile::Builder;
|
||||
|
||||
#[test]
|
||||
// Identical to the same-named unit test, but this one confirms that it works
|
||||
// via the public MANAGER singleton.
|
||||
fn test_same() {
|
||||
let root = Builder::new().prefix("test_same_singleton").tempdir().expect("tempdir");
|
||||
fs::create_dir_all(root.path()).expect("dir created");
|
||||
|
||||
let p = root.path();
|
||||
assert!(Manager::singleton().read().unwrap().get(p).expect("success").is_none());
|
||||
|
||||
let created_arc = Manager::singleton().write().unwrap().get_or_create(p, Rkv::new).expect("created");
|
||||
let fetched_arc = Manager::singleton().read().unwrap().get(p).expect("success").expect("existed");
|
||||
assert!(Arc::ptr_eq(&created_arc, &fetched_arc));
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
// Copyright 2018 Mozilla
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
// this file except in compliance with the License. You may obtain a copy of the
|
||||
// License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
use rkv::{
|
||||
PrimitiveInt,
|
||||
Rkv,
|
||||
StoreOptions,
|
||||
Value,
|
||||
};
|
||||
use serde_derive::Serialize;
|
||||
use std::fs;
|
||||
use tempfile::Builder;
|
||||
|
||||
#[test]
|
||||
fn test_multi_integer_keys() {
|
||||
let root = Builder::new().prefix("test_integer_keys").tempdir().expect("tempdir");
|
||||
fs::create_dir_all(root.path()).expect("dir created");
|
||||
let k = Rkv::new(root.path()).expect("new succeeded");
|
||||
let s = k.open_multi_integer("s", StoreOptions::create()).expect("open");
|
||||
|
||||
macro_rules! test_integer_keys {
|
||||
($store:expr, $key:expr) => {{
|
||||
let mut writer = k.write().expect("writer");
|
||||
|
||||
$store.put(&mut writer, $key, &Value::Str("hello1")).expect("write");
|
||||
$store.put(&mut writer, $key, &Value::Str("hello2")).expect("write");
|
||||
$store.put(&mut writer, $key, &Value::Str("hello3")).expect("write");
|
||||
let vals = $store
|
||||
.get(&writer, $key)
|
||||
.expect("read")
|
||||
.map(|result| result.expect("ok"))
|
||||
.map(|(_, v)| v.expect("multi read"))
|
||||
.collect::<Vec<Value>>();
|
||||
assert_eq!(vals, vec![Value::Str("hello1"), Value::Str("hello2"), Value::Str("hello3")]);
|
||||
writer.commit().expect("committed");
|
||||
|
||||
let reader = k.read().expect("reader");
|
||||
let vals = $store
|
||||
.get(&reader, $key)
|
||||
.expect("read")
|
||||
.map(|result| result.expect("ok"))
|
||||
.map(|(_, v)| v.expect("multi read"))
|
||||
.collect::<Vec<Value>>();
|
||||
assert_eq!(vals, vec![Value::Str("hello1"), Value::Str("hello2"), Value::Str("hello3")]);
|
||||
}};
|
||||
}
|
||||
|
||||
// The integer module provides only the u32 integer key variant
|
||||
// of IntegerStore, so we can use it without further ado.
|
||||
test_integer_keys!(s, std::u32::MIN);
|
||||
test_integer_keys!(s, std::u32::MAX);
|
||||
|
||||
// If you want to use another integer key variant, you need to implement
|
||||
// a newtype, implement PrimitiveInt, and implement or derive Serialize
|
||||
// for it. Here we do so for the i32 type.
|
||||
|
||||
// DANGER! Doing this enables you to open a store with multiple,
|
||||
// different integer key types, which may result in unexpected behavior.
|
||||
// Make sure you know what you're doing!
|
||||
|
||||
let t = k.open_multi_integer("s", StoreOptions::create()).expect("open");
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct I32(i32);
|
||||
impl PrimitiveInt for I32 {}
|
||||
test_integer_keys!(t, I32(std::i32::MIN));
|
||||
test_integer_keys!(t, I32(std::i32::MAX));
|
||||
|
||||
let u = k.open_multi_integer("s", StoreOptions::create()).expect("open");
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct U16(u16);
|
||||
impl PrimitiveInt for U16 {}
|
||||
test_integer_keys!(u, U16(std::u16::MIN));
|
||||
test_integer_keys!(u, U16(std::u16::MAX));
|
||||
|
||||
let v = k.open_multi_integer("s", StoreOptions::create()).expect("open");
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct U64(u64);
|
||||
impl PrimitiveInt for U64 {}
|
||||
test_integer_keys!(v, U64(std::u64::MIN));
|
||||
test_integer_keys!(v, U64(std::u64::MAX));
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
/// consider a struct like this
|
||||
/// struct Sample {
|
||||
/// id: u64,
|
||||
/// value: String,
|
||||
/// date: String,
|
||||
/// }
|
||||
/// We would like to index all of the fields so that we can search for the struct not only by ID
|
||||
/// but also by value and date. When we index the fields individually in their own tables, it
|
||||
/// is important that we run all operations within a single transaction to ensure coherence of
|
||||
/// the indices
|
||||
/// This test features helper functions for reading and writing the parts of the struct.
|
||||
/// Note that the reader functions take `Readable` because they might run within a Read
|
||||
/// Transaction or a Write Transaction. The test demonstrates fetching values via both.
|
||||
use rkv::{
|
||||
MultiStore,
|
||||
Readable,
|
||||
Rkv,
|
||||
SingleStore,
|
||||
StoreOptions,
|
||||
Value,
|
||||
Writer,
|
||||
};
|
||||
|
||||
use tempfile::Builder;
|
||||
|
||||
use std::fs;
|
||||
|
||||
#[test]
|
||||
fn read_many() {
|
||||
let root = Builder::new().prefix("test_txns").tempdir().expect("tempdir");
|
||||
fs::create_dir_all(root.path()).expect("dir created");
|
||||
let k = Rkv::new(root.path()).expect("new succeeded");
|
||||
let samplestore = k.open_single("s", StoreOptions::create()).expect("open");
|
||||
let datestore = k.open_multi("m", StoreOptions::create()).expect("open");
|
||||
let valuestore = k.open_multi("m", StoreOptions::create()).expect("open");
|
||||
|
||||
{
|
||||
let mut writer = k.write().expect("env write lock");
|
||||
|
||||
for id in 0..30_u64 {
|
||||
let value = format!("value{}", id);
|
||||
let date = format!("2019-06-{}", id);
|
||||
put_id_field(&mut writer, datestore, &date, id);
|
||||
put_id_field(&mut writer, valuestore, &value, id);
|
||||
put_sample(&mut writer, samplestore, id, &value);
|
||||
}
|
||||
|
||||
// now we read in the same transaction
|
||||
for id in 0..30_u64 {
|
||||
let value = format!("value{}", id);
|
||||
let date = format!("2019-06-{}", id);
|
||||
let ids = get_ids_by_field(&writer, datestore, &date);
|
||||
let ids2 = get_ids_by_field(&writer, valuestore, &value);
|
||||
let samples = get_samples(&writer, samplestore, &ids);
|
||||
let samples2 = get_samples(&writer, samplestore, &ids2);
|
||||
println!("{:?}, {:?}", samples, samples2);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let reader = k.read().expect("env read lock");
|
||||
for id in 0..30_u64 {
|
||||
let value = format!("value{}", id);
|
||||
let date = format!("2019-06-{}", id);
|
||||
let ids = get_ids_by_field(&reader, datestore, &date);
|
||||
let ids2 = get_ids_by_field(&reader, valuestore, &value);
|
||||
let samples = get_samples(&reader, samplestore, &ids);
|
||||
let samples2 = get_samples(&reader, samplestore, &ids2);
|
||||
println!("{:?}, {:?}", samples, samples2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_ids_by_field<Txn: Readable>(txn: &Txn, store: MultiStore, field: &str) -> Vec<u64> {
|
||||
store
|
||||
.get(txn, field)
|
||||
.expect("get iterator")
|
||||
.map(|id| match id.expect("field") {
|
||||
(_, Some(Value::U64(id))) => id,
|
||||
_ => panic!("getting value in iter"),
|
||||
})
|
||||
.collect::<Vec<u64>>()
|
||||
}
|
||||
|
||||
fn get_samples<Txn: Readable>(txn: &Txn, samplestore: SingleStore, ids: &[u64]) -> Vec<String> {
|
||||
ids.iter()
|
||||
.map(|id| {
|
||||
let bytes = id.to_be_bytes();
|
||||
match samplestore.get(txn, &bytes).expect("fetch sample") {
|
||||
Some(Value::Str(sample)) => String::from(sample),
|
||||
Some(_) => panic!("wrong type"),
|
||||
None => panic!("no sample for this id!"),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
}
|
||||
|
||||
fn put_sample(txn: &mut Writer, samplestore: SingleStore, id: u64, value: &str) {
|
||||
let idbytes = id.to_be_bytes();
|
||||
samplestore.put(txn, &idbytes, &Value::Str(value)).expect("put id");
|
||||
}
|
||||
|
||||
fn put_id_field(txn: &mut Writer, store: MultiStore, field: &str, id: u64) {
|
||||
store.put(txn, field, &Value::U64(id)).expect("put id");
|
||||
}
|
|
@ -7,5 +7,5 @@ authors = ["fuzzing@mozilla.com"]
|
|||
libc = "0.2"
|
||||
tempfile = "3"
|
||||
lazy_static = "1.4.0"
|
||||
rkv = { version = "0.10", features = ["with-fuzzer-no-link"] }
|
||||
rkv = { version = "0.16", features = ["with-fuzzer-no-link"] }
|
||||
lmdb-rkv = { version = "0.14", features = ["with-fuzzer-no-link"] }
|
||||
|
|
|
@ -9,6 +9,14 @@ extern crate lmdb;
|
|||
extern crate rkv;
|
||||
extern crate tempfile;
|
||||
|
||||
use rkv::backend::{
|
||||
BackendEnvironmentBuilder,
|
||||
SafeMode,
|
||||
SafeModeDatabase,
|
||||
SafeModeEnvironment,
|
||||
SafeModeRoTransaction,
|
||||
SafeModeRwTransaction,
|
||||
};
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
@ -18,6 +26,9 @@ use std::sync::Arc;
|
|||
use std::thread;
|
||||
use tempfile::Builder;
|
||||
|
||||
type Rkv = rkv::Rkv<SafeModeEnvironment>;
|
||||
type SingleStore = rkv::SingleStore<SafeModeDatabase>;
|
||||
|
||||
fn eat_lmdb_err<T>(value: Result<T, rkv::StoreError>) -> Result<Option<T>, rkv::StoreError> {
|
||||
match value {
|
||||
Ok(value) => Ok(Some(value)),
|
||||
|
@ -49,8 +60,7 @@ pub extern "C" fn fuzz_rkv_db_file(raw_data: *const u8, size: libc::size_t) -> l
|
|||
let mut db_file = File::create("data.mdb").unwrap();
|
||||
db_file.write_all(db).unwrap();
|
||||
|
||||
let &mut builder = rkv::Rkv::environment_builder().set_max_dbs(2);
|
||||
let env = rkv::Rkv::from_env(Path::new("."), builder).unwrap();
|
||||
let env = Rkv::with_capacity::<SafeMode>(Path::new("."), 2).unwrap();
|
||||
let store = env
|
||||
.open_single("test", rkv::StoreOptions::create())
|
||||
.unwrap();
|
||||
|
@ -68,7 +78,7 @@ pub extern "C" fn fuzz_rkv_db_name(raw_data: *const u8, size: libc::size_t) -> l
|
|||
let root = Builder::new().prefix("fuzz_rkv_db_name").tempdir().unwrap();
|
||||
fs::create_dir_all(root.path()).unwrap();
|
||||
|
||||
let env = rkv::Rkv::new(root.path()).unwrap();
|
||||
let env = Rkv::new::<SafeMode>(root.path()).unwrap();
|
||||
let name = String::from_utf8_lossy(data);
|
||||
println!("Checking string: '{:?}'", name);
|
||||
// Some strings are invalid database names, and are handled as store errors.
|
||||
|
@ -93,7 +103,7 @@ pub extern "C" fn fuzz_rkv_key_write(raw_data: *const u8, size: libc::size_t) ->
|
|||
.unwrap();
|
||||
fs::create_dir_all(root.path()).unwrap();
|
||||
|
||||
let env = rkv::Rkv::new(root.path()).unwrap();
|
||||
let env = Rkv::new::<SafeMode>(root.path()).unwrap();
|
||||
let store = env
|
||||
.open_single("test", rkv::StoreOptions::create())
|
||||
.unwrap();
|
||||
|
@ -116,7 +126,7 @@ pub extern "C" fn fuzz_rkv_val_write(raw_data: *const u8, size: libc::size_t) ->
|
|||
.unwrap();
|
||||
fs::create_dir_all(root.path()).unwrap();
|
||||
|
||||
let env = rkv::Rkv::new(root.path()).unwrap();
|
||||
let env = Rkv::new::<SafeMode>(root.path()).unwrap();
|
||||
let store = env
|
||||
.open_single("test", rkv::StoreOptions::create())
|
||||
.unwrap();
|
||||
|
@ -157,7 +167,7 @@ pub extern "C" fn fuzz_rkv_calls(raw_data: *const u8, size: libc::size_t) -> lib
|
|||
|
||||
fn maybe_abort<I: Iterator<Item = u8>>(
|
||||
fuzz: &mut I,
|
||||
read: rkv::Reader,
|
||||
read: rkv::Reader<SafeModeRoTransaction>,
|
||||
) -> Result<(), rkv::StoreError> {
|
||||
match fuzz.next().map(|byte| byte % 2) {
|
||||
Some(0) => Ok(read.abort()),
|
||||
|
@ -167,7 +177,7 @@ pub extern "C" fn fuzz_rkv_calls(raw_data: *const u8, size: libc::size_t) -> lib
|
|||
|
||||
fn maybe_commit<I: Iterator<Item = u8>>(
|
||||
fuzz: &mut I,
|
||||
write: rkv::Writer,
|
||||
write: rkv::Writer<SafeModeRwTransaction>,
|
||||
) -> Result<(), rkv::StoreError> {
|
||||
match fuzz.next().map(|byte| byte % 3) {
|
||||
Some(0) => write.commit(),
|
||||
|
@ -207,8 +217,8 @@ pub extern "C" fn fuzz_rkv_calls(raw_data: *const u8, size: libc::size_t) -> lib
|
|||
|
||||
fn store_put<I: Iterator<Item = u8> + Clone>(
|
||||
fuzz: &mut I,
|
||||
env: &rkv::Rkv,
|
||||
store: &rkv::SingleStore,
|
||||
env: &Rkv,
|
||||
store: &SingleStore,
|
||||
) {
|
||||
let key = match get_any_data(fuzz, 1024) {
|
||||
Some(key) => key,
|
||||
|
@ -239,8 +249,8 @@ pub extern "C" fn fuzz_rkv_calls(raw_data: *const u8, size: libc::size_t) -> lib
|
|||
|
||||
fn store_get<I: Iterator<Item = u8> + Clone>(
|
||||
fuzz: &mut I,
|
||||
env: &rkv::Rkv,
|
||||
store: &rkv::SingleStore,
|
||||
env: &Rkv,
|
||||
store: &SingleStore,
|
||||
) {
|
||||
let key = match get_any_data(fuzz, 1024) {
|
||||
Some(key) => key,
|
||||
|
@ -264,8 +274,8 @@ pub extern "C" fn fuzz_rkv_calls(raw_data: *const u8, size: libc::size_t) -> lib
|
|||
|
||||
fn store_delete<I: Iterator<Item = u8> + Clone>(
|
||||
fuzz: &mut I,
|
||||
env: &rkv::Rkv,
|
||||
store: &rkv::SingleStore,
|
||||
env: &Rkv,
|
||||
store: &SingleStore,
|
||||
) {
|
||||
let key = match get_any_data(fuzz, 1024) {
|
||||
Some(key) => key,
|
||||
|
@ -284,7 +294,7 @@ pub extern "C" fn fuzz_rkv_calls(raw_data: *const u8, size: libc::size_t) -> lib
|
|||
maybe_commit(fuzz, writer).unwrap();
|
||||
}
|
||||
|
||||
fn store_resize<I: Iterator<Item = u8>>(fuzz: &mut I, env: &rkv::Rkv) {
|
||||
fn store_resize<I: Iterator<Item = u8>>(fuzz: &mut I, env: &Rkv) {
|
||||
let n = fuzz.next().unwrap_or(1) as usize;
|
||||
env.set_map_size(1_048_576 * (n % 100)).unwrap() // 1,048,576 bytes, i.e. 1MiB.
|
||||
};
|
||||
|
@ -292,7 +302,7 @@ pub extern "C" fn fuzz_rkv_calls(raw_data: *const u8, size: libc::size_t) -> lib
|
|||
let root = Builder::new().prefix("fuzz_rkv_calls").tempdir().unwrap();
|
||||
fs::create_dir_all(root.path()).unwrap();
|
||||
|
||||
let mut builder = rkv::Rkv::environment_builder();
|
||||
let mut builder: SafeMode = Rkv::environment_builder();
|
||||
builder.set_max_dbs(1); // need at least one db
|
||||
|
||||
maybe_do(&mut fuzz, |fuzz| {
|
||||
|
@ -308,7 +318,7 @@ pub extern "C" fn fuzz_rkv_calls(raw_data: *const u8, size: libc::size_t) -> lib
|
|||
builder.set_map_size(1_048_576 * (n % 100)); // 1,048,576 bytes, i.e. 1MiB.
|
||||
});
|
||||
|
||||
let env = rkv::Rkv::from_env(root.path(), builder).unwrap();
|
||||
let env = Rkv::from_builder(root.path(), builder).unwrap();
|
||||
let store = env
|
||||
.open_single("test", rkv::StoreOptions::create())
|
||||
.unwrap();
|
||||
|
|
Загрузка…
Ссылка в новой задаче