nolo-cli 0.143.0-alpha.1 → 0.145.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/{_executeModel-QK43QR2R.js → _executeModel-M3FASQ2Z.js} +18 -17
- package/{agentDeleteCommand-SE4WUIRR.js → agentDeleteCommand-K7GCEEVS.js} +10 -9
- package/{agentEmailCommands-LW5JMYH2.js → agentEmailCommands-MNXRO36L.js} +24 -23
- package/{agentListCommands-LGZG2OQR.js → agentListCommands-KMZR6VVM.js} +12 -11
- package/{agentMachineCommands-BNOXYSPZ.js → agentMachineCommands-75TIBYDY.js} +10 -9
- package/{agentProviderCommands-AOG3OZJA.js → agentProviderCommands-TWB6E5WD.js} +1 -1
- package/{agentRecordCommands-W5ZXPHZV.js → agentRecordCommands-RDO5L3N4.js} +23 -22
- package/{agentRunCommand-VRLCL57M.js → agentRunCommand-FVJJPL27.js} +23 -22
- package/{agentRuntimeLocal-VOQ3UYZB.js → agentRuntimeLocal-DAK7RCER.js} +8 -7
- package/{agentSlice-4AK24F5F.js → agentSlice-6MRJR2BJ.js} +1 -1
- package/{appDeleteCommands-XWAIUHTN.js → appDeleteCommands-JMMXK3XT.js} +11 -10
- package/{appDeployCommands-CBJJCUMG.js → appDeployCommands-JFU6GALI.js} +11 -10
- package/{appDeployStatusCommands-LTTZTJQS.js → appDeployStatusCommands-AXTZ4GAN.js} +11 -10
- package/{appGetCommands-QHUB7MFF.js → appGetCommands-CR2W3DCM.js} +11 -10
- package/{appListCommands-HINIDJE7.js → appListCommands-2TV6P3EU.js} +11 -10
- package/{authCommand-LARLQ3GC.js → authCommand-UBJ4EWYZ.js} +6 -6
- package/{authCommands-GBIFZWD3.js → authCommands-H6WKKCRW.js} +1 -1
- package/{builtinSkillRegistry-AFPSTTAU.js → builtinSkillRegistry-ZMF6YGNO.js} +4 -3
- package/{chatQueueReduxAdapter-KIQ3FQ62.js → chatQueueReduxAdapter-CDOHVTSE.js} +2 -2
- package/{chromeCommands-NKU3OWKH.js → chromeCommands-2N6MEWRU.js} +94 -23
- package/{chromeOpsCommands-OQDJGD4Y.js → chromeOpsCommands-IXRLN57H.js} +4 -3
- package/{chunk-7MUSNQ6E.js → chunk-27ONFIEY.js} +20 -2
- package/{chunk-OZ6V53WV.js → chunk-2EE5UD3N.js} +1 -1
- package/{chunk-44QZPYLI.js → chunk-2SYTUTLI.js} +92 -20
- package/{chunk-DID5AVGT.js → chunk-2TKVOEQF.js} +11 -10
- package/chunk-5J3PYNUP.js +255 -0
- package/{chunk-OUOJE2P2.js → chunk-5XYFIDFU.js} +5 -5
- package/{chunk-WTUVMKSQ.js → chunk-62XHRZG4.js} +2 -2
- package/{chunk-WC6A2Q7S.js → chunk-6TPOEJMQ.js} +5 -2
- package/{chunk-RRMAZ2BI.js → chunk-7NANRWL4.js} +1 -1
- package/{chunk-44F6KKUE.js → chunk-7YAK46ML.js} +3 -3
- package/{chunk-NOGH7HCP.js → chunk-A6FNUMCF.js} +47 -6
- package/{chunk-OFYHCA6W.js → chunk-AATZZAZE.js} +1 -1
- package/{chunk-KUPDS66Q.js → chunk-ANX7Z7Z2.js} +8 -8
- package/{chunk-J5H6SUOW.js → chunk-B2XUQXQX.js} +2 -2
- package/{chunk-A5MB5RIT.js → chunk-CCOXTK3I.js} +1 -1
- package/chunk-GNMAFAIS.js +29 -0
- package/{chunk-GZQNKKDG.js → chunk-HKBFZONG.js} +1 -1
- package/{chunk-7LLWEG6F.js → chunk-I44MDY73.js} +3 -3
- package/{chunk-QDT5T7MK.js → chunk-I4OPU5TK.js} +1 -1
- package/{chunk-VPFEPRME.js → chunk-IVZJWTDI.js} +3 -0
- package/{chunk-CTGZO35O.js → chunk-JLQKV2ZJ.js} +2 -2
- package/{chunk-PQVOC2CZ.js → chunk-M2YBIBBS.js} +1 -1
- package/{chunk-ZOI6KA4N.js → chunk-M76SRQ3H.js} +34 -22
- package/{chunk-K25DK52L.js → chunk-OKSJZ4UU.js} +21 -223
- package/{chunk-U5QTKTZE.js → chunk-P6GMCCAT.js} +1 -1
- package/{chunk-L6RB42ZR.js → chunk-QFZM5BFI.js} +3 -3
- package/{chunk-GH4MH73K.js → chunk-RHDOQWXZ.js} +175 -17
- package/{chunk-M6NK74VP.js → chunk-S5XGT6GB.js} +1 -1
- package/{chunk-KUWT3C2P.js → chunk-T76D3OYX.js} +1 -1
- package/{chunk-MXRUAINC.js → chunk-UMB7CHAY.js} +3 -3
- package/{chunk-VVUSRF5D.js → chunk-VJ4FUWPC.js} +3 -3
- package/{chunk-T36EFAW7.js → chunk-VZVTPDPL.js} +3 -3
- package/{chunk-AAROOKLX.js → chunk-X2QRZXZH.js} +1 -1
- package/{chunk-WYYO2YOL.js → chunk-XNXNHAAP.js} +1 -1
- package/{chunk-O6OSWFZQ.js → chunk-XUWRURDF.js} +6 -0
- package/{chunk-DOBBRISY.js → chunk-Y6XHJ4JJ.js} +22 -10
- package/{chunk-W247GBSQ.js → chunk-YU2THNST.js} +1 -1
- package/{chunk-OY5IJI2N.js → chunk-YUNJN43V.js} +23 -3
- package/{commandRegistry-DETYXF5I.js → commandRegistry-ZF6YXQWV.js} +76 -65
- package/{contentThunks-6QBTPLWT.js → contentThunks-6Y6ATG3A.js} +3 -3
- package/{createAgentAutomationAction-TZVIJTZ7.js → createAgentAutomationAction-OQTLA2FC.js} +3 -3
- package/{createDialogAction-KTDAEA42.js → createDialogAction-IF5EZMBD.js} +6 -5
- package/{createPageAction-4RASVDAU.js → createPageAction-GU766R3L.js} +3 -3
- package/{dialogCommands-GY7LRERG.js → dialogCommands-QV7XAX2V.js} +11 -10
- package/{dialogSlice-J24APUGG.js → dialogSlice-CVHMDSVV.js} +2 -2
- package/{docCreateCommands-T7KII3WP.js → docCreateCommands-FSBANQTO.js} +10 -9
- package/{docDeleteCommands-SQ26EFE6.js → docDeleteCommands-4IR4YSGU.js} +11 -10
- package/{docReadCommands-I6NA2ZKV.js → docReadCommands-4EYPGUE3.js} +10 -9
- package/{docStore-SPOGE7K3.js → docStore-RQOWPNON.js} +1 -1
- package/{docUpdateCommands-IG43SZ6U.js → docUpdateCommands-RW2BYLSE.js} +10 -9
- package/{handleSendMessageAction-I2IN64DS.js → handleSendMessageAction-UDTPG5WK.js} +4 -3
- package/index.js +4 -4
- package/{machineCommands-U7VABJPW.js → machineCommands-WBQ4TCPD.js} +22 -21
- package/{machineWsRunDispatch-ZNU6LRNG.js → machineWsRunDispatch-7DNDXUOS.js} +22 -21
- package/node_modules/abstract-level/CHANGELOG.md +150 -0
- package/node_modules/abstract-level/LICENSE +21 -0
- package/node_modules/abstract-level/README.md +1957 -0
- package/node_modules/abstract-level/UPGRADING.md +669 -0
- package/node_modules/abstract-level/abstract-chained-batch.js +397 -0
- package/node_modules/abstract-level/abstract-iterator.js +404 -0
- package/node_modules/abstract-level/abstract-level.js +1127 -0
- package/node_modules/abstract-level/abstract-snapshot.js +84 -0
- package/node_modules/abstract-level/index.d.ts +52 -0
- package/node_modules/abstract-level/index.js +9 -0
- package/node_modules/abstract-level/lib/abstract-sublevel-iterator.js +141 -0
- package/node_modules/abstract-level/lib/abstract-sublevel.js +290 -0
- package/node_modules/abstract-level/lib/common.js +33 -0
- package/node_modules/abstract-level/lib/default-chained-batch.js +29 -0
- package/node_modules/abstract-level/lib/default-kv-iterator.js +74 -0
- package/node_modules/abstract-level/lib/deferred-iterator.js +110 -0
- package/node_modules/abstract-level/lib/deferred-queue.js +84 -0
- package/node_modules/abstract-level/lib/errors.js +21 -0
- package/node_modules/abstract-level/lib/event-monitor.js +30 -0
- package/node_modules/abstract-level/lib/hooks.js +78 -0
- package/node_modules/abstract-level/lib/prefixes.js +21 -0
- package/node_modules/abstract-level/lib/prewrite-batch.js +96 -0
- package/node_modules/abstract-level/lib/range-options.js +26 -0
- package/node_modules/abstract-level/package.json +66 -0
- package/node_modules/abstract-level/test/async-iterator-test.js +132 -0
- package/node_modules/abstract-level/test/batch-test.js +214 -0
- package/node_modules/abstract-level/test/chained-batch-test.js +315 -0
- package/node_modules/abstract-level/test/clear-range-test.js +262 -0
- package/node_modules/abstract-level/test/clear-test.js +110 -0
- package/node_modules/abstract-level/test/common.js +90 -0
- package/node_modules/abstract-level/test/deferred-open-test.js +209 -0
- package/node_modules/abstract-level/test/del-test.js +65 -0
- package/node_modules/abstract-level/test/encoding-buffer-test.js +336 -0
- package/node_modules/abstract-level/test/encoding-custom-test.js +92 -0
- package/node_modules/abstract-level/test/encoding-decode-error-test.js +85 -0
- package/node_modules/abstract-level/test/encoding-json-test.js +75 -0
- package/node_modules/abstract-level/test/encoding-test.js +130 -0
- package/node_modules/abstract-level/test/events/write.js +132 -0
- package/node_modules/abstract-level/test/factory-test.js +36 -0
- package/node_modules/abstract-level/test/get-many-test.js +157 -0
- package/node_modules/abstract-level/test/get-sync-test.js +61 -0
- package/node_modules/abstract-level/test/get-test.js +83 -0
- package/node_modules/abstract-level/test/has-many-test.js +144 -0
- package/node_modules/abstract-level/test/has-test.js +81 -0
- package/node_modules/abstract-level/test/hooks/newsub.js +57 -0
- package/node_modules/abstract-level/test/hooks/postopen.js +229 -0
- package/node_modules/abstract-level/test/hooks/prewrite.js +816 -0
- package/node_modules/abstract-level/test/hooks/shared.js +38 -0
- package/node_modules/abstract-level/test/index.js +86 -0
- package/node_modules/abstract-level/test/iterator-explicit-snapshot-test.js +393 -0
- package/node_modules/abstract-level/test/iterator-no-snapshot-test.js +49 -0
- package/node_modules/abstract-level/test/iterator-range-test.js +277 -0
- package/node_modules/abstract-level/test/iterator-seek-test.js +335 -0
- package/node_modules/abstract-level/test/iterator-snapshot-test.js +56 -0
- package/node_modules/abstract-level/test/iterator-test.js +621 -0
- package/node_modules/abstract-level/test/manifest-test.js +21 -0
- package/node_modules/abstract-level/test/open-create-if-missing-test.js +39 -0
- package/node_modules/abstract-level/test/open-error-if-exists-test.js +25 -0
- package/node_modules/abstract-level/test/open-test.js +263 -0
- package/node_modules/abstract-level/test/put-get-del-test.js +97 -0
- package/node_modules/abstract-level/test/put-test.js +70 -0
- package/node_modules/abstract-level/test/self/abstract-iterator-test.js +182 -0
- package/node_modules/abstract-level/test/self/async-iterator-test.js +242 -0
- package/node_modules/abstract-level/test/self/attach-resource-test.js +74 -0
- package/node_modules/abstract-level/test/self/defer-test.js +140 -0
- package/node_modules/abstract-level/test/self/deferred-iterator-test.js +314 -0
- package/node_modules/abstract-level/test/self/deferred-operations-test.js +86 -0
- package/node_modules/abstract-level/test/self/deferred-queue-test.js +93 -0
- package/node_modules/abstract-level/test/self/encoding-test.js +391 -0
- package/node_modules/abstract-level/test/self/errors-test.js +11 -0
- package/node_modules/abstract-level/test/self/iterator-test.js +1050 -0
- package/node_modules/abstract-level/test/self/sublevel-test.js +1039 -0
- package/node_modules/abstract-level/test/self.js +905 -0
- package/node_modules/abstract-level/test/sublevel-test.js +209 -0
- package/node_modules/abstract-level/test/traits/closed.js +42 -0
- package/node_modules/abstract-level/test/traits/index.js +4 -0
- package/node_modules/abstract-level/test/traits/open.js +62 -0
- package/node_modules/abstract-level/test/util.js +275 -0
- package/node_modules/abstract-level/types/abstract-chained-batch.d.ts +124 -0
- package/node_modules/abstract-level/types/abstract-iterator.d.ts +241 -0
- package/node_modules/abstract-level/types/abstract-level.d.ts +636 -0
- package/node_modules/abstract-level/types/abstract-snapshot.d.ts +30 -0
- package/node_modules/abstract-level/types/abstract-sublevel.d.ts +76 -0
- package/node_modules/abstract-level/types/interfaces.d.ts +43 -0
- package/node_modules/base64-js/LICENSE +21 -0
- package/node_modules/base64-js/README.md +34 -0
- package/node_modules/base64-js/base64js.min.js +1 -0
- package/node_modules/base64-js/index.d.ts +3 -0
- package/node_modules/base64-js/index.js +150 -0
- package/node_modules/base64-js/package.json +47 -0
- package/node_modules/browser-level/CHANGELOG.md +44 -0
- package/node_modules/browser-level/LICENSE +21 -0
- package/node_modules/browser-level/README.md +124 -0
- package/node_modules/browser-level/UPGRADING.md +143 -0
- package/node_modules/browser-level/index.d.ts +108 -0
- package/node_modules/browser-level/index.js +323 -0
- package/node_modules/browser-level/iterator.js +239 -0
- package/node_modules/browser-level/package.json +48 -0
- package/node_modules/browser-level/util/clear.js +38 -0
- package/node_modules/browser-level/util/deserialize.js +17 -0
- package/node_modules/browser-level/util/key-range.js +20 -0
- package/node_modules/buffer/AUTHORS.md +73 -0
- package/node_modules/buffer/LICENSE +21 -0
- package/node_modules/buffer/README.md +410 -0
- package/node_modules/buffer/index.d.ts +194 -0
- package/node_modules/buffer/index.js +2106 -0
- package/node_modules/buffer/package.json +93 -0
- package/node_modules/classic-level/CHANGELOG.md +115 -0
- package/node_modules/classic-level/LICENSE +21 -0
- package/node_modules/classic-level/README.md +274 -0
- package/node_modules/classic-level/UPGRADING.md +131 -0
- package/node_modules/classic-level/binding.cc +2652 -0
- package/node_modules/classic-level/binding.gyp +71 -0
- package/node_modules/classic-level/binding.js +1 -0
- package/node_modules/classic-level/chained-batch.js +35 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/LICENSE +27 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/Makefile +424 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/build_detect_platform +256 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/autocompact_test.cc +118 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/builder.cc +88 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/builder.h +34 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/c.cc +595 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/c_test.c +390 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/corruption_test.cc +374 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_bench.cc +1020 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_impl.cc +1568 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_impl.h +212 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_iter.cc +317 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_iter.h +28 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/db_test.cc +2158 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/dbformat.cc +140 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/dbformat.h +230 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/dbformat_test.cc +112 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/dumpfile.cc +225 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/fault_injection_test.cc +554 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/filename.cc +144 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/filename.h +85 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/filename_test.cc +123 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/leveldbutil.cc +64 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_format.h +35 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_reader.cc +284 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_reader.h +113 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_test.cc +591 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_writer.cc +112 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/log_writer.h +54 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/memtable.cc +145 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/memtable.h +89 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/recovery_test.cc +324 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/repair.cc +461 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/skiplist.h +384 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/skiplist_test.cc +378 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/snapshot.h +67 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/table_cache.cc +127 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/table_cache.h +61 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_edit.cc +266 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_edit.h +107 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_edit_test.cc +46 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_set.cc +1535 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_set.h +399 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/version_set_test.cc +179 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/write_batch.cc +147 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/write_batch_internal.h +50 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/db/write_batch_test.cc +120 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/helpers/memenv/memenv.cc +398 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/helpers/memenv/memenv.h +20 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/helpers/memenv/memenv_test.cc +241 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/c.h +290 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/cache.h +110 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/comparator.h +63 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/db.h +164 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/dumpfile.h +25 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/env.h +351 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/filter_policy.h +70 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/iterator.h +100 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/options.h +213 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/slice.h +109 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/status.h +112 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/table.h +85 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/table_builder.h +92 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/value_sink.h +26 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/include/leveldb/write_batch.h +64 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/issues/issue178_test.cc +92 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/issues/issue200_test.cc +59 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/atomic_pointer.h +242 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/port.h +21 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/port_example.h +141 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/port_posix.cc +53 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/port_posix.h +156 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/port_posix_sse.cc +129 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/thread_annotations.h +60 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/port/win/stdint.h +24 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/block.cc +268 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/block.h +44 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/block_builder.cc +109 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/block_builder.h +57 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/filter_block.cc +111 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/filter_block.h +68 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/filter_block_test.cc +128 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/format.cc +144 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/format.h +108 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/iterator.cc +67 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/iterator_wrapper.h +66 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/merger.cc +197 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/merger.h +26 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/table.cc +285 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/table_builder.cc +270 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/table_test.cc +876 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/two_level_iterator.cc +182 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/table/two_level_iterator.h +34 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/arena.cc +68 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/arena.h +68 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/arena_test.cc +68 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/bloom.cc +95 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/bloom_test.cc +162 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/cache.cc +405 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/cache_test.cc +226 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/coding.cc +194 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/coding.h +104 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/coding_test.cc +196 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/comparator.cc +81 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/crc32c.cc +350 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/crc32c.h +45 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/crc32c_test.cc +72 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/env.cc +100 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/env_posix.cc +695 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/env_posix_test.cc +66 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/env_posix_test_helper.h +28 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/env_test.cc +106 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/filter_policy.cc +11 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/hash.cc +52 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/hash.h +19 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/hash_test.cc +54 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/histogram.cc +139 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/histogram.h +42 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/logging.cc +72 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/logging.h +43 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/mutexlock.h +41 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/options.cc +30 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/posix_logger.h +98 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/random.h +64 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/status.cc +75 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/testharness.cc +77 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/testharness.h +138 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/testutil.cc +51 -0
- package/node_modules/classic-level/deps/leveldb/leveldb-1.20/util/testutil.h +63 -0
- package/node_modules/classic-level/deps/leveldb/leveldb.gyp +282 -0
- package/node_modules/classic-level/deps/leveldb/patches/001-value-sink.patch +164 -0
- package/node_modules/classic-level/deps/leveldb/port-libuv/atomic_pointer_win.h +48 -0
- package/node_modules/classic-level/deps/leveldb/port-libuv/env_win.cc +692 -0
- package/node_modules/classic-level/deps/leveldb/port-libuv/port_uv.cc +36 -0
- package/node_modules/classic-level/deps/leveldb/port-libuv/port_uv.h +157 -0
- package/node_modules/classic-level/deps/leveldb/port-libuv/port_uv.h.bak +153 -0
- package/node_modules/classic-level/deps/leveldb/port-libuv/stdint-msvc2008.h +247 -0
- package/node_modules/classic-level/deps/leveldb/port-libuv/uv_condvar.h +45 -0
- package/node_modules/classic-level/deps/leveldb/port-libuv/uv_condvar_posix.cc +139 -0
- package/node_modules/classic-level/deps/leveldb/port-libuv/win_logger.cc +79 -0
- package/node_modules/classic-level/deps/leveldb/port-libuv/win_logger.h +28 -0
- package/node_modules/classic-level/deps/snappy/freebsd/config.h +135 -0
- package/node_modules/classic-level/deps/snappy/freebsd/snappy-stubs-public.h +100 -0
- package/node_modules/classic-level/deps/snappy/linux/config.h +135 -0
- package/node_modules/classic-level/deps/snappy/linux/snappy-stubs-public.h +100 -0
- package/node_modules/classic-level/deps/snappy/mac/config.h +137 -0
- package/node_modules/classic-level/deps/snappy/mac/snappy-stubs-public.h +100 -0
- package/node_modules/classic-level/deps/snappy/openbsd/config.h +135 -0
- package/node_modules/classic-level/deps/snappy/openbsd/snappy-stubs-public.h +100 -0
- package/node_modules/classic-level/deps/snappy/snappy/COPYING +54 -0
- package/node_modules/classic-level/deps/snappy/snappy/cmake/SnappyConfig.cmake +1 -0
- package/node_modules/classic-level/deps/snappy/snappy/cmake/config.h.in +62 -0
- package/node_modules/classic-level/deps/snappy/snappy/snappy-c.cc +90 -0
- package/node_modules/classic-level/deps/snappy/snappy/snappy-c.h +138 -0
- package/node_modules/classic-level/deps/snappy/snappy/snappy-internal.h +224 -0
- package/node_modules/classic-level/deps/snappy/snappy/snappy-sinksource.cc +104 -0
- package/node_modules/classic-level/deps/snappy/snappy/snappy-sinksource.h +182 -0
- package/node_modules/classic-level/deps/snappy/snappy/snappy-stubs-internal.cc +42 -0
- package/node_modules/classic-level/deps/snappy/snappy/snappy-stubs-internal.h +561 -0
- package/node_modules/classic-level/deps/snappy/snappy/snappy-stubs-public.h.in +94 -0
- package/node_modules/classic-level/deps/snappy/snappy/snappy-test.cc +612 -0
- package/node_modules/classic-level/deps/snappy/snappy/snappy-test.h +573 -0
- package/node_modules/classic-level/deps/snappy/snappy/snappy.cc +1515 -0
- package/node_modules/classic-level/deps/snappy/snappy/snappy.h +203 -0
- package/node_modules/classic-level/deps/snappy/snappy/snappy_unittest.cc +1410 -0
- package/node_modules/classic-level/deps/snappy/snappy.gyp +119 -0
- package/node_modules/classic-level/deps/snappy/solaris/config.h +135 -0
- package/node_modules/classic-level/deps/snappy/solaris/snappy-stubs-public.h +100 -0
- package/node_modules/classic-level/deps/snappy/win32/config.h +29 -0
- package/node_modules/classic-level/deps/snappy/win32/snappy-stubs-public.h +100 -0
- package/node_modules/classic-level/index.d.ts +343 -0
- package/node_modules/classic-level/index.js +311 -0
- package/node_modules/classic-level/iterator.js +122 -0
- package/node_modules/classic-level/package.json +70 -0
- package/node_modules/classic-level/prebuilds/android-arm/classic-level.armv7.node +0 -0
- package/node_modules/classic-level/prebuilds/android-arm64/classic-level.armv8.node +0 -0
- package/node_modules/classic-level/prebuilds/darwin-x64+arm64/classic-level.node +0 -0
- package/node_modules/classic-level/prebuilds/linux-arm/classic-level.armv6.node +0 -0
- package/node_modules/classic-level/prebuilds/linux-arm/classic-level.armv7.node +0 -0
- package/node_modules/classic-level/prebuilds/linux-arm64/classic-level.armv8.node +0 -0
- package/node_modules/classic-level/prebuilds/linux-x64/classic-level.musl.node +0 -0
- package/node_modules/classic-level/prebuilds/linux-x64/classic-level.node +0 -0
- package/node_modules/classic-level/prebuilds/win32-ia32/classic-level.node +0 -0
- package/node_modules/classic-level/prebuilds/win32-x64/classic-level.node +0 -0
- package/node_modules/ieee754/LICENSE +11 -0
- package/node_modules/ieee754/README.md +51 -0
- package/node_modules/ieee754/index.d.ts +10 -0
- package/node_modules/ieee754/index.js +85 -0
- package/node_modules/ieee754/package.json +52 -0
- package/node_modules/is-buffer/LICENSE +21 -0
- package/node_modules/is-buffer/README.md +54 -0
- package/node_modules/is-buffer/index.d.ts +2 -0
- package/node_modules/is-buffer/index.js +11 -0
- package/node_modules/is-buffer/package.json +65 -0
- package/node_modules/level/CHANGELOG.md +532 -0
- package/node_modules/level/LICENSE +21 -0
- package/node_modules/level/README.md +107 -0
- package/node_modules/level/UPGRADING.md +413 -0
- package/node_modules/level/browser.js +1 -0
- package/node_modules/level/index.d.ts +75 -0
- package/node_modules/level/index.js +1 -0
- package/node_modules/level/package.json +62 -0
- package/node_modules/level-supports/CHANGELOG.md +140 -0
- package/node_modules/level-supports/LICENSE +21 -0
- package/node_modules/level-supports/README.md +276 -0
- package/node_modules/level-supports/index.d.ts +137 -0
- package/node_modules/level-supports/index.js +26 -0
- package/node_modules/level-supports/package.json +46 -0
- package/node_modules/level-supports/test/cloneable.js +26 -0
- package/node_modules/level-supports/test/index.js +24 -0
- package/node_modules/level-supports/test/self.js +86 -0
- package/node_modules/level-supports/test/shape.js +23 -0
- package/node_modules/level-transcoder/CHANGELOG.md +15 -0
- package/node_modules/level-transcoder/LICENSE +21 -0
- package/node_modules/level-transcoder/README.md +214 -0
- package/node_modules/level-transcoder/UPGRADING.md +13 -0
- package/node_modules/level-transcoder/index.d.ts +27 -0
- package/node_modules/level-transcoder/index.js +158 -0
- package/node_modules/level-transcoder/lib/encoding.d.ts +158 -0
- package/node_modules/level-transcoder/lib/encoding.js +105 -0
- package/node_modules/level-transcoder/lib/encodings.d.ts +9 -0
- package/node_modules/level-transcoder/lib/encodings.js +135 -0
- package/node_modules/level-transcoder/lib/formats.d.ts +13 -0
- package/node_modules/level-transcoder/lib/formats.js +111 -0
- package/node_modules/level-transcoder/lib/text-endec.d.ts +6 -0
- package/node_modules/level-transcoder/lib/text-endec.js +19 -0
- package/node_modules/level-transcoder/package.json +46 -0
- package/node_modules/maybe-combine-errors/LICENSE.md +21 -0
- package/node_modules/maybe-combine-errors/README.md +94 -0
- package/node_modules/maybe-combine-errors/index.js +60 -0
- package/node_modules/maybe-combine-errors/package.json +33 -0
- package/node_modules/module-error/CHANGELOG.md +23 -0
- package/node_modules/module-error/LICENSE +21 -0
- package/node_modules/module-error/README.md +89 -0
- package/node_modules/module-error/index.d.ts +18 -0
- package/node_modules/module-error/index.js +22 -0
- package/node_modules/module-error/package.json +36 -0
- package/node_modules/napi-macros/LICENSE +21 -0
- package/node_modules/napi-macros/README.md +277 -0
- package/node_modules/napi-macros/index.js +1 -0
- package/node_modules/napi-macros/napi-macros.h +255 -0
- package/node_modules/napi-macros/package.json +22 -0
- package/node_modules/node-gyp-build/LICENSE +21 -0
- package/node_modules/node-gyp-build/README.md +58 -0
- package/node_modules/node-gyp-build/SECURITY.md +5 -0
- package/node_modules/node-gyp-build/bin.js +84 -0
- package/node_modules/node-gyp-build/build-test.js +19 -0
- package/node_modules/node-gyp-build/index.js +6 -0
- package/node_modules/node-gyp-build/node-gyp-build.js +207 -0
- package/node_modules/node-gyp-build/optional.js +7 -0
- package/node_modules/node-gyp-build/package.json +43 -0
- package/package.json +6 -3
- package/{providerPresetApply-7XEHTODR.js → providerPresetApply-XTEXOH7X.js} +3 -3
- package/{providerRegistry-GNV4FFSA.js → providerRegistry-ADYDSLKR.js} +3 -3
- package/{readlineWorkspace-46KDFUHN.js → readlineWorkspace-LO6ZLFTO.js} +1381 -1317
- package/{runAgentClientLoop-CQWLWU4W.js → runAgentClientLoop-KAYGKVTK.js} +16 -15
- package/{runtimeDoctorCommands-3USD722U.js → runtimeDoctorCommands-ID23PNNZ.js} +28 -0
- package/{spaceCommands-TSBC5UWP.js → spaceCommands-FA6RCZ2Y.js} +10 -9
- package/{streamAgentChatTurn-XD5JEJXA.js → streamAgentChatTurn-DFNDU2UY.js} +17 -16
- package/{tools-5NAJJTFN.js → tools-6HKH75NT.js} +13 -12
- package/{tuiSummaryLlmCaller-7KLLDP22.js → tuiSummaryLlmCaller-CFFVXQWX.js} +2 -2
- package/{updateCommands-XYMGOKF4.js → updateCommands-4EHDLF5J.js} +1 -1
- package/{updateDialogTitleAction-IKZPJCRA.js → updateDialogTitleAction-FFSYTLBC.js} +3 -3
|
@@ -0,0 +1,2652 @@
|
|
|
1
|
+
#define NAPI_VERSION 3
|
|
2
|
+
|
|
3
|
+
#include <napi-macros.h>
|
|
4
|
+
#include <node_api.h>
|
|
5
|
+
#include <assert.h>
|
|
6
|
+
|
|
7
|
+
#include <leveldb/db.h>
|
|
8
|
+
#include <leveldb/write_batch.h>
|
|
9
|
+
#include <leveldb/cache.h>
|
|
10
|
+
#include <leveldb/filter_policy.h>
|
|
11
|
+
|
|
12
|
+
#include <map>
|
|
13
|
+
#include <vector>
|
|
14
|
+
#include <mutex>
|
|
15
|
+
#include <atomic>
|
|
16
|
+
#include <optional>
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Forward declarations.
|
|
20
|
+
*/
|
|
21
|
+
struct Database;
|
|
22
|
+
struct Resource;
|
|
23
|
+
struct Iterator;
|
|
24
|
+
struct ExplicitSnapshot;
|
|
25
|
+
static leveldb::Status threadsafe_open(const leveldb::Options &options,
|
|
26
|
+
bool multithreading,
|
|
27
|
+
Database &db_instance);
|
|
28
|
+
static leveldb::Status threadsafe_close(Database &db_instance);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Global declarations for multi-threaded access. These are not context-aware
|
|
32
|
+
* by definition and is specifically to allow for cross thread access to the
|
|
33
|
+
* single database handle.
|
|
34
|
+
*/
|
|
35
|
+
struct LevelDbHandle
|
|
36
|
+
{
|
|
37
|
+
leveldb::DB *db;
|
|
38
|
+
size_t open_handle_count;
|
|
39
|
+
};
|
|
40
|
+
static std::mutex handles_mutex;
|
|
41
|
+
// only access this when protected by the handles_mutex!
|
|
42
|
+
static std::map<std::string, LevelDbHandle> db_handles;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Macros.
|
|
46
|
+
*/
|
|
47
|
+
#define NAPI_DB_CONTEXT() \
|
|
48
|
+
Database* database = NULL; \
|
|
49
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&database));
|
|
50
|
+
|
|
51
|
+
#define NAPI_ITERATOR_CONTEXT() \
|
|
52
|
+
Iterator* iterator = NULL; \
|
|
53
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&iterator));
|
|
54
|
+
|
|
55
|
+
#define NAPI_BATCH_CONTEXT() \
|
|
56
|
+
Batch* batch = NULL; \
|
|
57
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&batch));
|
|
58
|
+
|
|
59
|
+
#define NAPI_SNAPSHOT_CONTEXT() \
|
|
60
|
+
ExplicitSnapshot* snapshot = NULL; \
|
|
61
|
+
NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)&snapshot));
|
|
62
|
+
|
|
63
|
+
#define NAPI_RETURN_UNDEFINED() \
|
|
64
|
+
return 0;
|
|
65
|
+
|
|
66
|
+
#define NAPI_PROMISE() \
|
|
67
|
+
napi_deferred deferred; \
|
|
68
|
+
napi_value promise; \
|
|
69
|
+
NAPI_STATUS_THROWS(napi_create_promise(env, &deferred, &promise));
|
|
70
|
+
|
|
71
|
+
#define NAPI_UTF8_NEW(name, val) \
|
|
72
|
+
size_t name##_size = 0; \
|
|
73
|
+
NAPI_STATUS_THROWS(napi_get_value_string_utf8(env, val, NULL, 0, &name##_size)) \
|
|
74
|
+
char* name = new char[name##_size + 1]; \
|
|
75
|
+
NAPI_STATUS_THROWS(napi_get_value_string_utf8(env, val, name, name##_size + 1, &name##_size)) \
|
|
76
|
+
name[name##_size] = '\0';
|
|
77
|
+
|
|
78
|
+
#define NAPI_ARGV_UTF8_NEW(name, i) \
|
|
79
|
+
NAPI_UTF8_NEW(name, argv[i])
|
|
80
|
+
|
|
81
|
+
// TODO: consider using encoding options instead of type checking
|
|
82
|
+
#define LD_STRING_OR_BUFFER_TO_COPY(env, from, to) \
|
|
83
|
+
char* to##Ch_ = 0; \
|
|
84
|
+
size_t to##Sz_ = 0; \
|
|
85
|
+
if (IsString(env, from)) { \
|
|
86
|
+
napi_get_value_string_utf8(env, from, NULL, 0, &to##Sz_); \
|
|
87
|
+
to##Ch_ = new char[to##Sz_ + 1]; \
|
|
88
|
+
napi_get_value_string_utf8(env, from, to##Ch_, to##Sz_ + 1, &to##Sz_); \
|
|
89
|
+
to##Ch_[to##Sz_] = '\0'; \
|
|
90
|
+
} else if (IsBuffer(env, from)) { \
|
|
91
|
+
char* buf = 0; \
|
|
92
|
+
napi_get_buffer_info(env, from, (void **)&buf, &to##Sz_); \
|
|
93
|
+
to##Ch_ = new char[to##Sz_]; \
|
|
94
|
+
memcpy(to##Ch_, buf, to##Sz_); \
|
|
95
|
+
} else { \
|
|
96
|
+
char* buf = 0; \
|
|
97
|
+
napi_typedarray_type type; \
|
|
98
|
+
napi_status status = napi_get_typedarray_info(env, from, &type, &to##Sz_, (void **)&buf, NULL, NULL); \
|
|
99
|
+
if (status != napi_ok || type != napi_typedarray_type::napi_uint8_array) { \
|
|
100
|
+
/* TODO: refactor so that we can napi_throw_type_error() here */ \
|
|
101
|
+
to##Sz_ = 0; \
|
|
102
|
+
to##Ch_ = new char[to##Sz_]; \
|
|
103
|
+
} else { \
|
|
104
|
+
to##Ch_ = new char[to##Sz_]; \
|
|
105
|
+
memcpy(to##Ch_, buf, to##Sz_); \
|
|
106
|
+
} \
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
#define undefined NULL
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Bit fields.
|
|
113
|
+
*/
|
|
114
|
+
#define STATE_ENDED 1
|
|
115
|
+
|
|
116
|
+
/*********************************************************************
|
|
117
|
+
* Helpers.
|
|
118
|
+
********************************************************************/
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Returns true if 'value' is a string.
|
|
122
|
+
*/
|
|
123
|
+
static bool IsString (napi_env env, napi_value value) {
|
|
124
|
+
napi_valuetype type;
|
|
125
|
+
napi_typeof(env, value, &type);
|
|
126
|
+
return type == napi_string;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Returns true if 'value' is a buffer.
|
|
131
|
+
*/
|
|
132
|
+
static bool IsBuffer (napi_env env, napi_value value) {
|
|
133
|
+
bool isBuffer;
|
|
134
|
+
napi_is_buffer(env, value, &isBuffer);
|
|
135
|
+
return isBuffer;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Returns true if 'value' is an object.
|
|
140
|
+
*/
|
|
141
|
+
static bool IsObject (napi_env env, napi_value value) {
|
|
142
|
+
napi_valuetype type;
|
|
143
|
+
napi_typeof(env, value, &type);
|
|
144
|
+
return type == napi_object;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Create an error object.
|
|
149
|
+
*/
|
|
150
|
+
static napi_value CreateError (napi_env env, const char* str) {
|
|
151
|
+
napi_value msg;
|
|
152
|
+
napi_create_string_utf8(env, str, strlen(str), &msg);
|
|
153
|
+
napi_value error;
|
|
154
|
+
napi_create_error(env, NULL, msg, &error);
|
|
155
|
+
return error;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
static napi_value CreateCodeError (napi_env env, const char* code, const char* msg) {
|
|
159
|
+
napi_value codeValue;
|
|
160
|
+
napi_create_string_utf8(env, code, strlen(code), &codeValue);
|
|
161
|
+
napi_value msgValue;
|
|
162
|
+
napi_create_string_utf8(env, msg, strlen(msg), &msgValue);
|
|
163
|
+
napi_value error;
|
|
164
|
+
napi_create_error(env, codeValue, msgValue, &error);
|
|
165
|
+
return error;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
static void ThrowError(napi_env env, leveldb::Status status) {
|
|
169
|
+
if (status.IsCorruption()) {
|
|
170
|
+
napi_throw_error(env, "LEVEL_CORRUPTION", status.ToString().c_str());
|
|
171
|
+
} else if (!status.ok()) {
|
|
172
|
+
napi_throw_error(env, NULL, status.ToString().c_str());
|
|
173
|
+
} else {
|
|
174
|
+
napi_throw_error(env, NULL, "Operation failed");
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
static napi_value ErrorOrNotFound(napi_env env, leveldb::Status status) {
|
|
179
|
+
if (status.IsNotFound()) {
|
|
180
|
+
return undefined;
|
|
181
|
+
} else {
|
|
182
|
+
ThrowError(env, status);
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Returns true if 'obj' has a property 'key'.
|
|
189
|
+
*/
|
|
190
|
+
static bool HasProperty (napi_env env, napi_value obj, const char* key) {
|
|
191
|
+
bool has = false;
|
|
192
|
+
napi_has_named_property(env, obj, key, &has);
|
|
193
|
+
return has;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Returns a property in napi_value form.
|
|
198
|
+
*/
|
|
199
|
+
static napi_value GetProperty (napi_env env, napi_value obj, const char* key) {
|
|
200
|
+
napi_value value;
|
|
201
|
+
napi_get_named_property(env, obj, key, &value);
|
|
202
|
+
return value;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Returns a boolean property 'key' from 'obj'.
|
|
207
|
+
* Returns 'DEFAULT' if the property doesn't exist.
|
|
208
|
+
*/
|
|
209
|
+
static bool BooleanProperty (napi_env env, napi_value obj, const char* key,
|
|
210
|
+
bool DEFAULT) {
|
|
211
|
+
if (HasProperty(env, obj, key)) {
|
|
212
|
+
napi_value value = GetProperty(env, obj, key);
|
|
213
|
+
bool result;
|
|
214
|
+
napi_get_value_bool(env, value, &result);
|
|
215
|
+
return result;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return DEFAULT;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Returns a boolean value.
|
|
223
|
+
* Returns 'DEFAULT' if the JS value is undefined or otherwise not a boolean.
|
|
224
|
+
*/
|
|
225
|
+
static bool BooleanValue (napi_env env, napi_value value, bool DEFAULT) {
|
|
226
|
+
bool result;
|
|
227
|
+
|
|
228
|
+
if (napi_get_value_bool(env, value, &result) == napi_ok) {
|
|
229
|
+
return result;
|
|
230
|
+
} else {
|
|
231
|
+
return DEFAULT;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
enum Encoding { buffer, utf8, view };
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Returns internal Encoding enum matching the given encoding option.
|
|
239
|
+
*/
|
|
240
|
+
static Encoding GetEncoding (napi_env env, napi_value options, const char* option) {
|
|
241
|
+
napi_value value;
|
|
242
|
+
size_t copied;
|
|
243
|
+
char buf[2];
|
|
244
|
+
|
|
245
|
+
if (napi_get_named_property(env, options, option, &value) == napi_ok &&
|
|
246
|
+
napi_get_value_string_utf8(env, value, buf, 2, &copied) == napi_ok && copied == 1) {
|
|
247
|
+
// Value is either "buffer", "utf8" or "view" so we only have to read the first char
|
|
248
|
+
switch (buf[0]) {
|
|
249
|
+
case 'b': return Encoding::buffer;
|
|
250
|
+
case 'v': return Encoding::view;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return Encoding::utf8;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Returns internal Encoding enum by its equivalent numeric value.
|
|
259
|
+
*/
|
|
260
|
+
static Encoding GetEncoding (napi_env env, napi_value value) {
|
|
261
|
+
int32_t result;
|
|
262
|
+
|
|
263
|
+
if (napi_get_value_int32(env, value, &result) == napi_ok) {
|
|
264
|
+
return static_cast<Encoding>(result);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return Encoding::utf8;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Returns a uint32 property 'key' from 'obj'.
|
|
272
|
+
* Returns 'DEFAULT' if the property doesn't exist.
|
|
273
|
+
*/
|
|
274
|
+
static uint32_t Uint32Property (napi_env env, napi_value obj, const char* key,
|
|
275
|
+
uint32_t DEFAULT) {
|
|
276
|
+
if (HasProperty(env, obj, key)) {
|
|
277
|
+
napi_value value = GetProperty(env, obj, key);
|
|
278
|
+
uint32_t result;
|
|
279
|
+
napi_get_value_uint32(env, value, &result);
|
|
280
|
+
return result;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return DEFAULT;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Returns a int32 property 'key' from 'obj'.
|
|
288
|
+
* Returns 'DEFAULT' if the property doesn't exist.
|
|
289
|
+
*/
|
|
290
|
+
static int Int32Property (napi_env env, napi_value obj, const char* key,
|
|
291
|
+
int DEFAULT) {
|
|
292
|
+
if (HasProperty(env, obj, key)) {
|
|
293
|
+
napi_value value = GetProperty(env, obj, key);
|
|
294
|
+
int result;
|
|
295
|
+
napi_get_value_int32(env, value, &result);
|
|
296
|
+
return result;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return DEFAULT;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Returns a string property 'key' from 'obj'.
|
|
304
|
+
* Returns empty string if the property doesn't exist.
|
|
305
|
+
*/
|
|
306
|
+
static std::string StringProperty (napi_env env, napi_value obj, const char* key) {
|
|
307
|
+
if (HasProperty(env, obj, key)) {
|
|
308
|
+
napi_value value = GetProperty(env, obj, key);
|
|
309
|
+
if (IsString(env, value)) {
|
|
310
|
+
size_t size = 0;
|
|
311
|
+
napi_get_value_string_utf8(env, value, NULL, 0, &size);
|
|
312
|
+
|
|
313
|
+
char* buf = new char[size + 1];
|
|
314
|
+
napi_get_value_string_utf8(env, value, buf, size + 1, &size);
|
|
315
|
+
buf[size] = '\0';
|
|
316
|
+
|
|
317
|
+
std::string result = buf;
|
|
318
|
+
delete [] buf;
|
|
319
|
+
return result;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return "";
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
static void DisposeSliceBuffer (leveldb::Slice slice) {
|
|
327
|
+
if (!slice.empty()) delete [] slice.data();
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Convert a napi_value to a leveldb::Slice.
|
|
332
|
+
*/
|
|
333
|
+
static leveldb::Slice ToSlice (napi_env env, napi_value from) {
|
|
334
|
+
LD_STRING_OR_BUFFER_TO_COPY(env, from, to);
|
|
335
|
+
return leveldb::Slice(toCh_, toSz_);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Takes a Buffer, string or Uint8Array property 'name' from 'opts'.
|
|
340
|
+
* Returns null if the property does not exist.
|
|
341
|
+
*/
|
|
342
|
+
static std::string* RangeOption (napi_env env, napi_value opts, const char* name) {
|
|
343
|
+
if (HasProperty(env, opts, name)) {
|
|
344
|
+
napi_value value = GetProperty(env, opts, name);
|
|
345
|
+
// TODO: we can avoid a copy here
|
|
346
|
+
LD_STRING_OR_BUFFER_TO_COPY(env, value, to);
|
|
347
|
+
std::string* result = new std::string(toCh_, toSz_);
|
|
348
|
+
delete [] toCh_;
|
|
349
|
+
return result;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return NULL;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Converts an array containing Buffer or string keys to a vector.
|
|
357
|
+
*/
|
|
358
|
+
static std::vector<std::string> KeyArray (napi_env env, napi_value arr) {
|
|
359
|
+
uint32_t length;
|
|
360
|
+
std::vector<std::string> result;
|
|
361
|
+
|
|
362
|
+
if (napi_get_array_length(env, arr, &length) == napi_ok) {
|
|
363
|
+
result.reserve(length);
|
|
364
|
+
|
|
365
|
+
for (uint32_t i = 0; i < length; i++) {
|
|
366
|
+
napi_value element;
|
|
367
|
+
|
|
368
|
+
if (napi_get_element(env, arr, i, &element) == napi_ok) {
|
|
369
|
+
LD_STRING_OR_BUFFER_TO_COPY(env, element, to);
|
|
370
|
+
result.emplace_back(toCh_, toSz_);
|
|
371
|
+
delete [] toCh_;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return result;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// TODO: use in more places
|
|
380
|
+
enum Flags : uint32_t {
|
|
381
|
+
FILL_CACHE = 1,
|
|
382
|
+
KEY_AS_BUFFER = 2,
|
|
383
|
+
VALUE_AS_BUFFER = 4,
|
|
384
|
+
SHARED_KEY = 8
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Whether to yield entries, keys or values.
|
|
389
|
+
*/
|
|
390
|
+
enum Mode {
|
|
391
|
+
entries,
|
|
392
|
+
keys,
|
|
393
|
+
values
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Helper struct for caching and converting a key-value pair to napi_values.
|
|
398
|
+
*/
|
|
399
|
+
struct Entry {
|
|
400
|
+
Entry (const leveldb::Slice& key, const leveldb::Slice& value)
|
|
401
|
+
: key_(key.data(), key.size()),
|
|
402
|
+
value_(value.data(), value.size()) {}
|
|
403
|
+
|
|
404
|
+
void ConvertByMode (napi_env env, Mode mode, const Encoding keyEncoding, const Encoding valueEncoding, napi_value& result) const {
|
|
405
|
+
if (mode == Mode::entries) {
|
|
406
|
+
napi_create_array_with_length(env, 2, &result);
|
|
407
|
+
|
|
408
|
+
napi_value keyElement;
|
|
409
|
+
napi_value valueElement;
|
|
410
|
+
|
|
411
|
+
Convert(env, &key_, keyEncoding, keyElement);
|
|
412
|
+
Convert(env, &value_, valueEncoding, valueElement);
|
|
413
|
+
|
|
414
|
+
napi_set_element(env, result, 0, keyElement);
|
|
415
|
+
napi_set_element(env, result, 1, valueElement);
|
|
416
|
+
} else if (mode == Mode::keys) {
|
|
417
|
+
Convert(env, &key_, keyEncoding, result);
|
|
418
|
+
} else {
|
|
419
|
+
Convert(env, &value_, valueEncoding, result);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
static void Convert (napi_env env, const std::string* s, const Encoding encoding, napi_value& result) {
|
|
424
|
+
if (s == NULL) {
|
|
425
|
+
napi_get_undefined(env, &result);
|
|
426
|
+
} else if (encoding == Encoding::buffer || encoding == Encoding::view) {
|
|
427
|
+
napi_create_buffer_copy(env, s->size(), s->data(), NULL, &result);
|
|
428
|
+
} else {
|
|
429
|
+
napi_create_string_utf8(env, s->data(), s->size(), &result);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
private:
|
|
434
|
+
std::string key_;
|
|
435
|
+
std::string value_;
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Base worker class. Handles the async work. Derived classes can override the
|
|
440
|
+
* following virtual methods (listed in the order in which they're called):
|
|
441
|
+
*
|
|
442
|
+
* - DoExecute (abstract, worker pool thread): main work
|
|
443
|
+
* - HandleOKCallback (main thread): resolve JS promise on success
|
|
444
|
+
* - HandleErrorCallback (main thread): reject JS promise on error
|
|
445
|
+
* - DoFinally (main thread): do cleanup regardless of success
|
|
446
|
+
*/
|
|
447
|
+
struct BaseWorker {
|
|
448
|
+
// Note: storing env is discouraged as we'd end up using it in unsafe places.
|
|
449
|
+
BaseWorker (napi_env env,
|
|
450
|
+
Database* database,
|
|
451
|
+
napi_deferred deferred,
|
|
452
|
+
const char* resourceName)
|
|
453
|
+
: database_(database), errMsg_(NULL), deferred_(deferred) {
|
|
454
|
+
// Note: napi_deferred is a strong reference to the JS promise, so there's no need to
|
|
455
|
+
// create a reference ourselves. See `v8_deferred = new v8::Persistent<v8::Value>()` in:
|
|
456
|
+
// https://github.com/nodejs/node/commit/7efb8f7619100973877c660d0ee527ea3d92de8d
|
|
457
|
+
|
|
458
|
+
napi_value asyncResourceName;
|
|
459
|
+
NAPI_STATUS_THROWS_VOID(napi_create_string_utf8(env, resourceName,
|
|
460
|
+
NAPI_AUTO_LENGTH,
|
|
461
|
+
&asyncResourceName));
|
|
462
|
+
NAPI_STATUS_THROWS_VOID(napi_create_async_work(env, NULL,
|
|
463
|
+
asyncResourceName,
|
|
464
|
+
BaseWorker::Execute,
|
|
465
|
+
BaseWorker::Complete,
|
|
466
|
+
this, &asyncWork_));
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
virtual ~BaseWorker () {
|
|
470
|
+
delete [] errMsg_;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
static void Execute (napi_env env, void* data) {
|
|
474
|
+
BaseWorker* self = (BaseWorker*)data;
|
|
475
|
+
|
|
476
|
+
// Don't pass env to DoExecute() because use of Node-API
|
|
477
|
+
// methods should generally be avoided in async work.
|
|
478
|
+
self->DoExecute();
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
bool SetStatus (leveldb::Status status) {
|
|
482
|
+
status_ = status;
|
|
483
|
+
if (!status.ok()) {
|
|
484
|
+
SetErrorMessage(status.ToString().c_str());
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
return true;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
void SetErrorMessage(const char *msg) {
|
|
491
|
+
delete [] errMsg_;
|
|
492
|
+
size_t size = strlen(msg) + 1;
|
|
493
|
+
errMsg_ = new char[size];
|
|
494
|
+
memcpy(errMsg_, msg, size);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
virtual void DoExecute () = 0;
|
|
498
|
+
|
|
499
|
+
static void Complete (napi_env env, napi_status status, void* data) {
|
|
500
|
+
BaseWorker* self = (BaseWorker*)data;
|
|
501
|
+
|
|
502
|
+
self->DoComplete(env);
|
|
503
|
+
self->DoFinally(env);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
void DoComplete (napi_env env) {
|
|
507
|
+
if (status_.ok()) {
|
|
508
|
+
HandleOKCallback(env, deferred_);
|
|
509
|
+
} else {
|
|
510
|
+
HandleErrorCallback(env, deferred_);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
virtual void HandleOKCallback (napi_env env, napi_deferred deferred) {
|
|
515
|
+
napi_value argv;
|
|
516
|
+
napi_get_undefined(env, &argv);
|
|
517
|
+
napi_resolve_deferred(env, deferred, argv);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
virtual void HandleErrorCallback (napi_env env, napi_deferred deferred) {
|
|
521
|
+
napi_value argv;
|
|
522
|
+
|
|
523
|
+
if (status_.IsNotFound()) {
|
|
524
|
+
napi_get_undefined(env, &argv);
|
|
525
|
+
napi_resolve_deferred(env, deferred, argv);
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (status_.IsCorruption()) {
|
|
530
|
+
argv = CreateCodeError(env, "LEVEL_CORRUPTION", errMsg_);
|
|
531
|
+
} else if (status_.IsIOError()) {
|
|
532
|
+
if (strlen(errMsg_) > 15 && strncmp("IO error: lock ", errMsg_, 15) == 0) { // env_posix.cc
|
|
533
|
+
argv = CreateCodeError(env, "LEVEL_LOCKED", errMsg_);
|
|
534
|
+
} else if (strlen(errMsg_) > 19 && strncmp("IO error: LockFile ", errMsg_, 19) == 0) { // env_win.cc
|
|
535
|
+
argv = CreateCodeError(env, "LEVEL_LOCKED", errMsg_);
|
|
536
|
+
} else {
|
|
537
|
+
argv = CreateCodeError(env, "LEVEL_IO_ERROR", errMsg_);
|
|
538
|
+
}
|
|
539
|
+
} else {
|
|
540
|
+
argv = CreateError(env, errMsg_);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
napi_reject_deferred(env, deferred, argv);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
virtual void DoFinally (napi_env env) {
|
|
547
|
+
napi_delete_async_work(env, asyncWork_);
|
|
548
|
+
deferred_ = NULL;
|
|
549
|
+
delete this;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
void Queue (napi_env env) {
|
|
553
|
+
napi_queue_async_work(env, asyncWork_);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
Database* database_;
|
|
557
|
+
|
|
558
|
+
private:
|
|
559
|
+
napi_deferred deferred_;
|
|
560
|
+
napi_async_work asyncWork_;
|
|
561
|
+
leveldb::Status status_;
|
|
562
|
+
char *errMsg_;
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Owns the LevelDB storage, cache, filter policy and resources.
|
|
567
|
+
*/
|
|
568
|
+
struct Database {
|
|
569
|
+
Database ()
|
|
570
|
+
: db_(NULL),
|
|
571
|
+
sharedBuffer_(NULL),
|
|
572
|
+
blockCache_(NULL),
|
|
573
|
+
filterPolicy_(leveldb::NewBloomFilterPolicy(10)),
|
|
574
|
+
resourceSequence_(0),
|
|
575
|
+
pendingCloseWorker_(NULL),
|
|
576
|
+
ref_(NULL),
|
|
577
|
+
sharedBufferRef_(NULL),
|
|
578
|
+
priorityWork_(0) {}
|
|
579
|
+
|
|
580
|
+
~Database () {
|
|
581
|
+
if (db_ != NULL) {
|
|
582
|
+
threadsafe_close(*this);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
leveldb::Status Open (const leveldb::Options& options,
|
|
587
|
+
const std::string &location,
|
|
588
|
+
bool multithreading) {
|
|
589
|
+
location_ = location;
|
|
590
|
+
return threadsafe_open(options, multithreading, *this);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
void CloseDatabase () {
|
|
594
|
+
if (db_ != NULL) {
|
|
595
|
+
threadsafe_close(*this);
|
|
596
|
+
}
|
|
597
|
+
if (blockCache_) {
|
|
598
|
+
delete blockCache_;
|
|
599
|
+
blockCache_ = NULL;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
leveldb::Status Put (const leveldb::WriteOptions& options,
|
|
604
|
+
leveldb::Slice key,
|
|
605
|
+
leveldb::Slice value) {
|
|
606
|
+
return db_->Put(options, key, value);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
leveldb::Status Get (const leveldb::ReadOptions& options,
|
|
610
|
+
leveldb::Slice key,
|
|
611
|
+
leveldb::ValueSink& value) {
|
|
612
|
+
return db_->Get(options, key, &value);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
leveldb::Status Del (const leveldb::WriteOptions& options,
|
|
616
|
+
leveldb::Slice key) {
|
|
617
|
+
return db_->Delete(options, key);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
leveldb::Status WriteBatch (const leveldb::WriteOptions& options,
|
|
621
|
+
leveldb::WriteBatch* batch) {
|
|
622
|
+
return db_->Write(options, batch);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
uint64_t ApproximateSize (const leveldb::Range* range) {
|
|
626
|
+
uint64_t size = 0;
|
|
627
|
+
db_->GetApproximateSizes(range, 1, &size);
|
|
628
|
+
return size;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
void CompactRange (const leveldb::Slice* start,
|
|
632
|
+
const leveldb::Slice* end) {
|
|
633
|
+
db_->CompactRange(start, end);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
void GetProperty (const leveldb::Slice& property, std::string* value) {
|
|
637
|
+
db_->GetProperty(property, value);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
const leveldb::Snapshot* NewSnapshot () {
|
|
641
|
+
return db_->GetSnapshot();
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
leveldb::Iterator* NewIterator (leveldb::ReadOptions* options) {
|
|
645
|
+
return db_->NewIterator(*options);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
void ReleaseSnapshot (const leveldb::Snapshot* snapshot) {
|
|
649
|
+
return db_->ReleaseSnapshot(snapshot);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// The env argument is unused but reminds us to increment priorityWork_
|
|
653
|
+
// only in the JavaScript main thread to avoid needing a lock around
|
|
654
|
+
// that and pendingCloseWorker_.
|
|
655
|
+
void IncrementPriorityWork (napi_env env) {
|
|
656
|
+
priorityWork_++;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
void DecrementPriorityWork (napi_env env) {
|
|
660
|
+
if (--priorityWork_ == 0 && pendingCloseWorker_ != NULL) {
|
|
661
|
+
pendingCloseWorker_->Queue(env);
|
|
662
|
+
pendingCloseWorker_ = NULL;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
bool HasPriorityWork () const {
|
|
667
|
+
return priorityWork_ > 0;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
bool SetSharedBuffer (napi_env env, napi_value value) {
|
|
671
|
+
// Delete reference to previous buffer if any
|
|
672
|
+
if (sharedBufferRef_) {
|
|
673
|
+
napi_delete_reference(env, sharedBufferRef_);
|
|
674
|
+
sharedBufferRef_ = NULL;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// Get underlying data (length is separately communicated, on use)
|
|
678
|
+
if (napi_get_buffer_info(env, value, (void**)&sharedBuffer_, NULL) != napi_ok) {
|
|
679
|
+
sharedBuffer_ = NULL;
|
|
680
|
+
return false;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// Create reference in order to keep buffer alive
|
|
684
|
+
if (napi_create_reference(env, value, 1, &sharedBufferRef_) != napi_ok) {
|
|
685
|
+
sharedBufferRef_ = NULL;
|
|
686
|
+
return false;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
return true;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
void ReleaseReferences (napi_env env) {
|
|
693
|
+
if (ref_ != NULL) napi_reference_unref(env, ref_, NULL);
|
|
694
|
+
if (sharedBufferRef_ != NULL) napi_reference_unref(env, sharedBufferRef_, NULL);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
void DeleteReferences (napi_env env) {
|
|
698
|
+
if (ref_ != NULL) napi_delete_reference(env, ref_);
|
|
699
|
+
if (sharedBufferRef_ != NULL) napi_delete_reference(env, sharedBufferRef_);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
leveldb::DB* db_;
|
|
703
|
+
char* sharedBuffer_;
|
|
704
|
+
leveldb::Cache* blockCache_;
|
|
705
|
+
const leveldb::FilterPolicy* filterPolicy_;
|
|
706
|
+
uint32_t resourceSequence_;
|
|
707
|
+
BaseWorker *pendingCloseWorker_;
|
|
708
|
+
std::map<uint32_t, Resource*> resources_;
|
|
709
|
+
napi_ref ref_;
|
|
710
|
+
|
|
711
|
+
private:
|
|
712
|
+
napi_ref sharedBufferRef_;
|
|
713
|
+
std::atomic<uint32_t> priorityWork_;
|
|
714
|
+
std::string location_;
|
|
715
|
+
|
|
716
|
+
// for separation of concerns the threadsafe functionality was kept at the global
|
|
717
|
+
// level and made a friend so it is explict where the threadsafe boundary exists
|
|
718
|
+
friend leveldb::Status threadsafe_open(const leveldb::Options &options,
|
|
719
|
+
bool multithreading,
|
|
720
|
+
Database &db_instance);
|
|
721
|
+
friend leveldb::Status threadsafe_close(Database &db_instance);
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
leveldb::Status threadsafe_open(const leveldb::Options &options,
|
|
726
|
+
bool multithreading,
|
|
727
|
+
Database &db_instance) {
|
|
728
|
+
// Bypass lock and handles if multithreading is disabled
|
|
729
|
+
if (!multithreading) {
|
|
730
|
+
return leveldb::DB::Open(options, db_instance.location_, &db_instance.db_);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
std::unique_lock<std::mutex> lock(handles_mutex);
|
|
734
|
+
|
|
735
|
+
auto it = db_handles.find(db_instance.location_);
|
|
736
|
+
if (it == db_handles.end()) {
|
|
737
|
+
// Database not opened yet for this location, unless it was with
|
|
738
|
+
// multithreading disabled, in which case we're expected to fail here.
|
|
739
|
+
LevelDbHandle handle = {nullptr, 0};
|
|
740
|
+
leveldb::Status status = leveldb::DB::Open(options, db_instance.location_, &handle.db);
|
|
741
|
+
|
|
742
|
+
if (status.ok()) {
|
|
743
|
+
handle.open_handle_count++;
|
|
744
|
+
db_instance.db_ = handle.db;
|
|
745
|
+
db_handles[db_instance.location_] = handle;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
return status;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
++(it->second.open_handle_count);
|
|
752
|
+
db_instance.db_ = it->second.db;
|
|
753
|
+
|
|
754
|
+
return leveldb::Status::OK();
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
leveldb::Status threadsafe_close(Database &db_instance) {
|
|
758
|
+
std::unique_lock<std::mutex> lock(handles_mutex);
|
|
759
|
+
|
|
760
|
+
auto it = db_handles.find(db_instance.location_);
|
|
761
|
+
if (it == db_handles.end()) {
|
|
762
|
+
// Was not opened with multithreading enabled
|
|
763
|
+
delete db_instance.db_;
|
|
764
|
+
} else if (--(it->second.open_handle_count) == 0) {
|
|
765
|
+
delete it->second.db;
|
|
766
|
+
db_handles.erase(it);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// ensure db_ pointer is nullified in Database instance
|
|
770
|
+
db_instance.db_ = NULL;
|
|
771
|
+
return leveldb::Status::OK();
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/**
|
|
775
|
+
* Represents an object that has a strong reference until explicitly closed. In
|
|
776
|
+
* addition, resources are tracked in database->resources in order to close
|
|
777
|
+
* them when the Node.js environment is tore down.
|
|
778
|
+
*/
|
|
779
|
+
struct Resource {
|
|
780
|
+
Resource (Database* database)
|
|
781
|
+
: database(database),
|
|
782
|
+
id_(++database->resourceSequence_),
|
|
783
|
+
ref_(NULL) {
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
virtual ~Resource () { }
|
|
787
|
+
virtual void CloseResource () = 0;
|
|
788
|
+
|
|
789
|
+
void Attach (napi_env env, napi_value context) {
|
|
790
|
+
napi_create_reference(env, context, 1, &ref_);
|
|
791
|
+
database->resources_[id_] = this;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
void Detach (napi_env env) {
|
|
795
|
+
database->resources_.erase(id_);
|
|
796
|
+
if (ref_ != NULL) napi_delete_reference(env, ref_);
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
static void CollectGarbage (napi_env env, void* data, void* hint) {
|
|
800
|
+
if (data) {
|
|
801
|
+
delete (Resource*)data;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
Database* database;
|
|
806
|
+
|
|
807
|
+
private:
|
|
808
|
+
const uint32_t id_;
|
|
809
|
+
napi_ref ref_;
|
|
810
|
+
};
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* Explicit snapshot of database.
|
|
814
|
+
*/
|
|
815
|
+
struct ExplicitSnapshot final : public Resource {
|
|
816
|
+
ExplicitSnapshot (Database* database)
|
|
817
|
+
: Resource(database),
|
|
818
|
+
nut(database->NewSnapshot()) {
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
void CloseResource () override {
|
|
822
|
+
database->ReleaseSnapshot(nut);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
const leveldb::Snapshot* nut;
|
|
826
|
+
};
|
|
827
|
+
|
|
828
|
+
/**
|
|
829
|
+
* Base worker class for doing async work that defers closing the database.
|
|
830
|
+
*/
|
|
831
|
+
struct PriorityWorker : public BaseWorker {
|
|
832
|
+
PriorityWorker (napi_env env, Database* database, napi_deferred deferred, const char* resourceName)
|
|
833
|
+
: BaseWorker(env, database, deferred, resourceName) {
|
|
834
|
+
database_->IncrementPriorityWork(env);
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
virtual ~PriorityWorker () {}
|
|
838
|
+
|
|
839
|
+
void DoFinally (napi_env env) override {
|
|
840
|
+
database_->DecrementPriorityWork(env);
|
|
841
|
+
BaseWorker::DoFinally(env);
|
|
842
|
+
}
|
|
843
|
+
};
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* Owns a leveldb iterator.
|
|
847
|
+
*/
|
|
848
|
+
struct BaseIterator {
|
|
849
|
+
BaseIterator(Database* database,
|
|
850
|
+
const bool reverse,
|
|
851
|
+
std::string* lt,
|
|
852
|
+
std::string* lte,
|
|
853
|
+
std::string* gt,
|
|
854
|
+
std::string* gte,
|
|
855
|
+
const int limit,
|
|
856
|
+
const bool fillCache,
|
|
857
|
+
ExplicitSnapshot* snapshot)
|
|
858
|
+
: database_(database),
|
|
859
|
+
hasClosed_(false),
|
|
860
|
+
didSeek_(false),
|
|
861
|
+
reverse_(reverse),
|
|
862
|
+
lt_(lt),
|
|
863
|
+
lte_(lte),
|
|
864
|
+
gt_(gt),
|
|
865
|
+
gte_(gte),
|
|
866
|
+
limit_(limit),
|
|
867
|
+
count_(0) {
|
|
868
|
+
options_ = new leveldb::ReadOptions();
|
|
869
|
+
options_->fill_cache = fillCache;
|
|
870
|
+
|
|
871
|
+
if (snapshot == NULL) {
|
|
872
|
+
implicitSnapshot_ = database_->NewSnapshot();
|
|
873
|
+
options_->snapshot = implicitSnapshot_;
|
|
874
|
+
} else {
|
|
875
|
+
implicitSnapshot_ = NULL;
|
|
876
|
+
options_->snapshot = snapshot->nut;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
dbIterator_ = database_->NewIterator(options_);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
virtual ~BaseIterator () {
|
|
883
|
+
assert(hasClosed_);
|
|
884
|
+
|
|
885
|
+
if (lt_ != NULL) delete lt_;
|
|
886
|
+
if (gt_ != NULL) delete gt_;
|
|
887
|
+
if (lte_ != NULL) delete lte_;
|
|
888
|
+
if (gte_ != NULL) delete gte_;
|
|
889
|
+
|
|
890
|
+
delete options_;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
bool DidSeek () const {
|
|
894
|
+
return didSeek_;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
/**
|
|
898
|
+
* Seek to the first relevant key based on range options.
|
|
899
|
+
*/
|
|
900
|
+
void SeekToRange () {
|
|
901
|
+
didSeek_ = true;
|
|
902
|
+
|
|
903
|
+
if (!reverse_ && gte_ != NULL) {
|
|
904
|
+
dbIterator_->Seek(*gte_);
|
|
905
|
+
} else if (!reverse_ && gt_ != NULL) {
|
|
906
|
+
dbIterator_->Seek(*gt_);
|
|
907
|
+
|
|
908
|
+
if (dbIterator_->Valid() && dbIterator_->key().compare(*gt_) == 0) {
|
|
909
|
+
dbIterator_->Next();
|
|
910
|
+
}
|
|
911
|
+
} else if (reverse_ && lte_ != NULL) {
|
|
912
|
+
dbIterator_->Seek(*lte_);
|
|
913
|
+
|
|
914
|
+
if (!dbIterator_->Valid()) {
|
|
915
|
+
dbIterator_->SeekToLast();
|
|
916
|
+
} else if (dbIterator_->key().compare(*lte_) > 0) {
|
|
917
|
+
dbIterator_->Prev();
|
|
918
|
+
}
|
|
919
|
+
} else if (reverse_ && lt_ != NULL) {
|
|
920
|
+
dbIterator_->Seek(*lt_);
|
|
921
|
+
|
|
922
|
+
if (!dbIterator_->Valid()) {
|
|
923
|
+
dbIterator_->SeekToLast();
|
|
924
|
+
} else if (dbIterator_->key().compare(*lt_) >= 0) {
|
|
925
|
+
dbIterator_->Prev();
|
|
926
|
+
}
|
|
927
|
+
} else if (reverse_) {
|
|
928
|
+
dbIterator_->SeekToLast();
|
|
929
|
+
} else {
|
|
930
|
+
dbIterator_->SeekToFirst();
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
/**
|
|
935
|
+
* Seek manually (during iteration).
|
|
936
|
+
*/
|
|
937
|
+
void Seek (leveldb::Slice& target) {
|
|
938
|
+
didSeek_ = true;
|
|
939
|
+
|
|
940
|
+
if (OutOfRange(target)) {
|
|
941
|
+
return SeekToEnd();
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
dbIterator_->Seek(target);
|
|
945
|
+
|
|
946
|
+
if (dbIterator_->Valid()) {
|
|
947
|
+
int cmp = dbIterator_->key().compare(target);
|
|
948
|
+
if (reverse_ ? cmp > 0 : cmp < 0) {
|
|
949
|
+
Next();
|
|
950
|
+
}
|
|
951
|
+
} else { // TODO: can we skip this code path if not in reverse?
|
|
952
|
+
SeekToFirst();
|
|
953
|
+
if (dbIterator_->Valid()) {
|
|
954
|
+
int cmp = dbIterator_->key().compare(target);
|
|
955
|
+
if (reverse_ ? cmp > 0 : cmp < 0) {
|
|
956
|
+
SeekToEnd();
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
/**
|
|
963
|
+
* Seek to an exact key.
|
|
964
|
+
*/
|
|
965
|
+
bool SeekExact (leveldb::Slice& target) {
|
|
966
|
+
didSeek_ = true;
|
|
967
|
+
dbIterator_->Seek(target);
|
|
968
|
+
return dbIterator_->Valid() && dbIterator_->key() == target;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
void CloseIterator () {
|
|
972
|
+
if (!hasClosed_) {
|
|
973
|
+
hasClosed_ = true;
|
|
974
|
+
|
|
975
|
+
delete dbIterator_;
|
|
976
|
+
dbIterator_ = NULL;
|
|
977
|
+
|
|
978
|
+
if (implicitSnapshot_) {
|
|
979
|
+
database_->ReleaseSnapshot(implicitSnapshot_);
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
bool Valid () const {
|
|
985
|
+
return dbIterator_->Valid() && !OutOfRange(dbIterator_->key());
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
bool Increment () {
|
|
989
|
+
return limit_ < 0 || ++count_ <= limit_;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
void Next () {
|
|
993
|
+
if (reverse_) dbIterator_->Prev();
|
|
994
|
+
else dbIterator_->Next();
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
void SeekToFirst () {
|
|
998
|
+
if (reverse_) dbIterator_->SeekToLast();
|
|
999
|
+
else dbIterator_->SeekToFirst();
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
void SeekToLast () {
|
|
1003
|
+
if (reverse_) dbIterator_->SeekToFirst();
|
|
1004
|
+
else dbIterator_->SeekToLast();
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
void SeekToEnd () {
|
|
1008
|
+
SeekToLast();
|
|
1009
|
+
Next();
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
leveldb::Slice CurrentKey () const {
|
|
1013
|
+
return dbIterator_->key();
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
leveldb::Slice CurrentValue () const {
|
|
1017
|
+
return dbIterator_->value();
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
leveldb::Status Status () const {
|
|
1021
|
+
return dbIterator_->status();
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
bool OutOfRange (const leveldb::Slice& target) const {
|
|
1025
|
+
// The lte and gte options take precedence over lt and gt respectively
|
|
1026
|
+
if (lte_ != NULL) {
|
|
1027
|
+
if (target.compare(*lte_) > 0) return true;
|
|
1028
|
+
} else if (lt_ != NULL) {
|
|
1029
|
+
if (target.compare(*lt_) >= 0) return true;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
if (gte_ != NULL) {
|
|
1033
|
+
if (target.compare(*gte_) < 0) return true;
|
|
1034
|
+
} else if (gt_ != NULL) {
|
|
1035
|
+
if (target.compare(*gt_) <= 0) return true;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
return false;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
Database* database_;
|
|
1042
|
+
bool hasClosed_;
|
|
1043
|
+
|
|
1044
|
+
private:
|
|
1045
|
+
leveldb::Iterator* dbIterator_;
|
|
1046
|
+
bool didSeek_;
|
|
1047
|
+
const bool reverse_;
|
|
1048
|
+
std::string* lt_;
|
|
1049
|
+
std::string* lte_;
|
|
1050
|
+
std::string* gt_;
|
|
1051
|
+
std::string* gte_;
|
|
1052
|
+
const int limit_;
|
|
1053
|
+
int count_;
|
|
1054
|
+
leveldb::ReadOptions* options_;
|
|
1055
|
+
const leveldb::Snapshot* implicitSnapshot_;
|
|
1056
|
+
};
|
|
1057
|
+
|
|
1058
|
+
/**
|
|
1059
|
+
* Extends BaseIterator for reading it from JS land.
|
|
1060
|
+
*/
|
|
1061
|
+
struct Iterator final : public BaseIterator, public Resource {
|
|
1062
|
+
Iterator (Database* database,
|
|
1063
|
+
const bool reverse,
|
|
1064
|
+
const bool keys,
|
|
1065
|
+
const bool values,
|
|
1066
|
+
const int limit,
|
|
1067
|
+
std::string* lt,
|
|
1068
|
+
std::string* lte,
|
|
1069
|
+
std::string* gt,
|
|
1070
|
+
std::string* gte,
|
|
1071
|
+
const bool fillCache,
|
|
1072
|
+
const Encoding keyEncoding,
|
|
1073
|
+
const Encoding valueEncoding,
|
|
1074
|
+
const uint32_t highWaterMarkBytes,
|
|
1075
|
+
unsigned char* state,
|
|
1076
|
+
ExplicitSnapshot* snapshot)
|
|
1077
|
+
: BaseIterator(database, reverse, lt, lte, gt, gte, limit, fillCache, snapshot),
|
|
1078
|
+
Resource(database),
|
|
1079
|
+
keys_(keys),
|
|
1080
|
+
values_(values),
|
|
1081
|
+
keyEncoding_(keyEncoding),
|
|
1082
|
+
valueEncoding_(valueEncoding),
|
|
1083
|
+
highWaterMarkBytes_(highWaterMarkBytes),
|
|
1084
|
+
first_(true),
|
|
1085
|
+
nexting_(false),
|
|
1086
|
+
aborted_(false),
|
|
1087
|
+
ended_(false),
|
|
1088
|
+
state_(state) {
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
~Iterator () {}
|
|
1092
|
+
|
|
1093
|
+
void CloseResource () override {
|
|
1094
|
+
BaseIterator::CloseIterator();
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
bool ReadMany (uint32_t size) {
|
|
1098
|
+
cache_.clear();
|
|
1099
|
+
cache_.reserve(size);
|
|
1100
|
+
size_t bytesRead = 0;
|
|
1101
|
+
leveldb::Slice empty;
|
|
1102
|
+
|
|
1103
|
+
while (!aborted_) {
|
|
1104
|
+
if (!first_) Next();
|
|
1105
|
+
else first_ = false;
|
|
1106
|
+
|
|
1107
|
+
if (!Valid() || !Increment()) break;
|
|
1108
|
+
|
|
1109
|
+
if (keys_ && values_) {
|
|
1110
|
+
leveldb::Slice k = CurrentKey();
|
|
1111
|
+
leveldb::Slice v = CurrentValue();
|
|
1112
|
+
cache_.emplace_back(k, v);
|
|
1113
|
+
bytesRead += k.size() + v.size();
|
|
1114
|
+
} else if (keys_) {
|
|
1115
|
+
leveldb::Slice k = CurrentKey();
|
|
1116
|
+
cache_.emplace_back(k, empty);
|
|
1117
|
+
bytesRead += k.size();
|
|
1118
|
+
} else if (values_) {
|
|
1119
|
+
leveldb::Slice v = CurrentValue();
|
|
1120
|
+
cache_.emplace_back(empty, v);
|
|
1121
|
+
bytesRead += v.size();
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
if (bytesRead > highWaterMarkBytes_ || cache_.size() >= size) {
|
|
1125
|
+
return true;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
ended_ = true;
|
|
1130
|
+
return false;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
const bool keys_;
|
|
1134
|
+
const bool values_;
|
|
1135
|
+
const Encoding keyEncoding_;
|
|
1136
|
+
const Encoding valueEncoding_;
|
|
1137
|
+
const uint32_t highWaterMarkBytes_;
|
|
1138
|
+
bool first_;
|
|
1139
|
+
bool nexting_;
|
|
1140
|
+
std::atomic<bool> aborted_;
|
|
1141
|
+
bool ended_;
|
|
1142
|
+
unsigned char* state_;
|
|
1143
|
+
std::vector<Entry> cache_;
|
|
1144
|
+
};
|
|
1145
|
+
|
|
1146
|
+
/**
|
|
1147
|
+
* Hook for when the environment exits. This hook will be called after
|
|
1148
|
+
* already-scheduled napi_async_work items have finished, which gives us
|
|
1149
|
+
* the guarantee that no db operations will be in-flight at this time.
|
|
1150
|
+
*/
|
|
1151
|
+
static void env_cleanup_hook (void* arg) {
|
|
1152
|
+
Database* database = (Database*)arg;
|
|
1153
|
+
|
|
1154
|
+
// Do everything that db.close() does but synchronously. We're expecting that GC
|
|
1155
|
+
// did not (yet) collect the database because that would be a user mistake (not
|
|
1156
|
+
// closing their db) made during the lifetime of the environment. That's different
|
|
1157
|
+
// from an environment being torn down (like the main process or a worker thread)
|
|
1158
|
+
// where it's our responsibility to clean up. Note also, the following code must
|
|
1159
|
+
// be a safe noop if called before db_open() or after db_close().
|
|
1160
|
+
if (database && database->db_ != NULL) {
|
|
1161
|
+
for (const auto& kv : database->resources_) {
|
|
1162
|
+
kv.second->CloseResource();
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
database->CloseDatabase();
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
/**
|
|
1170
|
+
* Runs when a Database is garbage collected.
|
|
1171
|
+
*/
|
|
1172
|
+
static void FinalizeDatabase (napi_env env, void* data, void* hint) {
|
|
1173
|
+
if (data) {
|
|
1174
|
+
Database* database = (Database*)data;
|
|
1175
|
+
napi_remove_env_cleanup_hook(env, env_cleanup_hook, database);
|
|
1176
|
+
database->DeleteReferences(env);
|
|
1177
|
+
delete database;
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
/**
|
|
1182
|
+
* Returns a context object for a database.
|
|
1183
|
+
*/
|
|
1184
|
+
NAPI_METHOD(db_init) {
|
|
1185
|
+
Database* database = new Database();
|
|
1186
|
+
napi_add_env_cleanup_hook(env, env_cleanup_hook, database);
|
|
1187
|
+
|
|
1188
|
+
napi_value result;
|
|
1189
|
+
NAPI_STATUS_THROWS(napi_create_external(env, database,
|
|
1190
|
+
FinalizeDatabase,
|
|
1191
|
+
NULL, &result));
|
|
1192
|
+
|
|
1193
|
+
// Prevent GC of database before close()
|
|
1194
|
+
NAPI_STATUS_THROWS(napi_create_reference(env, result, 1, &database->ref_));
|
|
1195
|
+
|
|
1196
|
+
return result;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
/**
|
|
1200
|
+
* Worker class for opening a database.
|
|
1201
|
+
* TODO: shouldn't this be a PriorityWorker?
|
|
1202
|
+
*/
|
|
1203
|
+
struct OpenWorker final : public BaseWorker {
|
|
1204
|
+
OpenWorker (napi_env env,
|
|
1205
|
+
Database* database,
|
|
1206
|
+
napi_deferred deferred,
|
|
1207
|
+
const std::string& location,
|
|
1208
|
+
const bool createIfMissing,
|
|
1209
|
+
const bool errorIfExists,
|
|
1210
|
+
const bool compression,
|
|
1211
|
+
const bool multithreading,
|
|
1212
|
+
const uint32_t writeBufferSize,
|
|
1213
|
+
const uint32_t blockSize,
|
|
1214
|
+
const uint32_t maxOpenFiles,
|
|
1215
|
+
const uint32_t blockRestartInterval,
|
|
1216
|
+
const uint32_t maxFileSize)
|
|
1217
|
+
: BaseWorker(env, database, deferred, "classic_level.db.open"),
|
|
1218
|
+
location_(location),
|
|
1219
|
+
multithreading_(multithreading) {
|
|
1220
|
+
options_.block_cache = database->blockCache_;
|
|
1221
|
+
options_.filter_policy = database->filterPolicy_;
|
|
1222
|
+
options_.create_if_missing = createIfMissing;
|
|
1223
|
+
options_.error_if_exists = errorIfExists;
|
|
1224
|
+
options_.compression = compression
|
|
1225
|
+
? leveldb::kSnappyCompression
|
|
1226
|
+
: leveldb::kNoCompression;
|
|
1227
|
+
options_.write_buffer_size = writeBufferSize;
|
|
1228
|
+
options_.block_size = blockSize;
|
|
1229
|
+
options_.max_open_files = maxOpenFiles;
|
|
1230
|
+
options_.block_restart_interval = blockRestartInterval;
|
|
1231
|
+
options_.max_file_size = maxFileSize;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
~OpenWorker () {}
|
|
1235
|
+
|
|
1236
|
+
void DoExecute () override {
|
|
1237
|
+
SetStatus(database_->Open(options_, location_, multithreading_));
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
leveldb::Options options_;
|
|
1241
|
+
std::string location_;
|
|
1242
|
+
bool multithreading_;
|
|
1243
|
+
};
|
|
1244
|
+
|
|
1245
|
+
/**
|
|
1246
|
+
* Open a database.
|
|
1247
|
+
*/
|
|
1248
|
+
NAPI_METHOD(db_open) {
|
|
1249
|
+
NAPI_ARGV(3);
|
|
1250
|
+
NAPI_DB_CONTEXT();
|
|
1251
|
+
NAPI_ARGV_UTF8_NEW(location, 1);
|
|
1252
|
+
NAPI_PROMISE();
|
|
1253
|
+
|
|
1254
|
+
napi_value options = argv[2];
|
|
1255
|
+
const bool createIfMissing = BooleanProperty(env, options, "createIfMissing", true);
|
|
1256
|
+
const bool errorIfExists = BooleanProperty(env, options, "errorIfExists", false);
|
|
1257
|
+
const bool compression = BooleanProperty(env, options, "compression", true);
|
|
1258
|
+
const bool multithreading = BooleanProperty(env, options, "multithreading", false);
|
|
1259
|
+
|
|
1260
|
+
const uint32_t cacheSize = Uint32Property(env, options, "cacheSize", 8 << 20);
|
|
1261
|
+
const uint32_t writeBufferSize = Uint32Property(env, options , "writeBufferSize" , 4 << 20);
|
|
1262
|
+
const uint32_t blockSize = Uint32Property(env, options, "blockSize", 4096);
|
|
1263
|
+
const uint32_t maxOpenFiles = Uint32Property(env, options, "maxOpenFiles", 1000);
|
|
1264
|
+
const uint32_t blockRestartInterval = Uint32Property(env, options,
|
|
1265
|
+
"blockRestartInterval", 16);
|
|
1266
|
+
const uint32_t maxFileSize = Uint32Property(env, options, "maxFileSize", 2 << 20);
|
|
1267
|
+
|
|
1268
|
+
database->blockCache_ = leveldb::NewLRUCache(cacheSize);
|
|
1269
|
+
|
|
1270
|
+
OpenWorker* worker = new OpenWorker(
|
|
1271
|
+
env, database, deferred, location,
|
|
1272
|
+
createIfMissing, errorIfExists,
|
|
1273
|
+
compression, multithreading,
|
|
1274
|
+
writeBufferSize, blockSize,
|
|
1275
|
+
maxOpenFiles, blockRestartInterval,
|
|
1276
|
+
maxFileSize
|
|
1277
|
+
);
|
|
1278
|
+
|
|
1279
|
+
worker->Queue(env);
|
|
1280
|
+
delete [] location;
|
|
1281
|
+
|
|
1282
|
+
return promise;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
/**
|
|
1286
|
+
* Worker class for closing a database
|
|
1287
|
+
*
|
|
1288
|
+
* TODO: once we've moved the PriorityWork logic to AbstractLevel, check if
|
|
1289
|
+
* CloseDatabase is fast enough to be done synchronously.
|
|
1290
|
+
*/
|
|
1291
|
+
struct CloseWorker final : public BaseWorker {
|
|
1292
|
+
CloseWorker (napi_env env, Database* database, napi_deferred deferred)
|
|
1293
|
+
: BaseWorker(env, database, deferred, "classic_level.db.close") {}
|
|
1294
|
+
|
|
1295
|
+
~CloseWorker () {}
|
|
1296
|
+
|
|
1297
|
+
void DoExecute () override {
|
|
1298
|
+
database_->CloseDatabase();
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
void DoFinally (napi_env env) override {
|
|
1302
|
+
database_->ReleaseReferences(env);
|
|
1303
|
+
BaseWorker::DoFinally(env);
|
|
1304
|
+
}
|
|
1305
|
+
};
|
|
1306
|
+
|
|
1307
|
+
napi_value noop_callback (napi_env env, napi_callback_info info) {
|
|
1308
|
+
return 0;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
/**
|
|
1312
|
+
* Close a database.
|
|
1313
|
+
*/
|
|
1314
|
+
NAPI_METHOD(db_close) {
|
|
1315
|
+
NAPI_ARGV(1);
|
|
1316
|
+
NAPI_DB_CONTEXT();
|
|
1317
|
+
NAPI_PROMISE();
|
|
1318
|
+
|
|
1319
|
+
// AbstractLevel should not call _close() before resources are closed
|
|
1320
|
+
assert(database->resources_.size() == 0);
|
|
1321
|
+
|
|
1322
|
+
CloseWorker* worker = new CloseWorker(env, database, deferred);
|
|
1323
|
+
|
|
1324
|
+
if (!database->HasPriorityWork()) {
|
|
1325
|
+
worker->Queue(env);
|
|
1326
|
+
} else {
|
|
1327
|
+
database->pendingCloseWorker_ = worker;
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
return promise;
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
/**
|
|
1334
|
+
* Worker class for putting key/value to the database
|
|
1335
|
+
*/
|
|
1336
|
+
struct PutWorker final : public PriorityWorker {
|
|
1337
|
+
PutWorker (napi_env env,
|
|
1338
|
+
Database* database,
|
|
1339
|
+
napi_deferred deferred,
|
|
1340
|
+
leveldb::Slice key,
|
|
1341
|
+
leveldb::Slice value,
|
|
1342
|
+
bool sync)
|
|
1343
|
+
: PriorityWorker(env, database, deferred, "classic_level.db.put"),
|
|
1344
|
+
key_(key), value_(value) {
|
|
1345
|
+
options_.sync = sync;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
~PutWorker () {
|
|
1349
|
+
DisposeSliceBuffer(key_);
|
|
1350
|
+
DisposeSliceBuffer(value_);
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
void DoExecute () override {
|
|
1354
|
+
SetStatus(database_->Put(options_, key_, value_));
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
leveldb::WriteOptions options_;
|
|
1358
|
+
leveldb::Slice key_;
|
|
1359
|
+
leveldb::Slice value_;
|
|
1360
|
+
};
|
|
1361
|
+
|
|
1362
|
+
/**
|
|
1363
|
+
* Puts a key and a value to a database.
|
|
1364
|
+
*/
|
|
1365
|
+
NAPI_METHOD(db_put) {
|
|
1366
|
+
NAPI_ARGV(4);
|
|
1367
|
+
NAPI_DB_CONTEXT();
|
|
1368
|
+
NAPI_PROMISE();
|
|
1369
|
+
|
|
1370
|
+
leveldb::Slice key = ToSlice(env, argv[1]);
|
|
1371
|
+
leveldb::Slice value = ToSlice(env, argv[2]);
|
|
1372
|
+
bool sync = BooleanProperty(env, argv[3], "sync", false);
|
|
1373
|
+
|
|
1374
|
+
PutWorker* worker = new PutWorker(env, database, deferred, key, value, sync);
|
|
1375
|
+
worker->Queue(env);
|
|
1376
|
+
|
|
1377
|
+
return promise;
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
/**
|
|
1381
|
+
* Worker class for getting a value from a database.
|
|
1382
|
+
*/
|
|
1383
|
+
struct GetWorker final : public PriorityWorker {
|
|
1384
|
+
GetWorker (napi_env env,
|
|
1385
|
+
Database* database,
|
|
1386
|
+
napi_deferred deferred,
|
|
1387
|
+
uint32_t flags,
|
|
1388
|
+
leveldb::Slice key,
|
|
1389
|
+
napi_ref keyRef,
|
|
1390
|
+
ExplicitSnapshot* snapshot)
|
|
1391
|
+
: PriorityWorker(env, database, deferred, "classic_level.db.get"),
|
|
1392
|
+
flags_(flags),
|
|
1393
|
+
key_(key),
|
|
1394
|
+
keyRef_(keyRef) {
|
|
1395
|
+
options_.fill_cache = (flags & Flags::FILL_CACHE) != 0;
|
|
1396
|
+
|
|
1397
|
+
if (snapshot == NULL) {
|
|
1398
|
+
implicitSnapshot_ = database->NewSnapshot();
|
|
1399
|
+
options_.snapshot = implicitSnapshot_;
|
|
1400
|
+
} else {
|
|
1401
|
+
implicitSnapshot_ = NULL;
|
|
1402
|
+
options_.snapshot = snapshot->nut;
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
~GetWorker () {
|
|
1407
|
+
if (!keyRef_) DisposeSliceBuffer(key_);
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
void DoExecute () override {
|
|
1411
|
+
leveldb::StringValueSink wrapped(&value_);
|
|
1412
|
+
SetStatus(database_->Get(options_, key_, wrapped));
|
|
1413
|
+
|
|
1414
|
+
if (implicitSnapshot_) {
|
|
1415
|
+
database_->ReleaseSnapshot(implicitSnapshot_);
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
void DoFinally (napi_env env) override {
|
|
1420
|
+
if (keyRef_) napi_delete_reference(env, keyRef_);
|
|
1421
|
+
PriorityWorker::DoFinally(env);
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
void HandleOKCallback (napi_env env, napi_deferred deferred) override {
|
|
1425
|
+
napi_value argv;
|
|
1426
|
+
|
|
1427
|
+
if ((flags_ & Flags::VALUE_AS_BUFFER) != 0) {
|
|
1428
|
+
napi_create_buffer_copy(env, value_.size(), value_.data(), NULL, &argv);
|
|
1429
|
+
} else {
|
|
1430
|
+
napi_create_string_utf8(env, value_.data(), value_.size(), &argv);
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
napi_resolve_deferred(env, deferred, argv);
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
private:
|
|
1437
|
+
leveldb::ReadOptions options_;
|
|
1438
|
+
uint32_t flags_;
|
|
1439
|
+
leveldb::Slice key_;
|
|
1440
|
+
napi_ref keyRef_;
|
|
1441
|
+
std::string value_;
|
|
1442
|
+
const leveldb::Snapshot* implicitSnapshot_;
|
|
1443
|
+
};
|
|
1444
|
+
|
|
1445
|
+
/**
|
|
1446
|
+
* Gets a value from a database.
|
|
1447
|
+
*/
|
|
1448
|
+
NAPI_METHOD(db_get) {
|
|
1449
|
+
NAPI_ARGV(4);
|
|
1450
|
+
NAPI_DB_CONTEXT();
|
|
1451
|
+
NAPI_PROMISE();
|
|
1452
|
+
|
|
1453
|
+
uint32_t flags;
|
|
1454
|
+
NAPI_STATUS_THROWS(napi_get_value_uint32(env, argv[1], &flags));
|
|
1455
|
+
|
|
1456
|
+
ExplicitSnapshot* snapshot = NULL;
|
|
1457
|
+
napi_get_value_external(env, argv[3], (void**)&snapshot);
|
|
1458
|
+
|
|
1459
|
+
char* keyBuffer;
|
|
1460
|
+
size_t keySize;
|
|
1461
|
+
GetWorker* worker;
|
|
1462
|
+
|
|
1463
|
+
if ((flags & Flags::KEY_AS_BUFFER) != 0) {
|
|
1464
|
+
// Instead of copying the memory, create a reference so that it stays valid
|
|
1465
|
+
napi_ref keyRef;
|
|
1466
|
+
NAPI_STATUS_THROWS(napi_create_reference(env, argv[2], 1, &keyRef));
|
|
1467
|
+
NAPI_STATUS_THROWS(napi_get_typedarray_info(env, argv[2], NULL, &keySize, (void**)&keyBuffer, NULL, NULL));
|
|
1468
|
+
leveldb::Slice keySlice(keyBuffer, keySize);
|
|
1469
|
+
worker = new GetWorker(env, database, deferred, flags, keySlice, keyRef, snapshot);
|
|
1470
|
+
} else {
|
|
1471
|
+
NAPI_STATUS_THROWS(napi_get_value_string_utf8(env, argv[2], NULL, 0, &keySize));
|
|
1472
|
+
keyBuffer = new char[keySize + 1];
|
|
1473
|
+
NAPI_STATUS_THROWS(napi_get_value_string_utf8(env, argv[2], keyBuffer, keySize + 1, NULL));
|
|
1474
|
+
keyBuffer[keySize] = '\0';
|
|
1475
|
+
leveldb::Slice keySlice(keyBuffer, keySize);
|
|
1476
|
+
|
|
1477
|
+
// A null keyRef implies that keyBuffer needs to be deleted after the read
|
|
1478
|
+
// TODO: solve in a more obvious way like a subclass
|
|
1479
|
+
worker = new GetWorker(env, database, deferred, flags, keySlice, NULL, snapshot);
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
worker->Queue(env);
|
|
1483
|
+
return promise;
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
struct NapiValueSink : public leveldb::ValueSink {
|
|
1487
|
+
NapiValueSink (napi_env env)
|
|
1488
|
+
: leveldb::ValueSink(),
|
|
1489
|
+
result(NULL),
|
|
1490
|
+
env_(env),
|
|
1491
|
+
status_(napi_generic_failure) {}
|
|
1492
|
+
|
|
1493
|
+
virtual ~NapiValueSink() = default;
|
|
1494
|
+
|
|
1495
|
+
const bool valid() {
|
|
1496
|
+
return status_ == napi_ok;
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
napi_value result;
|
|
1500
|
+
|
|
1501
|
+
protected:
|
|
1502
|
+
napi_env env_;
|
|
1503
|
+
napi_status status_;
|
|
1504
|
+
};
|
|
1505
|
+
|
|
1506
|
+
struct NapiStringValueSink : public NapiValueSink {
|
|
1507
|
+
NapiStringValueSink (napi_env env)
|
|
1508
|
+
: NapiValueSink(env) {}
|
|
1509
|
+
|
|
1510
|
+
public:
|
|
1511
|
+
void assign(const char* data, size_t size) override {
|
|
1512
|
+
status_ = napi_create_string_utf8(env_, data, size, &result);
|
|
1513
|
+
}
|
|
1514
|
+
};
|
|
1515
|
+
|
|
1516
|
+
struct NapiBufferValueSink : public NapiValueSink {
|
|
1517
|
+
NapiBufferValueSink (napi_env env)
|
|
1518
|
+
: NapiValueSink(env) {}
|
|
1519
|
+
|
|
1520
|
+
public:
|
|
1521
|
+
void assign(const char* data, size_t size) override {
|
|
1522
|
+
status_ = napi_create_buffer_copy(env_, size, data, NULL, &result);
|
|
1523
|
+
}
|
|
1524
|
+
};
|
|
1525
|
+
|
|
1526
|
+
/**
|
|
1527
|
+
* Get a value from a database synchronously.
|
|
1528
|
+
*/
|
|
1529
|
+
NAPI_METHOD(db_get_sync) {
|
|
1530
|
+
NAPI_ARGV(4);
|
|
1531
|
+
NAPI_DB_CONTEXT();
|
|
1532
|
+
|
|
1533
|
+
uint32_t flags;
|
|
1534
|
+
NAPI_STATUS_THROWS(napi_get_value_uint32(env, argv[1], &flags));
|
|
1535
|
+
|
|
1536
|
+
std::optional<leveldb::Slice> keySlice;
|
|
1537
|
+
|
|
1538
|
+
if ((flags & Flags::SHARED_KEY) != 0) {
|
|
1539
|
+
uint32_t keySize;
|
|
1540
|
+
NAPI_STATUS_THROWS(napi_get_value_uint32(env, argv[2], &keySize));
|
|
1541
|
+
keySlice.emplace(database->sharedBuffer_, keySize);
|
|
1542
|
+
} else {
|
|
1543
|
+
char* keyBuffer;
|
|
1544
|
+
size_t keySize;
|
|
1545
|
+
NAPI_STATUS_THROWS(napi_get_typedarray_info(env, argv[2], NULL, &keySize, (void**)&keyBuffer, NULL, NULL));
|
|
1546
|
+
keySlice.emplace(keyBuffer, keySize);
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
ExplicitSnapshot* snapshot = NULL;
|
|
1550
|
+
napi_get_value_external(env, argv[3], (void**)&snapshot);
|
|
1551
|
+
|
|
1552
|
+
leveldb::ReadOptions options;
|
|
1553
|
+
options.fill_cache = (flags & Flags::FILL_CACHE) != 0;
|
|
1554
|
+
options.snapshot = snapshot != NULL ? snapshot->nut : NULL;
|
|
1555
|
+
|
|
1556
|
+
if ((flags & Flags::VALUE_AS_BUFFER) != 0) {
|
|
1557
|
+
NapiBufferValueSink valueSink(env);
|
|
1558
|
+
leveldb::Status status = database->Get(options, *keySlice, valueSink);
|
|
1559
|
+
return status.ok() && valueSink.valid() ? valueSink.result : ErrorOrNotFound(env, status);
|
|
1560
|
+
} else {
|
|
1561
|
+
NapiStringValueSink valueSink(env);
|
|
1562
|
+
leveldb::Status status = database->Get(options, *keySlice, valueSink);
|
|
1563
|
+
return status.ok() && valueSink.valid() ? valueSink.result : ErrorOrNotFound(env, status);
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
NAPI_METHOD(db_set_shared_buffer) {
|
|
1568
|
+
NAPI_ARGV(2);
|
|
1569
|
+
NAPI_DB_CONTEXT();
|
|
1570
|
+
|
|
1571
|
+
if (!database->SetSharedBuffer(env, argv[1])) {
|
|
1572
|
+
napi_throw_error(env, NULL, "SetSharedBuffer failed");
|
|
1573
|
+
return undefined;
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
return undefined;
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
/**
|
|
1580
|
+
* Worker class for db.has().
|
|
1581
|
+
*/
|
|
1582
|
+
struct HasWorker final : public PriorityWorker {
|
|
1583
|
+
HasWorker(
|
|
1584
|
+
napi_env env,
|
|
1585
|
+
Database* database,
|
|
1586
|
+
napi_deferred deferred,
|
|
1587
|
+
leveldb::Slice key,
|
|
1588
|
+
const bool fillCache,
|
|
1589
|
+
ExplicitSnapshot* snapshot
|
|
1590
|
+
) : PriorityWorker(env, database, deferred, "classic_level.db.has"),
|
|
1591
|
+
key_(key) {
|
|
1592
|
+
iterator_ = new BaseIterator(
|
|
1593
|
+
database,
|
|
1594
|
+
// Range options (not relevant)
|
|
1595
|
+
false, NULL, NULL, NULL, NULL, -1,
|
|
1596
|
+
fillCache,
|
|
1597
|
+
snapshot
|
|
1598
|
+
);
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
~HasWorker () {
|
|
1602
|
+
DisposeSliceBuffer(key_);
|
|
1603
|
+
delete iterator_;
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
void DoExecute () override {
|
|
1607
|
+
// LevelDB has no Has() method so use an iterator
|
|
1608
|
+
result_ = iterator_->SeekExact(key_);
|
|
1609
|
+
SetStatus(iterator_->Status());
|
|
1610
|
+
iterator_->CloseIterator();
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
void HandleOKCallback (napi_env env, napi_deferred deferred) override {
|
|
1614
|
+
napi_value resultBoolean;
|
|
1615
|
+
napi_get_boolean(env, result_, &resultBoolean);
|
|
1616
|
+
napi_resolve_deferred(env, deferred, resultBoolean);
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
private:
|
|
1620
|
+
leveldb::Slice key_;
|
|
1621
|
+
BaseIterator* iterator_;
|
|
1622
|
+
bool result_;
|
|
1623
|
+
};
|
|
1624
|
+
|
|
1625
|
+
/**
|
|
1626
|
+
* Check if the database has an entry with the given key.
|
|
1627
|
+
*/
|
|
1628
|
+
NAPI_METHOD(db_has) {
|
|
1629
|
+
NAPI_ARGV(4);
|
|
1630
|
+
NAPI_DB_CONTEXT();
|
|
1631
|
+
NAPI_PROMISE();
|
|
1632
|
+
|
|
1633
|
+
leveldb::Slice key = ToSlice(env, argv[1]);
|
|
1634
|
+
const bool fillCache = BooleanValue(env, argv[2], true);
|
|
1635
|
+
|
|
1636
|
+
ExplicitSnapshot* snapshot = NULL;
|
|
1637
|
+
napi_get_value_external(env, argv[3], (void**)&snapshot);
|
|
1638
|
+
|
|
1639
|
+
HasWorker* worker = new HasWorker(
|
|
1640
|
+
env, database, deferred, key, fillCache, snapshot
|
|
1641
|
+
);
|
|
1642
|
+
|
|
1643
|
+
worker->Queue(env);
|
|
1644
|
+
return promise;
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
/**
|
|
1648
|
+
* Worker class for getting many values.
|
|
1649
|
+
*/
|
|
1650
|
+
struct GetManyWorker final : public PriorityWorker {
|
|
1651
|
+
GetManyWorker (napi_env env,
|
|
1652
|
+
Database* database,
|
|
1653
|
+
std::vector<std::string> keys,
|
|
1654
|
+
napi_deferred deferred,
|
|
1655
|
+
const Encoding valueEncoding,
|
|
1656
|
+
const bool fillCache,
|
|
1657
|
+
ExplicitSnapshot* snapshot)
|
|
1658
|
+
: PriorityWorker(env, database, deferred, "classic_level.get.many"),
|
|
1659
|
+
keys_(std::move(keys)), valueEncoding_(valueEncoding) {
|
|
1660
|
+
options_.fill_cache = fillCache;
|
|
1661
|
+
|
|
1662
|
+
if (snapshot == NULL) {
|
|
1663
|
+
implicitSnapshot_ = database->NewSnapshot();
|
|
1664
|
+
options_.snapshot = implicitSnapshot_;
|
|
1665
|
+
} else {
|
|
1666
|
+
implicitSnapshot_ = NULL;
|
|
1667
|
+
options_.snapshot = snapshot->nut;
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
void DoExecute () override {
|
|
1672
|
+
cache_.reserve(keys_.size());
|
|
1673
|
+
|
|
1674
|
+
for (const std::string& key: keys_) {
|
|
1675
|
+
std::string* value = new std::string();
|
|
1676
|
+
leveldb::StringValueSink wrapped(value);
|
|
1677
|
+
leveldb::Status status = database_->Get(options_, key, wrapped);
|
|
1678
|
+
|
|
1679
|
+
if (status.ok()) {
|
|
1680
|
+
cache_.push_back(value);
|
|
1681
|
+
} else if (status.IsNotFound()) {
|
|
1682
|
+
delete value;
|
|
1683
|
+
cache_.push_back(NULL);
|
|
1684
|
+
} else {
|
|
1685
|
+
delete value;
|
|
1686
|
+
for (const std::string* value: cache_) {
|
|
1687
|
+
if (value != NULL) delete value;
|
|
1688
|
+
}
|
|
1689
|
+
SetStatus(status);
|
|
1690
|
+
break;
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
if (implicitSnapshot_) {
|
|
1695
|
+
database_->ReleaseSnapshot(implicitSnapshot_);
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
void HandleOKCallback (napi_env env, napi_deferred deferred) override {
|
|
1700
|
+
size_t size = cache_.size();
|
|
1701
|
+
napi_value array;
|
|
1702
|
+
napi_create_array_with_length(env, size, &array);
|
|
1703
|
+
|
|
1704
|
+
for (size_t idx = 0; idx < size; idx++) {
|
|
1705
|
+
std::string* value = cache_[idx];
|
|
1706
|
+
napi_value element;
|
|
1707
|
+
Entry::Convert(env, value, valueEncoding_, element);
|
|
1708
|
+
napi_set_element(env, array, static_cast<uint32_t>(idx), element);
|
|
1709
|
+
if (value != NULL) delete value;
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
napi_resolve_deferred(env, deferred, array);
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
private:
|
|
1716
|
+
leveldb::ReadOptions options_;
|
|
1717
|
+
const std::vector<std::string> keys_;
|
|
1718
|
+
const Encoding valueEncoding_;
|
|
1719
|
+
std::vector<std::string*> cache_;
|
|
1720
|
+
const leveldb::Snapshot* implicitSnapshot_;
|
|
1721
|
+
};
|
|
1722
|
+
|
|
1723
|
+
/**
|
|
1724
|
+
* Gets many values from a database.
|
|
1725
|
+
*/
|
|
1726
|
+
NAPI_METHOD(db_get_many) {
|
|
1727
|
+
NAPI_ARGV(4);
|
|
1728
|
+
NAPI_DB_CONTEXT();
|
|
1729
|
+
NAPI_PROMISE();
|
|
1730
|
+
|
|
1731
|
+
const auto keys = KeyArray(env, argv[1]);
|
|
1732
|
+
napi_value options = argv[2];
|
|
1733
|
+
const Encoding valueEncoding = GetEncoding(env, options, "valueEncoding");
|
|
1734
|
+
const bool fillCache = BooleanProperty(env, options, "fillCache", true);
|
|
1735
|
+
|
|
1736
|
+
ExplicitSnapshot* snapshot = NULL;
|
|
1737
|
+
napi_get_value_external(env, argv[3], (void**)&snapshot);
|
|
1738
|
+
|
|
1739
|
+
GetManyWorker* worker = new GetManyWorker(
|
|
1740
|
+
env,
|
|
1741
|
+
database,
|
|
1742
|
+
keys,
|
|
1743
|
+
deferred,
|
|
1744
|
+
valueEncoding,
|
|
1745
|
+
fillCache,
|
|
1746
|
+
snapshot
|
|
1747
|
+
);
|
|
1748
|
+
|
|
1749
|
+
worker->Queue(env);
|
|
1750
|
+
return promise;
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
/**
|
|
1754
|
+
* Worker class for db.hasMany().
|
|
1755
|
+
*/
|
|
1756
|
+
struct HasManyWorker final : public PriorityWorker {
|
|
1757
|
+
HasManyWorker(
|
|
1758
|
+
napi_env env,
|
|
1759
|
+
Database* database,
|
|
1760
|
+
std::vector<std::string> keys,
|
|
1761
|
+
napi_deferred deferred,
|
|
1762
|
+
uint32_t* bitset,
|
|
1763
|
+
const bool fillCache,
|
|
1764
|
+
ExplicitSnapshot* snapshot
|
|
1765
|
+
) : PriorityWorker(env, database, deferred, "classic_level.has.many"),
|
|
1766
|
+
keys_(std::move(keys)),
|
|
1767
|
+
bitset_(bitset) {
|
|
1768
|
+
iterator_ = new BaseIterator(
|
|
1769
|
+
database,
|
|
1770
|
+
// Range options (not relevant)
|
|
1771
|
+
false, NULL, NULL, NULL, NULL, -1,
|
|
1772
|
+
fillCache,
|
|
1773
|
+
snapshot
|
|
1774
|
+
);
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
~HasManyWorker () {
|
|
1778
|
+
delete iterator_;
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
void DoExecute () override {
|
|
1782
|
+
for (size_t i = 0; i != keys_.size(); i++) {
|
|
1783
|
+
leveldb::Slice target = leveldb::Slice(keys_[i]);
|
|
1784
|
+
|
|
1785
|
+
if (iterator_->SeekExact(target)) {
|
|
1786
|
+
bitset_[i >> 5] |= 1 << (i & 31); // Set bit
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
SetStatus(iterator_->Status());
|
|
1791
|
+
iterator_->CloseIterator();
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
private:
|
|
1795
|
+
const std::vector<std::string> keys_;
|
|
1796
|
+
uint32_t* bitset_;
|
|
1797
|
+
BaseIterator* iterator_;
|
|
1798
|
+
};
|
|
1799
|
+
|
|
1800
|
+
/**
|
|
1801
|
+
* Check if the database has entries with the given keys.
|
|
1802
|
+
*/
|
|
1803
|
+
NAPI_METHOD(db_has_many) {
|
|
1804
|
+
NAPI_ARGV(5);
|
|
1805
|
+
NAPI_DB_CONTEXT();
|
|
1806
|
+
NAPI_PROMISE();
|
|
1807
|
+
|
|
1808
|
+
const auto keys = KeyArray(env, argv[1]);
|
|
1809
|
+
const bool fillCache = BooleanValue(env, argv[2], true);
|
|
1810
|
+
|
|
1811
|
+
ExplicitSnapshot* snapshot = NULL;
|
|
1812
|
+
napi_get_value_external(env, argv[3], (void**)&snapshot);
|
|
1813
|
+
|
|
1814
|
+
uint32_t* bitset = NULL;
|
|
1815
|
+
NAPI_STATUS_THROWS(napi_get_arraybuffer_info(env, argv[4], (void**)&bitset, NULL));
|
|
1816
|
+
|
|
1817
|
+
HasManyWorker* worker = new HasManyWorker(
|
|
1818
|
+
env, database, keys, deferred, bitset, fillCache, snapshot
|
|
1819
|
+
);
|
|
1820
|
+
|
|
1821
|
+
worker->Queue(env);
|
|
1822
|
+
return promise;
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
/**
|
|
1826
|
+
* Worker class for deleting a value from a database.
|
|
1827
|
+
*/
|
|
1828
|
+
struct DelWorker final : public PriorityWorker {
|
|
1829
|
+
DelWorker (napi_env env,
|
|
1830
|
+
Database* database,
|
|
1831
|
+
napi_deferred deferred,
|
|
1832
|
+
leveldb::Slice key,
|
|
1833
|
+
bool sync)
|
|
1834
|
+
: PriorityWorker(env, database, deferred, "classic_level.db.del"),
|
|
1835
|
+
key_(key) {
|
|
1836
|
+
options_.sync = sync;
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
~DelWorker () {
|
|
1840
|
+
DisposeSliceBuffer(key_);
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
void DoExecute () override {
|
|
1844
|
+
SetStatus(database_->Del(options_, key_));
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
leveldb::WriteOptions options_;
|
|
1848
|
+
leveldb::Slice key_;
|
|
1849
|
+
};
|
|
1850
|
+
|
|
1851
|
+
/**
|
|
1852
|
+
* Delete a value from a database.
|
|
1853
|
+
*/
|
|
1854
|
+
NAPI_METHOD(db_del) {
|
|
1855
|
+
NAPI_ARGV(3);
|
|
1856
|
+
NAPI_DB_CONTEXT();
|
|
1857
|
+
NAPI_PROMISE();
|
|
1858
|
+
|
|
1859
|
+
leveldb::Slice key = ToSlice(env, argv[1]);
|
|
1860
|
+
bool sync = BooleanProperty(env, argv[2], "sync", false);
|
|
1861
|
+
|
|
1862
|
+
DelWorker* worker = new DelWorker(env, database, deferred, key, sync);
|
|
1863
|
+
worker->Queue(env);
|
|
1864
|
+
|
|
1865
|
+
return promise;
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
/**
|
|
1869
|
+
* Worker class for deleting a range from a database.
|
|
1870
|
+
*/
|
|
1871
|
+
struct ClearWorker final : public PriorityWorker {
|
|
1872
|
+
ClearWorker (napi_env env,
|
|
1873
|
+
Database* database,
|
|
1874
|
+
napi_deferred deferred,
|
|
1875
|
+
const bool reverse,
|
|
1876
|
+
const int limit,
|
|
1877
|
+
std::string* lt,
|
|
1878
|
+
std::string* lte,
|
|
1879
|
+
std::string* gt,
|
|
1880
|
+
std::string* gte,
|
|
1881
|
+
ExplicitSnapshot* snapshot)
|
|
1882
|
+
: PriorityWorker(env, database, deferred, "classic_level.db.clear") {
|
|
1883
|
+
iterator_ = new BaseIterator(database, reverse, lt, lte, gt, gte, limit, false, snapshot);
|
|
1884
|
+
writeOptions_ = new leveldb::WriteOptions();
|
|
1885
|
+
writeOptions_->sync = false;
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
~ClearWorker () {
|
|
1889
|
+
delete iterator_;
|
|
1890
|
+
delete writeOptions_;
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
void DoExecute () override {
|
|
1894
|
+
iterator_->SeekToRange();
|
|
1895
|
+
|
|
1896
|
+
// TODO: add option
|
|
1897
|
+
uint32_t hwm = 16 * 1024;
|
|
1898
|
+
leveldb::WriteBatch batch;
|
|
1899
|
+
|
|
1900
|
+
while (true) {
|
|
1901
|
+
size_t bytesRead = 0;
|
|
1902
|
+
|
|
1903
|
+
while (bytesRead <= hwm && iterator_->Valid() && iterator_->Increment()) {
|
|
1904
|
+
leveldb::Slice key = iterator_->CurrentKey();
|
|
1905
|
+
batch.Delete(key);
|
|
1906
|
+
bytesRead += key.size();
|
|
1907
|
+
iterator_->Next();
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
if (!SetStatus(iterator_->Status()) || bytesRead == 0) {
|
|
1911
|
+
break;
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
if (!SetStatus(database_->WriteBatch(*writeOptions_, &batch))) {
|
|
1915
|
+
break;
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
batch.Clear();
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
iterator_->CloseIterator();
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
private:
|
|
1925
|
+
BaseIterator* iterator_;
|
|
1926
|
+
leveldb::WriteOptions* writeOptions_;
|
|
1927
|
+
};
|
|
1928
|
+
|
|
1929
|
+
/**
|
|
1930
|
+
* Delete a range from a database.
|
|
1931
|
+
*/
|
|
1932
|
+
NAPI_METHOD(db_clear) {
|
|
1933
|
+
NAPI_ARGV(3);
|
|
1934
|
+
NAPI_DB_CONTEXT();
|
|
1935
|
+
NAPI_PROMISE();
|
|
1936
|
+
|
|
1937
|
+
napi_value options = argv[1];
|
|
1938
|
+
|
|
1939
|
+
const bool reverse = BooleanProperty(env, options, "reverse", false);
|
|
1940
|
+
const int limit = Int32Property(env, options, "limit", -1);
|
|
1941
|
+
|
|
1942
|
+
std::string* lt = RangeOption(env, options, "lt");
|
|
1943
|
+
std::string* lte = RangeOption(env, options, "lte");
|
|
1944
|
+
std::string* gt = RangeOption(env, options, "gt");
|
|
1945
|
+
std::string* gte = RangeOption(env, options, "gte");
|
|
1946
|
+
|
|
1947
|
+
ExplicitSnapshot* snapshot = NULL;
|
|
1948
|
+
napi_get_value_external(env, argv[2], (void**)&snapshot);
|
|
1949
|
+
|
|
1950
|
+
ClearWorker* worker = new ClearWorker(
|
|
1951
|
+
env, database, deferred, reverse, limit, lt, lte, gt, gte, snapshot
|
|
1952
|
+
);
|
|
1953
|
+
|
|
1954
|
+
worker->Queue(env);
|
|
1955
|
+
return promise;
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
/**
|
|
1959
|
+
* Worker class for calculating the size of a range.
|
|
1960
|
+
*/
|
|
1961
|
+
struct ApproximateSizeWorker final : public PriorityWorker {
|
|
1962
|
+
ApproximateSizeWorker (napi_env env,
|
|
1963
|
+
Database* database,
|
|
1964
|
+
napi_deferred deferred,
|
|
1965
|
+
leveldb::Slice start,
|
|
1966
|
+
leveldb::Slice end)
|
|
1967
|
+
: PriorityWorker(env, database, deferred, "classic_level.db.approximate_size"),
|
|
1968
|
+
start_(start), end_(end) {}
|
|
1969
|
+
|
|
1970
|
+
~ApproximateSizeWorker () {
|
|
1971
|
+
DisposeSliceBuffer(start_);
|
|
1972
|
+
DisposeSliceBuffer(end_);
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
void DoExecute () override {
|
|
1976
|
+
leveldb::Range range(start_, end_);
|
|
1977
|
+
size_ = database_->ApproximateSize(&range);
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
void HandleOKCallback (napi_env env, napi_deferred deferred) override {
|
|
1981
|
+
napi_value argv;
|
|
1982
|
+
napi_create_int64(env, (uint64_t)size_, &argv);
|
|
1983
|
+
napi_resolve_deferred(env, deferred, argv);
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
leveldb::Slice start_;
|
|
1987
|
+
leveldb::Slice end_;
|
|
1988
|
+
uint64_t size_;
|
|
1989
|
+
};
|
|
1990
|
+
|
|
1991
|
+
/**
|
|
1992
|
+
* Calculates the approximate size of a range in a database.
|
|
1993
|
+
*/
|
|
1994
|
+
NAPI_METHOD(db_approximate_size) {
|
|
1995
|
+
NAPI_ARGV(3);
|
|
1996
|
+
NAPI_DB_CONTEXT();
|
|
1997
|
+
NAPI_PROMISE();
|
|
1998
|
+
|
|
1999
|
+
leveldb::Slice start = ToSlice(env, argv[1]);
|
|
2000
|
+
leveldb::Slice end = ToSlice(env, argv[2]);
|
|
2001
|
+
|
|
2002
|
+
ApproximateSizeWorker* worker = new ApproximateSizeWorker(
|
|
2003
|
+
env, database, deferred, start, end
|
|
2004
|
+
);
|
|
2005
|
+
|
|
2006
|
+
worker->Queue(env);
|
|
2007
|
+
return promise;
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
/**
|
|
2011
|
+
* Worker class for compacting a range in a database.
|
|
2012
|
+
*/
|
|
2013
|
+
struct CompactRangeWorker final : public PriorityWorker {
|
|
2014
|
+
CompactRangeWorker (napi_env env,
|
|
2015
|
+
Database* database,
|
|
2016
|
+
napi_deferred deferred,
|
|
2017
|
+
leveldb::Slice start,
|
|
2018
|
+
leveldb::Slice end)
|
|
2019
|
+
: PriorityWorker(env, database, deferred, "classic_level.db.compact_range"),
|
|
2020
|
+
start_(start), end_(end) {}
|
|
2021
|
+
|
|
2022
|
+
~CompactRangeWorker () {
|
|
2023
|
+
DisposeSliceBuffer(start_);
|
|
2024
|
+
DisposeSliceBuffer(end_);
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
void DoExecute () override {
|
|
2028
|
+
database_->CompactRange(&start_, &end_);
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
leveldb::Slice start_;
|
|
2032
|
+
leveldb::Slice end_;
|
|
2033
|
+
};
|
|
2034
|
+
|
|
2035
|
+
/**
|
|
2036
|
+
* Compacts a range in a database.
|
|
2037
|
+
*/
|
|
2038
|
+
NAPI_METHOD(db_compact_range) {
|
|
2039
|
+
NAPI_ARGV(3);
|
|
2040
|
+
NAPI_DB_CONTEXT();
|
|
2041
|
+
NAPI_PROMISE();
|
|
2042
|
+
|
|
2043
|
+
leveldb::Slice start = ToSlice(env, argv[1]);
|
|
2044
|
+
leveldb::Slice end = ToSlice(env, argv[2]);
|
|
2045
|
+
|
|
2046
|
+
CompactRangeWorker* worker = new CompactRangeWorker(
|
|
2047
|
+
env, database, deferred, start, end
|
|
2048
|
+
);
|
|
2049
|
+
|
|
2050
|
+
worker->Queue(env);
|
|
2051
|
+
return promise;
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
/**
|
|
2055
|
+
* Get a property from a database.
|
|
2056
|
+
*/
|
|
2057
|
+
NAPI_METHOD(db_get_property) {
|
|
2058
|
+
NAPI_ARGV(2);
|
|
2059
|
+
NAPI_DB_CONTEXT();
|
|
2060
|
+
|
|
2061
|
+
leveldb::Slice property = ToSlice(env, argv[1]);
|
|
2062
|
+
|
|
2063
|
+
std::string value;
|
|
2064
|
+
database->GetProperty(property, &value);
|
|
2065
|
+
|
|
2066
|
+
napi_value result;
|
|
2067
|
+
napi_create_string_utf8(env, value.data(), value.size(), &result);
|
|
2068
|
+
|
|
2069
|
+
DisposeSliceBuffer(property);
|
|
2070
|
+
|
|
2071
|
+
return result;
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
/**
|
|
2075
|
+
* Worker class for destroying a database.
|
|
2076
|
+
*/
|
|
2077
|
+
struct DestroyWorker final : public BaseWorker {
|
|
2078
|
+
DestroyWorker (napi_env env, const std::string& location, napi_deferred deferred)
|
|
2079
|
+
: BaseWorker(env, NULL, deferred, "classic_level.destroy_db"),
|
|
2080
|
+
location_(location) {}
|
|
2081
|
+
|
|
2082
|
+
~DestroyWorker () {}
|
|
2083
|
+
|
|
2084
|
+
void DoExecute () override {
|
|
2085
|
+
leveldb::Options options;
|
|
2086
|
+
SetStatus(leveldb::DestroyDB(location_, options));
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
std::string location_;
|
|
2090
|
+
};
|
|
2091
|
+
|
|
2092
|
+
/**
|
|
2093
|
+
* Destroys a database.
|
|
2094
|
+
*/
|
|
2095
|
+
NAPI_METHOD(destroy_db) {
|
|
2096
|
+
NAPI_ARGV(1);
|
|
2097
|
+
NAPI_ARGV_UTF8_NEW(location, 0);
|
|
2098
|
+
NAPI_PROMISE();
|
|
2099
|
+
|
|
2100
|
+
DestroyWorker* worker = new DestroyWorker(env, location, deferred);
|
|
2101
|
+
worker->Queue(env);
|
|
2102
|
+
|
|
2103
|
+
delete [] location;
|
|
2104
|
+
return promise;
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
/**
|
|
2108
|
+
* Worker class for repairing a database.
|
|
2109
|
+
*/
|
|
2110
|
+
struct RepairWorker final : public BaseWorker {
|
|
2111
|
+
RepairWorker (napi_env env, const std::string& location, napi_deferred deferred)
|
|
2112
|
+
: BaseWorker(env, NULL, deferred, "classic_level.repair_db"),
|
|
2113
|
+
location_(location) {}
|
|
2114
|
+
|
|
2115
|
+
~RepairWorker () {}
|
|
2116
|
+
|
|
2117
|
+
void DoExecute () override {
|
|
2118
|
+
leveldb::Options options;
|
|
2119
|
+
SetStatus(leveldb::RepairDB(location_, options));
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2122
|
+
std::string location_;
|
|
2123
|
+
};
|
|
2124
|
+
|
|
2125
|
+
/**
|
|
2126
|
+
* Repairs a database.
|
|
2127
|
+
*/
|
|
2128
|
+
NAPI_METHOD(repair_db) {
|
|
2129
|
+
NAPI_ARGV(1);
|
|
2130
|
+
NAPI_ARGV_UTF8_NEW(location, 0);
|
|
2131
|
+
NAPI_PROMISE();
|
|
2132
|
+
|
|
2133
|
+
RepairWorker* worker = new RepairWorker(env, location, deferred);
|
|
2134
|
+
worker->Queue(env);
|
|
2135
|
+
|
|
2136
|
+
delete [] location;
|
|
2137
|
+
|
|
2138
|
+
return promise;
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
/**
|
|
2142
|
+
* Create an iterator.
|
|
2143
|
+
*/
|
|
2144
|
+
NAPI_METHOD(iterator_init) {
|
|
2145
|
+
NAPI_ARGV(4);
|
|
2146
|
+
NAPI_DB_CONTEXT();
|
|
2147
|
+
|
|
2148
|
+
unsigned char* state = 0;
|
|
2149
|
+
size_t stateLength;
|
|
2150
|
+
NAPI_STATUS_THROWS(napi_get_typedarray_info(env, argv[1], NULL, &stateLength, (void**)&state, NULL, NULL));
|
|
2151
|
+
assert(stateLength == 1);
|
|
2152
|
+
|
|
2153
|
+
napi_value options = argv[2];
|
|
2154
|
+
const bool reverse = BooleanProperty(env, options, "reverse", false);
|
|
2155
|
+
const bool keys = BooleanProperty(env, options, "keys", true);
|
|
2156
|
+
const bool values = BooleanProperty(env, options, "values", true);
|
|
2157
|
+
const bool fillCache = BooleanProperty(env, options, "fillCache", false);
|
|
2158
|
+
const Encoding keyEncoding = GetEncoding(env, options, "keyEncoding");
|
|
2159
|
+
const Encoding valueEncoding = GetEncoding(env, options, "valueEncoding");
|
|
2160
|
+
const int limit = Int32Property(env, options, "limit", -1);
|
|
2161
|
+
const uint32_t highWaterMarkBytes = Uint32Property(env, options, "highWaterMarkBytes", 16 * 1024);
|
|
2162
|
+
|
|
2163
|
+
std::string* lt = RangeOption(env, options, "lt");
|
|
2164
|
+
std::string* lte = RangeOption(env, options, "lte");
|
|
2165
|
+
std::string* gt = RangeOption(env, options, "gt");
|
|
2166
|
+
std::string* gte = RangeOption(env, options, "gte");
|
|
2167
|
+
|
|
2168
|
+
ExplicitSnapshot* snapshot = NULL;
|
|
2169
|
+
napi_get_value_external(env, argv[3], (void**)&snapshot);
|
|
2170
|
+
|
|
2171
|
+
Iterator* iterator = new Iterator(
|
|
2172
|
+
database,
|
|
2173
|
+
reverse,
|
|
2174
|
+
keys, values,
|
|
2175
|
+
limit,
|
|
2176
|
+
lt, lte, gt, gte,
|
|
2177
|
+
fillCache,
|
|
2178
|
+
keyEncoding, valueEncoding,
|
|
2179
|
+
highWaterMarkBytes,
|
|
2180
|
+
state,
|
|
2181
|
+
snapshot
|
|
2182
|
+
);
|
|
2183
|
+
|
|
2184
|
+
napi_value result;
|
|
2185
|
+
NAPI_STATUS_THROWS(napi_create_external(env, iterator,
|
|
2186
|
+
Resource::CollectGarbage,
|
|
2187
|
+
NULL, &result));
|
|
2188
|
+
|
|
2189
|
+
iterator->Attach(env, result);
|
|
2190
|
+
|
|
2191
|
+
return result;
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2194
|
+
/**
|
|
2195
|
+
* Seeks an iterator.
|
|
2196
|
+
*/
|
|
2197
|
+
NAPI_METHOD(iterator_seek) {
|
|
2198
|
+
NAPI_ARGV(2);
|
|
2199
|
+
NAPI_ITERATOR_CONTEXT();
|
|
2200
|
+
|
|
2201
|
+
// AbstractIterator should not call _seek() after _close()
|
|
2202
|
+
assert(!iterator->hasClosed_);
|
|
2203
|
+
|
|
2204
|
+
leveldb::Slice target = ToSlice(env, argv[1]);
|
|
2205
|
+
iterator->first_ = true;
|
|
2206
|
+
iterator->ended_ = false;
|
|
2207
|
+
iterator->Seek(target);
|
|
2208
|
+
|
|
2209
|
+
DisposeSliceBuffer(target);
|
|
2210
|
+
NAPI_RETURN_UNDEFINED();
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2213
|
+
/**
|
|
2214
|
+
* Closes an iterator.
|
|
2215
|
+
*/
|
|
2216
|
+
NAPI_METHOD(iterator_close) {
|
|
2217
|
+
NAPI_ARGV(1);
|
|
2218
|
+
NAPI_ITERATOR_CONTEXT();
|
|
2219
|
+
|
|
2220
|
+
// AbstractIterator should not call _close() more than once or while nexting
|
|
2221
|
+
assert(!iterator->hasClosed_);
|
|
2222
|
+
assert(!iterator->nexting_);
|
|
2223
|
+
|
|
2224
|
+
iterator->CloseResource();
|
|
2225
|
+
iterator->Detach(env);
|
|
2226
|
+
|
|
2227
|
+
NAPI_RETURN_UNDEFINED();
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
/**
|
|
2231
|
+
* Aborts a NextWorker (if any, eventually).
|
|
2232
|
+
*/
|
|
2233
|
+
NAPI_METHOD(iterator_abort) {
|
|
2234
|
+
NAPI_ARGV(1);
|
|
2235
|
+
NAPI_ITERATOR_CONTEXT();
|
|
2236
|
+
iterator->aborted_ = true;
|
|
2237
|
+
NAPI_RETURN_UNDEFINED();
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
/**
|
|
2241
|
+
* Worker class for nexting an iterator.
|
|
2242
|
+
*/
|
|
2243
|
+
struct NextWorker final : public BaseWorker {
|
|
2244
|
+
NextWorker (napi_env env, Iterator* iterator, uint32_t size, napi_deferred deferred)
|
|
2245
|
+
: BaseWorker(env, iterator->database, deferred, "classic_level.iterator.next"),
|
|
2246
|
+
iterator_(iterator), size_(size), ok_() {}
|
|
2247
|
+
|
|
2248
|
+
~NextWorker () {}
|
|
2249
|
+
|
|
2250
|
+
void DoExecute () override {
|
|
2251
|
+
if (!iterator_->DidSeek()) {
|
|
2252
|
+
iterator_->SeekToRange();
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2255
|
+
ok_ = iterator_->ReadMany(size_);
|
|
2256
|
+
|
|
2257
|
+
if (!ok_) {
|
|
2258
|
+
SetStatus(iterator_->Status());
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
void HandleOKCallback (napi_env env, napi_deferred deferred) override {
|
|
2263
|
+
if (iterator_->aborted_) {
|
|
2264
|
+
napi_value err = CreateCodeError(env, "LEVEL_ABORTED", "Operation has been aborted");
|
|
2265
|
+
napi_value name;
|
|
2266
|
+
napi_create_string_utf8(env, "AbortError", NAPI_AUTO_LENGTH, &name);
|
|
2267
|
+
napi_set_named_property(env, err, "name", name);
|
|
2268
|
+
napi_reject_deferred(env, deferred, err);
|
|
2269
|
+
return;
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
size_t size = iterator_->cache_.size();
|
|
2273
|
+
napi_value jsArray;
|
|
2274
|
+
napi_create_array_with_length(env, size, &jsArray);
|
|
2275
|
+
|
|
2276
|
+
const Encoding ke = iterator_->keyEncoding_;
|
|
2277
|
+
const Encoding ve = iterator_->valueEncoding_;
|
|
2278
|
+
|
|
2279
|
+
for (uint32_t idx = 0; idx < size; idx++) {
|
|
2280
|
+
napi_value element;
|
|
2281
|
+
iterator_->cache_[idx].ConvertByMode(env, Mode::entries, ke, ve, element);
|
|
2282
|
+
napi_set_element(env, jsArray, idx, element);
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2285
|
+
// TODO: use state_ internally too, replacing ended_?
|
|
2286
|
+
if (iterator_->ended_) {
|
|
2287
|
+
*iterator_->state_ |= STATE_ENDED;
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2290
|
+
napi_resolve_deferred(env, deferred, jsArray);
|
|
2291
|
+
}
|
|
2292
|
+
|
|
2293
|
+
void DoFinally (napi_env env) override {
|
|
2294
|
+
iterator_->nexting_ = false;
|
|
2295
|
+
BaseWorker::DoFinally(env);
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
private:
|
|
2299
|
+
Iterator* iterator_;
|
|
2300
|
+
uint32_t size_;
|
|
2301
|
+
bool ok_;
|
|
2302
|
+
};
|
|
2303
|
+
|
|
2304
|
+
/**
|
|
2305
|
+
* Advance repeatedly and get multiple entries at once.
|
|
2306
|
+
*/
|
|
2307
|
+
NAPI_METHOD(iterator_nextv) {
|
|
2308
|
+
NAPI_ARGV(2);
|
|
2309
|
+
NAPI_ITERATOR_CONTEXT();
|
|
2310
|
+
NAPI_PROMISE();
|
|
2311
|
+
|
|
2312
|
+
uint32_t size;
|
|
2313
|
+
NAPI_STATUS_THROWS(napi_get_value_uint32(env, argv[1], &size));
|
|
2314
|
+
if (size == 0) size = 1;
|
|
2315
|
+
|
|
2316
|
+
// AbstractIterator should not call _next() or _nextv() after _close()
|
|
2317
|
+
assert(!iterator->hasClosed_);
|
|
2318
|
+
|
|
2319
|
+
if (iterator->ended_) {
|
|
2320
|
+
napi_value empty;
|
|
2321
|
+
napi_create_array_with_length(env, 0, &empty);
|
|
2322
|
+
napi_resolve_deferred(env, deferred, empty);
|
|
2323
|
+
} else {
|
|
2324
|
+
NextWorker* worker = new NextWorker(env, iterator, size, deferred);
|
|
2325
|
+
iterator->nexting_ = true;
|
|
2326
|
+
worker->Queue(env);
|
|
2327
|
+
}
|
|
2328
|
+
|
|
2329
|
+
return promise;
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
/**
|
|
2333
|
+
* Worker class for batch write operation.
|
|
2334
|
+
*/
|
|
2335
|
+
struct BatchWorker final : public PriorityWorker {
|
|
2336
|
+
BatchWorker (napi_env env,
|
|
2337
|
+
Database* database,
|
|
2338
|
+
napi_deferred deferred,
|
|
2339
|
+
leveldb::WriteBatch* batch,
|
|
2340
|
+
const bool sync,
|
|
2341
|
+
const bool hasData)
|
|
2342
|
+
: PriorityWorker(env, database, deferred, "classic_level.batch.do"),
|
|
2343
|
+
batch_(batch), hasData_(hasData) {
|
|
2344
|
+
options_.sync = sync;
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
~BatchWorker () {
|
|
2348
|
+
delete batch_;
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2351
|
+
void DoExecute () override {
|
|
2352
|
+
if (hasData_) {
|
|
2353
|
+
SetStatus(database_->WriteBatch(options_, batch_));
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
|
|
2357
|
+
private:
|
|
2358
|
+
leveldb::WriteOptions options_;
|
|
2359
|
+
leveldb::WriteBatch* batch_;
|
|
2360
|
+
const bool hasData_;
|
|
2361
|
+
};
|
|
2362
|
+
|
|
2363
|
+
/**
|
|
2364
|
+
* Does a batch write operation on a database.
|
|
2365
|
+
*/
|
|
2366
|
+
NAPI_METHOD(batch_do) {
|
|
2367
|
+
NAPI_ARGV(3);
|
|
2368
|
+
NAPI_DB_CONTEXT();
|
|
2369
|
+
NAPI_PROMISE();
|
|
2370
|
+
|
|
2371
|
+
napi_value array = argv[1];
|
|
2372
|
+
const bool sync = BooleanProperty(env, argv[2], "sync", false);
|
|
2373
|
+
|
|
2374
|
+
uint32_t length;
|
|
2375
|
+
napi_get_array_length(env, array, &length);
|
|
2376
|
+
|
|
2377
|
+
leveldb::WriteBatch* batch = new leveldb::WriteBatch();
|
|
2378
|
+
bool hasData = false;
|
|
2379
|
+
|
|
2380
|
+
for (uint32_t i = 0; i < length; i++) {
|
|
2381
|
+
napi_value element;
|
|
2382
|
+
napi_get_element(env, array, i, &element);
|
|
2383
|
+
|
|
2384
|
+
if (!IsObject(env, element)) continue;
|
|
2385
|
+
|
|
2386
|
+
std::string type = StringProperty(env, element, "type");
|
|
2387
|
+
|
|
2388
|
+
if (type == "del") {
|
|
2389
|
+
if (!HasProperty(env, element, "key")) continue;
|
|
2390
|
+
leveldb::Slice key = ToSlice(env, GetProperty(env, element, "key"));
|
|
2391
|
+
|
|
2392
|
+
batch->Delete(key);
|
|
2393
|
+
if (!hasData) hasData = true;
|
|
2394
|
+
|
|
2395
|
+
DisposeSliceBuffer(key);
|
|
2396
|
+
} else if (type == "put") {
|
|
2397
|
+
if (!HasProperty(env, element, "key")) continue;
|
|
2398
|
+
if (!HasProperty(env, element, "value")) continue;
|
|
2399
|
+
|
|
2400
|
+
leveldb::Slice key = ToSlice(env, GetProperty(env, element, "key"));
|
|
2401
|
+
leveldb::Slice value = ToSlice(env, GetProperty(env, element, "value"));
|
|
2402
|
+
|
|
2403
|
+
batch->Put(key, value);
|
|
2404
|
+
if (!hasData) hasData = true;
|
|
2405
|
+
|
|
2406
|
+
DisposeSliceBuffer(key);
|
|
2407
|
+
DisposeSliceBuffer(value);
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2411
|
+
BatchWorker* worker = new BatchWorker(
|
|
2412
|
+
env, database, deferred, batch, sync, hasData
|
|
2413
|
+
);
|
|
2414
|
+
|
|
2415
|
+
worker->Queue(env);
|
|
2416
|
+
return promise;
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
/**
|
|
2420
|
+
* Owns a WriteBatch.
|
|
2421
|
+
*/
|
|
2422
|
+
struct Batch {
|
|
2423
|
+
Batch (Database* database)
|
|
2424
|
+
: database_(database),
|
|
2425
|
+
batch_(new leveldb::WriteBatch()),
|
|
2426
|
+
hasData_(false) {}
|
|
2427
|
+
|
|
2428
|
+
~Batch () {
|
|
2429
|
+
delete batch_;
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
void Put (leveldb::Slice key, leveldb::Slice value) {
|
|
2433
|
+
batch_->Put(key, value);
|
|
2434
|
+
hasData_ = true;
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2437
|
+
void Del (leveldb::Slice key) {
|
|
2438
|
+
batch_->Delete(key);
|
|
2439
|
+
hasData_ = true;
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
void Clear () {
|
|
2443
|
+
batch_->Clear();
|
|
2444
|
+
hasData_ = false;
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
leveldb::Status Write (bool sync) {
|
|
2448
|
+
leveldb::WriteOptions options;
|
|
2449
|
+
options.sync = sync;
|
|
2450
|
+
return database_->WriteBatch(options, batch_);
|
|
2451
|
+
}
|
|
2452
|
+
|
|
2453
|
+
Database* database_;
|
|
2454
|
+
leveldb::WriteBatch* batch_;
|
|
2455
|
+
bool hasData_;
|
|
2456
|
+
};
|
|
2457
|
+
|
|
2458
|
+
/**
|
|
2459
|
+
* Runs when a Batch is garbage collected.
|
|
2460
|
+
*/
|
|
2461
|
+
static void FinalizeBatch (napi_env env, void* data, void* hint) {
|
|
2462
|
+
if (data) {
|
|
2463
|
+
delete (Batch*)data;
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
|
|
2467
|
+
/**
|
|
2468
|
+
* Return a batch object.
|
|
2469
|
+
*/
|
|
2470
|
+
NAPI_METHOD(batch_init) {
|
|
2471
|
+
NAPI_ARGV(1);
|
|
2472
|
+
NAPI_DB_CONTEXT();
|
|
2473
|
+
|
|
2474
|
+
Batch* batch = new Batch(database);
|
|
2475
|
+
|
|
2476
|
+
napi_value result;
|
|
2477
|
+
NAPI_STATUS_THROWS(napi_create_external(env, batch,
|
|
2478
|
+
FinalizeBatch,
|
|
2479
|
+
NULL, &result));
|
|
2480
|
+
return result;
|
|
2481
|
+
}
|
|
2482
|
+
|
|
2483
|
+
/**
|
|
2484
|
+
* Adds a put instruction to a batch object.
|
|
2485
|
+
*/
|
|
2486
|
+
NAPI_METHOD(batch_put) {
|
|
2487
|
+
NAPI_ARGV(3);
|
|
2488
|
+
NAPI_BATCH_CONTEXT();
|
|
2489
|
+
|
|
2490
|
+
leveldb::Slice key = ToSlice(env, argv[1]);
|
|
2491
|
+
leveldb::Slice value = ToSlice(env, argv[2]);
|
|
2492
|
+
batch->Put(key, value);
|
|
2493
|
+
DisposeSliceBuffer(key);
|
|
2494
|
+
DisposeSliceBuffer(value);
|
|
2495
|
+
|
|
2496
|
+
NAPI_RETURN_UNDEFINED();
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
/**
|
|
2500
|
+
* Adds a delete instruction to a batch object.
|
|
2501
|
+
*/
|
|
2502
|
+
NAPI_METHOD(batch_del) {
|
|
2503
|
+
NAPI_ARGV(2);
|
|
2504
|
+
NAPI_BATCH_CONTEXT();
|
|
2505
|
+
|
|
2506
|
+
leveldb::Slice key = ToSlice(env, argv[1]);
|
|
2507
|
+
batch->Del(key);
|
|
2508
|
+
DisposeSliceBuffer(key);
|
|
2509
|
+
|
|
2510
|
+
NAPI_RETURN_UNDEFINED();
|
|
2511
|
+
}
|
|
2512
|
+
|
|
2513
|
+
/**
|
|
2514
|
+
* Clears a batch object.
|
|
2515
|
+
*/
|
|
2516
|
+
NAPI_METHOD(batch_clear) {
|
|
2517
|
+
NAPI_ARGV(1);
|
|
2518
|
+
NAPI_BATCH_CONTEXT();
|
|
2519
|
+
|
|
2520
|
+
batch->Clear();
|
|
2521
|
+
|
|
2522
|
+
NAPI_RETURN_UNDEFINED();
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
/**
|
|
2526
|
+
* Worker class for batch write operation.
|
|
2527
|
+
*/
|
|
2528
|
+
struct BatchWriteWorker final : public PriorityWorker {
|
|
2529
|
+
BatchWriteWorker (napi_env env,
|
|
2530
|
+
napi_value context,
|
|
2531
|
+
Batch* batch,
|
|
2532
|
+
napi_deferred deferred,
|
|
2533
|
+
const bool sync)
|
|
2534
|
+
: PriorityWorker(env, batch->database_, deferred, "classic_level.batch.write"),
|
|
2535
|
+
batch_(batch),
|
|
2536
|
+
sync_(sync) {
|
|
2537
|
+
// Prevent GC of batch object before we execute
|
|
2538
|
+
NAPI_STATUS_THROWS_VOID(napi_create_reference(env, context, 1, &contextRef_));
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
~BatchWriteWorker () {}
|
|
2542
|
+
|
|
2543
|
+
void DoExecute () override {
|
|
2544
|
+
if (batch_->hasData_) {
|
|
2545
|
+
SetStatus(batch_->Write(sync_));
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
void DoFinally (napi_env env) override {
|
|
2550
|
+
napi_delete_reference(env, contextRef_);
|
|
2551
|
+
PriorityWorker::DoFinally(env);
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
private:
|
|
2555
|
+
Batch* batch_;
|
|
2556
|
+
const bool sync_;
|
|
2557
|
+
napi_ref contextRef_;
|
|
2558
|
+
};
|
|
2559
|
+
|
|
2560
|
+
/**
|
|
2561
|
+
* Writes a batch object.
|
|
2562
|
+
*/
|
|
2563
|
+
NAPI_METHOD(batch_write) {
|
|
2564
|
+
NAPI_ARGV(2);
|
|
2565
|
+
NAPI_BATCH_CONTEXT();
|
|
2566
|
+
NAPI_PROMISE();
|
|
2567
|
+
|
|
2568
|
+
napi_value options = argv[1];
|
|
2569
|
+
const bool sync = BooleanProperty(env, options, "sync", false);
|
|
2570
|
+
|
|
2571
|
+
BatchWriteWorker* worker = new BatchWriteWorker(
|
|
2572
|
+
env, argv[0], batch, deferred, sync
|
|
2573
|
+
);
|
|
2574
|
+
|
|
2575
|
+
worker->Queue(env);
|
|
2576
|
+
return promise;
|
|
2577
|
+
}
|
|
2578
|
+
|
|
2579
|
+
/**
|
|
2580
|
+
* Create a snapshot context.
|
|
2581
|
+
*/
|
|
2582
|
+
NAPI_METHOD(snapshot_init) {
|
|
2583
|
+
NAPI_ARGV(1);
|
|
2584
|
+
NAPI_DB_CONTEXT();
|
|
2585
|
+
|
|
2586
|
+
ExplicitSnapshot* snapshot = new ExplicitSnapshot(database);
|
|
2587
|
+
|
|
2588
|
+
napi_value context;
|
|
2589
|
+
NAPI_STATUS_THROWS(napi_create_external(
|
|
2590
|
+
env,
|
|
2591
|
+
snapshot,
|
|
2592
|
+
Resource::CollectGarbage,
|
|
2593
|
+
NULL,
|
|
2594
|
+
&context
|
|
2595
|
+
));
|
|
2596
|
+
|
|
2597
|
+
snapshot->Attach(env, context);
|
|
2598
|
+
return context;
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2601
|
+
/**
|
|
2602
|
+
* Closes the snapshot.
|
|
2603
|
+
*/
|
|
2604
|
+
NAPI_METHOD(snapshot_close) {
|
|
2605
|
+
NAPI_ARGV(1);
|
|
2606
|
+
NAPI_SNAPSHOT_CONTEXT();
|
|
2607
|
+
|
|
2608
|
+
snapshot->CloseResource();
|
|
2609
|
+
snapshot->Detach(env);
|
|
2610
|
+
|
|
2611
|
+
NAPI_RETURN_UNDEFINED();
|
|
2612
|
+
}
|
|
2613
|
+
|
|
2614
|
+
/**
|
|
2615
|
+
* All exported functions.
|
|
2616
|
+
*/
|
|
2617
|
+
NAPI_INIT() {
|
|
2618
|
+
NAPI_EXPORT_FUNCTION(db_init);
|
|
2619
|
+
NAPI_EXPORT_FUNCTION(db_set_shared_buffer)
|
|
2620
|
+
NAPI_EXPORT_FUNCTION(db_open);
|
|
2621
|
+
NAPI_EXPORT_FUNCTION(db_close);
|
|
2622
|
+
NAPI_EXPORT_FUNCTION(db_put);
|
|
2623
|
+
NAPI_EXPORT_FUNCTION(db_get);
|
|
2624
|
+
NAPI_EXPORT_FUNCTION(db_get_sync);
|
|
2625
|
+
NAPI_EXPORT_FUNCTION(db_get_many);
|
|
2626
|
+
NAPI_EXPORT_FUNCTION(db_has);
|
|
2627
|
+
NAPI_EXPORT_FUNCTION(db_has_many);
|
|
2628
|
+
NAPI_EXPORT_FUNCTION(db_del);
|
|
2629
|
+
NAPI_EXPORT_FUNCTION(db_clear);
|
|
2630
|
+
NAPI_EXPORT_FUNCTION(db_approximate_size);
|
|
2631
|
+
NAPI_EXPORT_FUNCTION(db_compact_range);
|
|
2632
|
+
NAPI_EXPORT_FUNCTION(db_get_property);
|
|
2633
|
+
|
|
2634
|
+
NAPI_EXPORT_FUNCTION(destroy_db);
|
|
2635
|
+
NAPI_EXPORT_FUNCTION(repair_db);
|
|
2636
|
+
|
|
2637
|
+
NAPI_EXPORT_FUNCTION(iterator_init);
|
|
2638
|
+
NAPI_EXPORT_FUNCTION(iterator_seek);
|
|
2639
|
+
NAPI_EXPORT_FUNCTION(iterator_close);
|
|
2640
|
+
NAPI_EXPORT_FUNCTION(iterator_nextv);
|
|
2641
|
+
NAPI_EXPORT_FUNCTION(iterator_abort);
|
|
2642
|
+
|
|
2643
|
+
NAPI_EXPORT_FUNCTION(batch_do);
|
|
2644
|
+
NAPI_EXPORT_FUNCTION(batch_init);
|
|
2645
|
+
NAPI_EXPORT_FUNCTION(batch_put);
|
|
2646
|
+
NAPI_EXPORT_FUNCTION(batch_del);
|
|
2647
|
+
NAPI_EXPORT_FUNCTION(batch_clear);
|
|
2648
|
+
NAPI_EXPORT_FUNCTION(batch_write);
|
|
2649
|
+
|
|
2650
|
+
NAPI_EXPORT_FUNCTION(snapshot_init);
|
|
2651
|
+
NAPI_EXPORT_FUNCTION(snapshot_close);
|
|
2652
|
+
}
|