mdbxmou 0.3.11 → 0.3.12

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 CHANGED
@@ -7,7 +7,7 @@ Node.js binding for [libmdbx](https://github.com/Mithril-mine/libmdbx) — a fas
7
7
  - **Synchronous API** — Direct MDBX operations in main thread
8
8
  - **Asynchronous API** — Background operations with async/await
9
9
  - **Transactions** — ACID transactions with read/write modes
10
- - **Multiple key/value types** — String, binary, ordinal (integer) keys
10
+ - **Multiple key/value types** — String, binary, ordinal keys and ordinal duplicate values
11
11
  - **Batch operations** — Efficient multi-key read/write
12
12
  - **Memory-mapped** — High-performance memory-mapped I/O
13
13
 
@@ -86,6 +86,8 @@ Options:
86
86
  - `keyFlag` - Default key encoding for all operations (optional, defaults to Buffer)
87
87
  - Only `string` can be set (ordinal mode uses `number`/`bigint` separately)
88
88
  - `valueFlag` - Default value encoding for all operations (optional, defaults to Buffer)
89
+ - `string` affects normal string values
90
+ - `number` and `bigint` are meaningful for `valueMode.multiOrdinal`
89
91
  - `maxDbi` - Maximum number of databases (optional, default `32`)
90
92
  - `mode` - Filesystem permissions mode (optional, default `0664`)
91
93
  - `geometry` - Map size/geometry options (optional)
@@ -190,14 +192,21 @@ const txn = env.startRead();
190
192
  ```javascript
191
193
  const result = await env.query([
192
194
  {
193
- dbMode: MDBX_Param.dbMode.accede,
194
- keyMode: MDBX_Param.keyMode.ordinal,
195
+ dbi,
195
196
  mode: MDBX_Param.queryMode.get,
196
197
  item: [{ key: 1 }, { key: 2 }]
198
+ },
199
+ {
200
+ dbi,
201
+ mode: MDBX_Param.queryMode.upsert,
202
+ putFlag: MDBX_Param.putFlag.noOverwrite,
203
+ item: [{ key: 3, value: "v3" }]
197
204
  }
198
205
  ]);
199
206
  ```
200
207
 
208
+ `query()` uses the passed `dbi` and inherits key/value settings from it. `queryMode` selects the operation (`get`, `del`, or base write mode), and optional `putFlag` adds write-only MDBX flags. In `query()` only `noOverwrite`, `noDupData`, `current`, `append`, and `appendDup` are supported.
209
+
201
210
  ### Transaction
202
211
 
203
212
  #### Methods
@@ -271,6 +280,8 @@ const dbi = txn.openMap({
271
280
  > - `keyMode: BigInt(number)` → keys returned as `BigInt`
272
281
  > - When you pass `keyMode.ordinal` as a positional argument (Number/BigInt), it also updates `keyFlag` to number/bigint unless a numeric keyFlag was already set in env or explicitly provided.
273
282
 
283
+ > **Note**: When `valueMode.multiOrdinal` is used and `valueFlag` is not specified, values are returned as `number` by default. Set `valueFlag: MDBX_Param.valueFlag.bigint` if you need `BigInt` on read.
284
+
274
285
  **commit()**
275
286
  ```javascript
276
287
  txn.commit();
@@ -285,10 +296,16 @@ txn.abort();
285
296
 
286
297
  #### Methods
287
298
 
288
- **put(txn, key, value)**
299
+ **put(txn, key, value, [flags])**
289
300
  ```javascript
290
301
  dbi.put(txn, 123, "value");
291
302
  dbi.put(txn, "key", Buffer.from("binary data"));
303
+
304
+ // Insert only if key does not exist
305
+ dbi.put(txn, 123, "value", MDBX_Param.putFlag.noOverwrite);
306
+
307
+ // Fast append for sorted inserts
308
+ dbi.put(txn, 124, "value", MDBX_Param.putFlag.append);
292
309
  ```
293
310
 
294
311
  **get(txn, key) → value**
@@ -297,6 +314,8 @@ const value = dbi.get(txn, 123);
297
314
  const binary = dbi.get(txn, "key");
298
315
  ```
299
316
 
317
+ For `valueMode.multiOrdinal`, `get()` returns the first duplicate value for the key, decoded as `number` by default.
318
+
300
319
  **del(txn, key) → boolean**
301
320
  ```javascript
302
321
  const deleted = dbi.del(txn, 123);
@@ -355,6 +374,12 @@ const rows = dbi.getRange(txn, { start: 10, end: 15 });
355
374
  // ]
356
375
  ```
357
376
 
377
+ **getCount(txn, [options]) → number**
378
+ ```javascript
379
+ const total = dbi.getCount(txn, { start: 10, end: 20 });
380
+ // 11
381
+ ```
382
+
358
383
  **keysRange(txn, [options]) → Array**
359
384
  ```javascript
360
385
  const keys = dbi.keysRange(txn, { start: 10, end: 20, limit: 5 });
@@ -378,6 +403,7 @@ Range options:
378
403
  - `reverse` - scan from upper bound to lower bound
379
404
  - `limit` - maximum number of returned items
380
405
  - `offset` - skip N items after initial positioning
406
+ - `getCount()` ignores `offset` and `limit` and returns the total size of the bounded range
381
407
 
382
408
  **drop(txn, [delete_db]) → void**
383
409
  ```javascript
@@ -461,7 +487,7 @@ Insert or update a record at cursor position.
461
487
  cursor.put('newKey', 'newValue');
462
488
 
463
489
  // With flags (MDBX_NOOVERWRITE, etc.)
464
- cursor.put('key', 'value', MDBX_Param.queryMode.insertUnique);
490
+ cursor.put('key', 'value', MDBX_Param.putFlag.noOverwrite);
465
491
  ```
466
492
 
467
493
  **del([flags]) → boolean**
@@ -595,12 +621,20 @@ txn.commit();
595
621
  ### Value Modes (MDBX_Param.valueMode)
596
622
 
597
623
  - **single** - Single value per key (default)
598
- - **multi** - Multiple values per key (dupsort)
624
+ - **multi** - `MDBX_DUPSORT`, multiple values per key
625
+ - **multiReverse** - `MDBX_DUPSORT | MDBX_REVERSEDUP`
626
+ - **multiSamelength** - `MDBX_DUPSORT | MDBX_DUPFIXED`
627
+ - **multiOrdinal** - `MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_INTEGERDUP`
628
+ - **multiReverseSamelength** - `MDBX_DUPSORT | MDBX_REVERSEDUP | MDBX_DUPFIXED`
599
629
 
600
630
  ### Value Flags (MDBX_Param.valueFlag)
601
631
 
602
- - **binary** - Raw binary data (default)
632
+ - **binary** - Raw binary data (default, represented by `0`)
603
633
  - **string** - UTF-8 strings
634
+ - **number** - Numeric decode for ordinal duplicate values
635
+ - **bigint** - BigInt decode for ordinal duplicate values
636
+
637
+ `valueFlag.number` and `valueFlag.bigint` matter primarily for `valueMode.multiOrdinal`. For ordinary values, use Buffer (default) or `valueFlag.string`.
604
638
 
605
639
  ## Examples
606
640
 
@@ -815,6 +849,9 @@ function rangeExample() {
815
849
  // { key: 6, value: 'value_6' }
816
850
  // ]
817
851
 
852
+ const total = readDbi.getCount(readTxn, { start: 3, end: 8 });
853
+ console.log(total); // 6
854
+
818
855
  const keys = readDbi.keysRange(readTxn, {
819
856
  start: 3,
820
857
  end: 8,
@@ -838,6 +875,44 @@ function rangeExample() {
838
875
  rangeExample();
839
876
  ```
840
877
 
878
+ ### MultiOrdinal Values
879
+
880
+ ```javascript
881
+ const { MDBX_Env, MDBX_Param } = require('mdbxmou');
882
+
883
+ function multiOrdinalExample() {
884
+ const env = new MDBX_Env();
885
+ env.openSync({ path: './multi-ordinal-data' });
886
+
887
+ const writeTxn = env.startWrite();
888
+ const dbi = writeTxn.createMap({
889
+ name: 'dup-ids',
890
+ keyMode: MDBX_Param.keyMode.ordinal,
891
+ valueMode: MDBX_Param.valueMode.multiOrdinal
892
+ });
893
+
894
+ dbi.put(writeTxn, 5, 30);
895
+ dbi.put(writeTxn, 5, 10);
896
+ dbi.put(writeTxn, 5, 20n);
897
+ writeTxn.commit();
898
+
899
+ const readTxn = env.startRead();
900
+ const readDbi = readTxn.openMap({
901
+ name: 'dup-ids',
902
+ keyMode: MDBX_Param.keyMode.ordinal,
903
+ valueMode: MDBX_Param.valueMode.multiOrdinal
904
+ });
905
+
906
+ console.log(readDbi.get(readTxn, 5)); // 10
907
+ console.log(readDbi.valuesRange(readTxn, { start: 5, end: 5 })); // [10, 20, 30]
908
+
909
+ readTxn.commit();
910
+ env.closeSync();
911
+ }
912
+
913
+ multiOrdinalExample();
914
+ ```
915
+
841
916
  ### Query API (Advanced Async)
842
917
 
843
918
  ```javascript
@@ -856,7 +931,8 @@ async function queryExample() {
856
931
  const results = await env.query([
857
932
  {
858
933
  dbi,
859
- mode: MDBX_Param.queryMode.insertUnique,
934
+ mode: MDBX_Param.queryMode.upsert,
935
+ putFlag: MDBX_Param.putFlag.noOverwrite,
860
936
  item: [
861
937
  { key: 1, value: JSON.stringify({ name: "Alice" }) },
862
938
  { key: 2, value: JSON.stringify({ name: "Bob" }) }
@@ -981,7 +1057,21 @@ MDBX_Param.keyFlag.number // Number type (used with ordinal mode)
981
1057
  MDBX_Param.keyFlag.bigint // BigInt type (used with ordinal mode)
982
1058
  // Default - Buffer representation
983
1059
 
1060
+ // Value modes
1061
+ MDBX_Param.valueMode.multi // MDBX_DUPSORT
1062
+ MDBX_Param.valueMode.multiReverse // MDBX_DUPSORT | MDBX_REVERSEDUP
1063
+ MDBX_Param.valueMode.multiSamelength // MDBX_DUPSORT | MDBX_DUPFIXED
1064
+ MDBX_Param.valueMode.multiOrdinal // MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_INTEGERDUP
1065
+ MDBX_Param.valueMode.multiReverseSamelength // MDBX_DUPSORT | MDBX_REVERSEDUP | MDBX_DUPFIXED
1066
+
1067
+ // Value flags (optional, control value representation)
1068
+ MDBX_Param.valueFlag.string // UTF-8 string values
1069
+ MDBX_Param.valueFlag.number // Number values for multiOrdinal
1070
+ MDBX_Param.valueFlag.bigint // BigInt values for multiOrdinal
1071
+ // Default - Buffer representation
1072
+
984
1073
  Note: For ordinal (integer) keys, use keyFlag.number or keyFlag.bigint to specify the data type.
1074
+ For `valueMode.multiOrdinal`, values are returned as `number` by default, or as `bigint` when `valueFlag.bigint` is used.
985
1075
  ```
986
1076
 
987
1077
  ### Environment Flags
@@ -1027,11 +1117,23 @@ Note: For ordinal (integer) keys, use keyFlag.number or keyFlag.bigint to specif
1027
1117
 
1028
1118
  ### Query Modes
1029
1119
  - `MDBX_Param.queryMode.get` - Read operations
1030
- - `MDBX_Param.queryMode.upsert` - Write operations (insert or update)
1031
- - `MDBX_Param.queryMode.update` - Update existing (MDBX_CURRENT)
1032
- - `MDBX_Param.queryMode.insertUnique` - Insert unique (MDBX_NOOVERWRITE)
1120
+ - `MDBX_Param.queryMode.upsert` - Base write mode (insert or update)
1121
+ - `MDBX_Param.queryMode.update` - Base write mode with `MDBX_CURRENT`
1122
+ - `MDBX_Param.queryMode.insertUnique` - Base write mode with `MDBX_NOOVERWRITE`
1033
1123
  - `MDBX_Param.queryMode.del` - Delete operations
1034
1124
 
1125
+ ### Put Flags
1126
+ - `MDBX_Param.putFlag.noOverwrite` - `MDBX_NOOVERWRITE`
1127
+ - `MDBX_Param.putFlag.noDupData` - `MDBX_NODUPDATA`
1128
+ - `MDBX_Param.putFlag.current` - `MDBX_CURRENT`
1129
+ - `MDBX_Param.putFlag.allDups` - `MDBX_ALLDUPS`
1130
+ - `MDBX_Param.putFlag.reserve` - `MDBX_RESERVE`
1131
+ - `MDBX_Param.putFlag.append` - `MDBX_APPEND`
1132
+ - `MDBX_Param.putFlag.appendDup` - `MDBX_APPENDDUP`
1133
+ - `MDBX_Param.putFlag.multiple` - `MDBX_MULTIPLE`
1134
+
1135
+ For `env.query()` write requests, only `noOverwrite`, `noDupData`, `current`, `append`, and `appendDup` are supported.
1136
+
1035
1137
  ### Cursor Modes
1036
1138
  - `MDBX_Param.cursorMode.first` - First key
1037
1139
  - `MDBX_Param.cursorMode.last` - Last key
package/lib/async.d.mts CHANGED
@@ -1,4 +1,6 @@
1
- import type { MDBXDbiStat, MDBXEnvOpenOptions, MDBXKey, MDBXValue } from "./types.js";
1
+ import type { MDBXDbiStat, MDBXEnvOpenOptions, MDBXKey } from "./types.js";
2
+
3
+ type MDBXAsyncValue = Buffer | string;
2
4
 
3
5
  export interface MDBXAsyncMapOpenOptions {
4
6
  name?: string;
@@ -35,15 +37,15 @@ export interface MDBXAsyncDelBatchResultItem {
35
37
  export declare class MDBX_Async_Dbi {
36
38
  readonly meta: MDBXAsyncDbiMeta;
37
39
 
38
- put(key: MDBXKey, value: MDBXValue, flags?: number): Promise<boolean>;
40
+ put(key: MDBXKey, value: MDBXAsyncValue, flags?: number): Promise<boolean>;
39
41
  get(key: MDBXKey): Promise<string | null | undefined>;
40
42
  del(key: MDBXKey): Promise<boolean>;
41
43
  stat(): Promise<MDBXDbiStat>;
42
44
 
43
- forEach(cb: (key: MDBXKey, value: MDBXValue, index: number) => void): Promise<void>;
45
+ forEach(cb: (key: MDBXKey, value: MDBXAsyncValue, index: number) => void): Promise<void>;
44
46
 
45
47
  getBatch(keys: MDBXKey[]): Promise<MDBXAsyncGetBatchResultItem[]>;
46
- putBatch(items: Array<{ key: MDBXKey; value: MDBXValue }>, flags?: number): Promise<MDBXAsyncPutBatchResultItem[]>;
48
+ putBatch(items: Array<{ key: MDBXKey; value: MDBXAsyncValue }>, flags?: number): Promise<MDBXAsyncPutBatchResultItem[]>;
47
49
  delBatch(keys: MDBXKey[]): Promise<MDBXAsyncDelBatchResultItem[]>;
48
50
  }
49
51
 
package/lib/types.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /// <reference types="node" />
2
2
 
3
3
  export type MDBXKey = Buffer | string | number | bigint;
4
- export type MDBXValue = Buffer | string;
4
+ export type MDBXValue = Buffer | string | number | bigint;
5
5
 
6
6
  export type MDBXCursorMode =
7
7
  | number
@@ -202,7 +202,7 @@ export interface MDBX_Dbi<K extends MDBXKey = MDBXKey, V extends MDBXValue = MDB
202
202
  readonly keyFlag: number;
203
203
  readonly valueFlag: number;
204
204
 
205
- put(txn: MDBX_Txn, key: K, value: MDBXValue): void;
205
+ put(txn: MDBX_Txn, key: K, value: MDBXValue, flags?: number): void;
206
206
  get(txn: MDBX_Txn, key: K): V | undefined;
207
207
  del(txn: MDBX_Txn, key: K): boolean;
208
208
  has(txn: MDBX_Txn, key: K): boolean;
@@ -224,6 +224,7 @@ export interface MDBX_Dbi<K extends MDBXKey = MDBXKey, V extends MDBXValue = MDB
224
224
  keys(txn: MDBX_Txn): K[];
225
225
  keysFrom(txn: MDBX_Txn, fromKey: K, limit?: number, cursorMode?: MDBXCursorMode): K[];
226
226
  getRange(txn: MDBX_Txn, options?: MDBXRangeOptions<K>): MDBXCursorResult<K, V>[];
227
+ getCount(txn: MDBX_Txn, options?: MDBXRangeOptions<K>): number;
227
228
  keysRange(txn: MDBX_Txn, options?: MDBXRangeOptions<K>): K[];
228
229
  valuesRange(txn: MDBX_Txn, options?: MDBXRangeOptions<K>): V[];
229
230
  drop(txn: MDBX_Txn, deleteDb?: boolean): void;
@@ -264,6 +265,7 @@ export interface MDBXQueryRequest {
264
265
  dbi: MDBX_Dbi;
265
266
  mode?: number;
266
267
  queryMode?: number;
268
+ putFlag?: number;
267
269
  item: MDBXQueryItem[];
268
270
  }
269
271
 
@@ -374,6 +376,8 @@ export interface MDBX_Param {
374
376
 
375
377
  readonly valueFlag: {
376
378
  readonly string: number;
379
+ readonly number: number;
380
+ readonly bigint: number;
377
381
  };
378
382
 
379
383
  readonly dbMode: {
@@ -389,6 +393,17 @@ export interface MDBX_Param {
389
393
  readonly del: number;
390
394
  };
391
395
 
396
+ readonly putFlag: {
397
+ readonly noOverwrite: number;
398
+ readonly noDupData: number;
399
+ readonly current: number;
400
+ readonly allDups: number;
401
+ readonly reserve: number;
402
+ readonly append: number;
403
+ readonly appendDup: number;
404
+ readonly multiple: number;
405
+ };
406
+
392
407
  readonly cursorMode: {
393
408
  readonly first: number;
394
409
  readonly last: number;
package/package.json CHANGED
@@ -61,6 +61,7 @@
61
61
  "e5": "node ./test/e5.js",
62
62
  "e7": "node ./test/e7.js",
63
63
  "e8": "node ./test/e8.js",
64
+ "e9": "node ./test/e9.js",
64
65
  "test:types": "node ./test/types/run.mjs",
65
66
  "build": "node build.js",
66
67
  "build-dev": "node build-dev.js",
@@ -68,7 +69,7 @@
68
69
  },
69
70
  "gypfile": true,
70
71
  "name": "mdbxmou",
71
- "version": "0.3.11",
72
+ "version": "0.3.12",
72
73
  "description": "Node bindings for mdbx",
73
74
  "repository": {
74
75
  "type": "git",
@@ -67,7 +67,7 @@ void async_keys::Execute()
67
67
  static Napi::Value write_row(Napi::Env env, const keys_line& row)
68
68
  {
69
69
  auto& param = row.item;
70
- convmou conv{row.key_mod, row.key_flag};
70
+ convmou conv{row.key_mod, {}, row.key_flag, {}};
71
71
  auto js_arr = Napi::Array::New(env, param.size());
72
72
  for (std::uint32_t j = 0; j < param.size(); ++j) {
73
73
  const auto& item = param[j];
@@ -13,9 +13,9 @@ void async_query::Execute()
13
13
  {
14
14
  mdbx::map_handle dbi{req.id};
15
15
  auto mode = req.mode;
16
- if (mode.val & query_mode::get) {
16
+ if (mode.is_get()) {
17
17
  do_get(txn, dbi, req);
18
- } else if (mode.val & query_mode::del) {
18
+ } else if (mode.is_del()) {
19
19
  do_del(txn, dbi, req);
20
20
  } else {
21
21
  do_put(txn, dbi, req);
@@ -33,7 +33,7 @@ static Napi::Value write_row(Napi::Env env, const query_line& row)
33
33
  {
34
34
  auto& param = row.item;
35
35
  auto mode = row.mode;
36
- convmou conv{row.key_mod, row.key_flag, row.value_flag};
36
+ convmou conv{row.key_mod, row.val_mod, row.key_flag, row.value_flag};
37
37
  auto js_arr = Napi::Array::New(env, param.size());
38
38
  for (std::size_t j = 0; j < param.size(); ++j) {
39
39
  const auto& item = param[j];
@@ -43,13 +43,11 @@ static Napi::Value write_row(Napi::Env env, const query_line& row)
43
43
  keymou{item.key_buf};
44
44
  js_item.Set("key", conv.convert_key(env, key));
45
45
 
46
- // все методы которые должны показать value в результате
47
- const auto mask{query_mode::get|query_mode::upsert|
48
- query_mode::update|query_mode::insert_unique};
49
- if (mode.val & mask)
50
- {
46
+ if (mode.is_get() || mode.is_write()) {
51
47
  auto& val_buf = item.val_buf;
52
- if (val_buf.empty()) {
48
+ if (is_ordinal(row.val_mod) && mode.is_write() && val_buf.empty()) {
49
+ js_item.Set("value", conv.convert_value(env, valuemou{item.val_num}));
50
+ } else if (val_buf.empty()) {
53
51
  js_item.Set("value", env.Null());
54
52
  } else {
55
53
  js_item.Set("value",
@@ -58,7 +56,7 @@ static Napi::Value write_row(Napi::Env env, const query_line& row)
58
56
  }
59
57
 
60
58
  // выдадим флаги удаления и успешности
61
- if (mode.val & query_mode::del) {
59
+ if (mode.is_del()) {
62
60
  js_item.Set("found", Napi::Boolean::New(env, item.found));
63
61
  }
64
62
  js_arr.Set(static_cast<uint32_t>(j), js_item);
@@ -133,15 +131,18 @@ void async_query::do_get(const txnmou_managed& txn,
133
131
  void async_query::do_put(txnmou_managed& txn,
134
132
  mdbx::map_handle dbi, query_line& arg0)
135
133
  {
136
- auto mode = arg0.mode;
134
+ auto flags = static_cast<MDBX_put_flags_t>(
135
+ arg0.mode.write_flags() | arg0.put_flags.val);
137
136
  // очищаем put флаги
138
137
  auto key_mode = arg0.key_mod;
139
138
  for (auto& q : arg0.item)
140
139
  {
141
140
  auto key = mdbx::is_ordinal(key_mode) ?
142
141
  keymou{q.id_buf} : keymou{q.key_buf};
143
- mdbx::slice val{q.val_buf.data(), q.val_buf.size()};
144
- txn.put(dbi, key, val, mode);
142
+ valuemou val = is_ordinal(arg0.val_mod) ?
143
+ valuemou{q.val_num} :
144
+ valuemou{q.val_buf};
145
+ mdbx::error::success_or_throw(txn.put(dbi, key, &val, flags));
145
146
  }
146
147
  }
147
148
 
package/src/convmou.cpp CHANGED
@@ -1,7 +1,18 @@
1
1
  #include "convmou.hpp"
2
+ #include "dbimou.hpp"
2
3
 
3
4
  namespace mdbxmou {
4
5
 
6
+ convmou convmou::for_dbi(const dbimou& dbi) noexcept
7
+ {
8
+ return {
9
+ dbi.get_key_mode(),
10
+ dbi.get_value_mode(),
11
+ dbi.get_key_flag(),
12
+ dbi.get_value_flag()
13
+ };
14
+ }
15
+
5
16
  Napi::Value convmou::convert_key(const Napi::Env& env, const keymou& key) const
6
17
  {
7
18
  if (mdbx::is_ordinal(key_mode_)) {
@@ -17,6 +28,12 @@ Napi::Value convmou::convert_key(const Napi::Env& env, const keymou& key) const
17
28
 
18
29
  Napi::Value convmou::convert_value(const Napi::Env& env, const valuemou& val) const
19
30
  {
31
+ if (is_ordinal(value_mode_)) {
32
+ return (value_flag_ & base_flag::bigint) ?
33
+ val.to_bigint(env) :
34
+ val.to_number(env);
35
+ }
36
+
20
37
  return (value_flag_ & base_flag::string) ?
21
38
  val.to_string(env) :
22
39
  val.to_buffer(env);
package/src/convmou.hpp CHANGED
@@ -4,20 +4,16 @@
4
4
 
5
5
  namespace mdbxmou {
6
6
 
7
+ class dbimou;
8
+
7
9
  struct convmou
8
10
  {
9
11
  key_mode key_mode_{};
12
+ value_mode value_mode_{};
10
13
  base_flag key_flag_{};
11
14
  base_flag value_flag_{};
12
15
 
13
- convmou() = default;
14
-
15
- convmou(key_mode key_mode, base_flag key_flag,
16
- base_flag value_flag = {}) noexcept
17
- : key_mode_{key_mode}
18
- , key_flag_{key_flag}
19
- , value_flag_{value_flag}
20
- { }
16
+ static convmou for_dbi(const dbimou& dbi) noexcept;
21
17
 
22
18
  Napi::Value convert_key(const Napi::Env& env, const keymou& key) const;
23
19
 
package/src/cursormou.cpp CHANGED
@@ -230,7 +230,8 @@ namespace mdbxmou
230
230
  keymou::from(info[0], env, key_num_) :
231
231
  keymou::from(info[0], env, key_buf_);
232
232
 
233
- valuemou val = valuemou::from(info[1], env, val_buf_);
233
+ valuemou val = valuemou::from(info[1], env, val_buf_, val_num_,
234
+ is_ordinal(dbi_->get_value_mode()));
234
235
 
235
236
  // Опциональный флаг put_mode (по умолчанию MDBX_UPSERT)
236
237
  MDBX_put_flags_t flags = MDBX_UPSERT;
package/src/cursormou.hpp CHANGED
@@ -20,6 +20,7 @@ private:
20
20
  buffer_type key_buf_{};
21
21
  buffer_type val_buf_{};
22
22
  std::uint64_t key_num_{};
23
+ std::uint64_t val_num_{};
23
24
 
24
25
  // Внутренний хелпер для навигации
25
26
  Napi::Value move(const Napi::Env& env, MDBX_cursor_op op);
package/src/dbimou.cpp CHANGED
@@ -165,14 +165,13 @@ MDBX_cursor_op range_turn_op(const range_options& options)
165
165
  return options.reverse ? MDBX_PREV : MDBX_NEXT;
166
166
  }
167
167
 
168
- Napi::Array collect_range(const Napi::Env& env, dbimou& self, txnmou& txn, const range_options& options, range_output output)
168
+ template<class Fn>
169
+ std::size_t scan_range(dbimou& self, txnmou& txn, const range_options& options, Fn&& fn)
169
170
  {
170
- Napi::Array result = Napi::Array::New(env);
171
171
  if (options.limit == 0) {
172
- return result;
172
+ return 0;
173
173
  }
174
174
 
175
- auto conv = self.get_convmou();
176
175
  auto cursor = self.open_cursor(txn);
177
176
  mdbx::slice key{};
178
177
  mdbx::slice value{};
@@ -186,7 +185,7 @@ Napi::Array collect_range(const Napi::Env& env, dbimou& self, txnmou& txn, const
186
185
  }
187
186
 
188
187
  if (!cursor_get(cursor, range_start_op(options), key, value)) {
189
- return result;
188
+ return 0;
190
189
  }
191
190
 
192
191
  std::size_t skipped{};
@@ -201,17 +200,8 @@ Napi::Array collect_range(const Napi::Env& env, dbimou& self, txnmou& txn, const
201
200
  if (skipped < options.offset) {
202
201
  ++skipped;
203
202
  } else {
204
- switch (output) {
205
- case range_output::items: {
206
- result.Set(index, conv.make_result(env, keymou{key}, valuemou{value}));
207
- break;
208
- }
209
- case range_output::keys:
210
- result.Set(index, conv.convert_key(env, keymou{key}));
211
- break;
212
- case range_output::values:
213
- result.Set(index, conv.convert_value(env, valuemou{value}));
214
- break;
203
+ if (fn(keymou{key}, valuemou{value}, index)) {
204
+ break;
215
205
  }
216
206
 
217
207
  ++index;
@@ -225,9 +215,39 @@ Napi::Array collect_range(const Napi::Env& env, dbimou& self, txnmou& txn, const
225
215
  }
226
216
  }
227
217
 
218
+ return index;
219
+ }
220
+
221
+ Napi::Array collect_range(const Napi::Env& env, dbimou& self, txnmou& txn, const range_options& options, range_output output)
222
+ {
223
+ Napi::Array result = Napi::Array::New(env);
224
+ auto conv = self.get_convmou();
225
+ scan_range(self, txn, options, [&](const keymou& key, const valuemou& value, std::size_t index) {
226
+ switch (output) {
227
+ case range_output::items:
228
+ result.Set(index, conv.make_result(env, key, value));
229
+ break;
230
+ case range_output::keys:
231
+ result.Set(index, conv.convert_key(env, key));
232
+ break;
233
+ case range_output::values:
234
+ result.Set(index, conv.convert_value(env, value));
235
+ break;
236
+ }
237
+ return false;
238
+ });
228
239
  return result;
229
240
  }
230
241
 
242
+ std::size_t count_range(dbimou& self, txnmou& txn, range_options options)
243
+ {
244
+ options.offset = 0;
245
+ options.limit = std::numeric_limits<std::size_t>::max();
246
+ return scan_range(self, txn, options, [](const keymou&, const valuemou&, std::size_t) {
247
+ return false;
248
+ });
249
+ }
250
+
231
251
  Napi::Value run_range_query(const Napi::CallbackInfo& info, dbimou& self, const char* method_name, range_output output)
232
252
  {
233
253
  Napi::Env env = info.Env();
@@ -247,6 +267,26 @@ Napi::Value run_range_query(const Napi::CallbackInfo& info, dbimou& self, const
247
267
  }
248
268
  }
249
269
 
270
+ Napi::Value run_range_count(const Napi::CallbackInfo& info, dbimou& self, const char* method_name)
271
+ {
272
+ Napi::Env env = info.Env();
273
+ if (info.Length() < 1) {
274
+ throw Napi::TypeError::New(env, std::string(method_name) + ": txnmou required");
275
+ }
276
+
277
+ auto txn = txnmou::unwrap_checked(env, info[0], method_name);
278
+
279
+ try {
280
+ auto options = info.Length() > 1 ?
281
+ parse_range_options(env, info[1], self) :
282
+ range_options{};
283
+ auto count = count_range(self, *txn, options);
284
+ return Napi::Number::New(env, static_cast<double>(count));
285
+ } catch (const std::exception& e) {
286
+ throw Napi::Error::New(env, std::string(method_name) + ": " + e.what());
287
+ }
288
+ }
289
+
250
290
  } // namespace
251
291
 
252
292
  Napi::FunctionReference dbimou::ctor{};
@@ -263,6 +303,7 @@ void dbimou::init(const char *class_name, Napi::Env env)
263
303
  InstanceMethod("keys", &dbimou::keys),
264
304
  InstanceMethod("keysFrom", &dbimou::keys_from),
265
305
  InstanceMethod("getRange", &dbimou::get_range),
306
+ InstanceMethod("getCount", &dbimou::get_count),
266
307
  InstanceMethod("keysRange", &dbimou::keys_range),
267
308
  InstanceMethod("valuesRange", &dbimou::values_range),
268
309
  InstanceMethod("drop", &dbimou::drop),
@@ -294,8 +335,15 @@ Napi::Value dbimou::put(const Napi::CallbackInfo& info)
294
335
  keymou::from(info[1], env, t) :
295
336
  keymou::from(info[1], env, key_buf_);
296
337
 
297
- auto val = valuemou::from(info[2], env, val_buf_);
298
- dbi::put(*txn, key, val, *this);
338
+ auto val = valuemou::from(info[2], env, val_buf_, val_num_, is_ordinal(value_mode_));
339
+ MDBX_put_flags_t flags = MDBX_UPSERT;
340
+ if (arg_len > 3 && !info[3].IsUndefined() && !info[3].IsNull()) {
341
+ if (!info[3].IsNumber()) {
342
+ throw Napi::TypeError::New(env, "put: flags must be a number");
343
+ }
344
+ flags = put_flag::parse(info[3]);
345
+ }
346
+ dbi::put(*txn, key, val, flags);
299
347
  } catch (const std::exception& e) {
300
348
  throw Napi::Error::New(env, std::string("put: ") + e.what());
301
349
  }
@@ -709,6 +757,11 @@ Napi::Value dbimou::get_range(const Napi::CallbackInfo& info)
709
757
  return run_range_query(info, *this, "getRange", range_output::items);
710
758
  }
711
759
 
760
+ Napi::Value dbimou::get_count(const Napi::CallbackInfo& info)
761
+ {
762
+ return run_range_count(info, *this, "getCount");
763
+ }
764
+
712
765
  Napi::Value dbimou::keys_range(const Napi::CallbackInfo& info)
713
766
  {
714
767
  return run_range_query(info, *this, "keysRange", range_output::keys);
package/src/dbimou.hpp CHANGED
@@ -25,6 +25,7 @@ class dbimou final
25
25
 
26
26
  buffer_type key_buf_{};
27
27
  buffer_type val_buf_{};
28
+ std::uint64_t val_num_{};
28
29
 
29
30
  public:
30
31
  static Napi::FunctionReference ctor;
@@ -73,6 +74,7 @@ public:
73
74
  Napi::Value keys(const Napi::CallbackInfo&);
74
75
  Napi::Value keys_from(const Napi::CallbackInfo&);
75
76
  Napi::Value get_range(const Napi::CallbackInfo&);
77
+ Napi::Value get_count(const Napi::CallbackInfo&);
76
78
  Napi::Value keys_range(const Napi::CallbackInfo&);
77
79
  Napi::Value values_range(const Napi::CallbackInfo&);
78
80
  Napi::Value drop(const Napi::CallbackInfo&);
@@ -124,7 +126,7 @@ public:
124
126
  }
125
127
 
126
128
  convmou get_convmou() const noexcept {
127
- return {key_mode_, key_flag_, value_flag_};
129
+ return convmou::for_dbi(*this);
128
130
  }
129
131
  };
130
132
 
package/src/modulemou.cpp CHANGED
@@ -80,6 +80,8 @@ Napi::Object Init(Napi::Env env, Napi::Object exports)
80
80
 
81
81
  Napi::Object value_flag = Napi::Object::New(env);
82
82
  MDBXMOU_DECLARE_FLAG_NAME(value_flag, "string", base_flag::string);
83
+ MDBXMOU_DECLARE_FLAG_NAME(value_flag, "number", base_flag::number);
84
+ MDBXMOU_DECLARE_FLAG_NAME(value_flag, "bigint", base_flag::bigint);
83
85
  mdbx_mou.Set("valueFlag", value_flag);
84
86
 
85
87
  using mdbxmou::db_mode;
@@ -97,6 +99,18 @@ Napi::Object Init(Napi::Env env, Napi::Object exports)
97
99
  MDBXMOU_DECLARE_FLAG_NAME(queryMode, "del", query_mode::del);
98
100
  mdbx_mou.Set("queryMode", queryMode);
99
101
 
102
+ using mdbxmou::put_flag;
103
+ Napi::Object putFlag = Napi::Object::New(env);
104
+ MDBXMOU_DECLARE_FLAG_NAME(putFlag, "noOverwrite", put_flag::no_overwrite);
105
+ MDBXMOU_DECLARE_FLAG_NAME(putFlag, "noDupData", put_flag::no_dup_data);
106
+ MDBXMOU_DECLARE_FLAG_NAME(putFlag, "current", put_flag::current);
107
+ MDBXMOU_DECLARE_FLAG_NAME(putFlag, "allDups", put_flag::all_dups);
108
+ MDBXMOU_DECLARE_FLAG_NAME(putFlag, "reserve", put_flag::reserve);
109
+ MDBXMOU_DECLARE_FLAG_NAME(putFlag, "append", put_flag::append);
110
+ MDBXMOU_DECLARE_FLAG_NAME(putFlag, "appendDup", put_flag::append_dup);
111
+ MDBXMOU_DECLARE_FLAG_NAME(putFlag, "multiple", put_flag::multiple);
112
+ mdbx_mou.Set("putFlag", putFlag);
113
+
100
114
  using move_operation = mdbx::cursor::move_operation;
101
115
  Napi::Object cursor_mode = Napi::Object::New(env);
102
116
  MDBXMOU_DECLARE_FLAG_NAME(cursor_mode, "first", move_operation::first);
package/src/querymou.cpp CHANGED
@@ -23,20 +23,11 @@ dbimou* async_common::parse(const Napi::Object& arg0)
23
23
 
24
24
  void async_key::parse(const async_common& common, const Napi::Value& item)
25
25
  {
26
- // утснавлиаем общие параметры
27
- auto key_flag = common.key_flag;
28
-
29
26
  keymou key{};
30
27
  if (mdbx::is_ordinal(common.key_mod)) {
31
- if (item.IsBigInt()) {
32
- key = keymou{item.As<Napi::BigInt>(), id_buf};
33
- } else if (item.IsNumber()) {
34
- key = keymou{item.As<Napi::Number>(), id_buf};
35
- }
28
+ key = keymou::from(item, item.Env(), id_buf);
36
29
  } else {
37
- key = (key_flag & base_flag::string) ?
38
- keymou{item.As<Napi::String>(), item.Env(), key_buf} :
39
- keymou{item.As<Napi::Buffer<char>>(), key_buf};
30
+ key = keymou::from(item, item.Env(), key_buf);
40
31
  }
41
32
  }
42
33
 
@@ -44,12 +35,14 @@ void async_keyval::parse(const query_line& common, const Napi::Object& item)
44
35
  {
45
36
  async_key::parse(common, item);
46
37
  // проверяем надо ли что-то писать
47
- if (common.mode.val & query_mode::write_mask) {
38
+ if (common.mode.is_write()) {
48
39
  valuemou val{};
49
40
  auto item_val = item.Get("value");
50
- val = (common.value_flag & base_flag::string) ?
51
- valuemou{item_val.As<Napi::String>(), item_val.Env(), val_buf} :
52
- valuemou{item_val.As<Napi::Buffer<char>>(), val_buf};
41
+ val = is_ordinal(common.val_mod) ?
42
+ valuemou::from(item_val, item_val.Env(), val_num) :
43
+ (common.value_flag & base_flag::string) ?
44
+ valuemou{item_val.As<Napi::String>(), item_val.Env(), val_buf} :
45
+ valuemou{item_val.As<Napi::Buffer<char>>(), val_buf};
53
46
  }
54
47
  }
55
48
 
@@ -63,6 +56,13 @@ void query_line::parse(txn_mode txn, const Napi::Object& arg0)
63
56
  } else if (arg0.Has("queryMode")) {
64
57
  mode = query_mode::parse(txn, arg0.Get("queryMode").As<Napi::Number>());
65
58
  }
59
+ if (arg0.Has("putFlag")) {
60
+ put_flags = put_flag::parse_query(arg0.Get("putFlag"));
61
+ if (!mode.is_write()) {
62
+ throw Napi::TypeError::New(arg0.Env(),
63
+ "query putFlag requires write queryMode");
64
+ }
65
+ }
66
66
  auto items_array = arg0.Get("item").As<Napi::Array>();
67
67
  auto item_len = items_array.Length();
68
68
  if (item_len > 0) {
package/src/querymou.hpp CHANGED
@@ -39,6 +39,7 @@ struct async_keyval
39
39
  : async_key
40
40
  {
41
41
  buffer_type val_buf{};
42
+ std::uint64_t val_num{};
42
43
  bool found{false};
43
44
 
44
45
  void parse(const query_line& line, const Napi::Object& obj);
@@ -57,6 +58,7 @@ struct query_line
57
58
  {
58
59
  base_flag value_flag{};
59
60
  query_mode mode{};
61
+ put_flag put_flags{};
60
62
  // буффер для запроса / ответа
61
63
  std::vector<async_keyval> item{};
62
64
  void parse(txn_mode txn, const Napi::Object& arg0);
@@ -83,4 +85,4 @@ struct keys_line
83
85
  using keys_request = std::vector<keys_line>;
84
86
  keys_request parse_keys(const Napi::Value& obj);
85
87
 
86
- } // namespace mdbxmou
88
+ } // namespace mdbxmou
package/src/txnmou.cpp CHANGED
@@ -159,7 +159,7 @@ Napi::Value txnmou::get_dbi(const Napi::Object& arg0, db_mode db_mode)
159
159
  if (arg0.Has("valueMode")) {
160
160
  auto value = arg0.Get("valueMode");
161
161
  if (!value.IsUndefined() && !value.IsNull()) {
162
- value_mode = value_mode::parse(value);
162
+ value_mode = parse_value_mode(value, value_flag);
163
163
  }
164
164
  }
165
165
 
@@ -189,7 +189,7 @@ Napi::Value txnmou::get_dbi(const Napi::CallbackInfo& info, db_mode db_mode)
189
189
  auto arg2 = info[2]; // value_mode
190
190
  db_name = arg0.As<Napi::String>().Utf8Value();
191
191
  key_mode = parse_key_mode(env, arg1, key_flag);
192
- value_mode = value_mode::parse(arg2);
192
+ value_mode = parse_value_mode(arg2, value_flag);
193
193
  } else if (arg_count == 2) {
194
194
  // db_name + key_mode || key_mode + value_mode
195
195
  auto arg0 = info[0];
@@ -199,7 +199,7 @@ Napi::Value txnmou::get_dbi(const Napi::CallbackInfo& info, db_mode db_mode)
199
199
  key_mode = parse_key_mode(env, arg1, key_flag);
200
200
  } else {
201
201
  key_mode = parse_key_mode(env, arg0, key_flag);
202
- value_mode = value_mode::parse(arg1);
202
+ value_mode = parse_value_mode(arg1, value_flag);
203
203
  }
204
204
  } else if (arg_count == 1) {
205
205
  // db_name || key_mode
package/src/typemou.hpp CHANGED
@@ -171,19 +171,80 @@ struct query_mode
171
171
  };
172
172
  int val{get};
173
173
 
174
+ bool is_get() const noexcept {
175
+ return (val & get) != 0;
176
+ }
177
+
178
+ bool is_del() const noexcept {
179
+ return (val & del) != 0;
180
+ }
181
+
182
+ bool is_write() const noexcept {
183
+ return !is_get() && !is_del();
184
+ }
185
+
174
186
  static inline query_mode parse(const txn_mode& mode, const Napi::Value& arg0) {
175
187
  query_mode rc{arg0.As<Napi::Number>().Int32Value() & mask};
176
- if ((mode.val & txn_mode::ro) && (rc.val & (del|write_mask))) {
188
+ if ((rc.is_get() || rc.is_del()) && (rc.val & write_mask)) {
189
+ throw std::runtime_error(
190
+ "queryMode must be one of get/del/upsert/update/insertUnique");
191
+ }
192
+ if (rc.is_get() && rc.is_del()) {
193
+ throw std::runtime_error(
194
+ "queryMode must be one of get/del/upsert/update/insertUnique");
195
+ }
196
+ if ((mode.val & txn_mode::ro) && (rc.is_del() || rc.is_write())) {
177
197
  throw std::runtime_error("rw query in read-only transaction");
178
198
  }
179
199
  return rc;
180
200
  }
181
201
 
202
+ int write_flags() const noexcept {
203
+ return val & write_mask;
204
+ }
205
+
182
206
  operator mdbx::put_mode() const noexcept {
183
207
  return static_cast<mdbx::put_mode>(val & write_mask);
184
208
  }
185
209
  };
186
210
 
211
+ struct put_flag
212
+ {
213
+ enum type : int {
214
+ no_overwrite = MDBX_NOOVERWRITE,
215
+ no_dup_data = MDBX_NODUPDATA,
216
+ current = MDBX_CURRENT,
217
+ all_dups = MDBX_ALLDUPS,
218
+ reserve = MDBX_RESERVE,
219
+ append = MDBX_APPEND,
220
+ append_dup = MDBX_APPENDDUP,
221
+ multiple = MDBX_MULTIPLE,
222
+ query_mask = MDBX_NOOVERWRITE | MDBX_NODUPDATA | MDBX_CURRENT |
223
+ MDBX_APPEND | MDBX_APPENDDUP,
224
+ mask = MDBX_NOOVERWRITE | MDBX_NODUPDATA | MDBX_CURRENT |
225
+ MDBX_ALLDUPS | MDBX_RESERVE | MDBX_APPEND |
226
+ MDBX_APPENDDUP | MDBX_MULTIPLE
227
+ };
228
+ int val{};
229
+
230
+ static inline put_flag parse(const Napi::Value& arg0) {
231
+ return {arg0.As<Napi::Number>().Int32Value() & mask};
232
+ }
233
+
234
+ static inline put_flag parse_query(const Napi::Value& arg0) {
235
+ put_flag rc = parse(arg0);
236
+ if (rc.val & ~query_mask) {
237
+ throw Napi::TypeError::New(arg0.Env(),
238
+ "query putFlag supports only noOverwrite/noDupData/current/append/appendDup");
239
+ }
240
+ return rc;
241
+ }
242
+
243
+ operator MDBX_put_flags_t() const noexcept {
244
+ return static_cast<MDBX_put_flags_t>(val & mask);
245
+ }
246
+ };
247
+
187
248
  struct key_mode
188
249
  {
189
250
  enum type : int {
@@ -223,6 +284,11 @@ struct value_mode
223
284
  }
224
285
  };
225
286
 
287
+ static inline bool is_ordinal(const value_mode& mode) noexcept
288
+ {
289
+ return (mode.val & static_cast<int>(MDBX_INTEGERDUP)) != 0;
290
+ }
291
+
226
292
 
227
293
  struct base_flag
228
294
  {
@@ -231,7 +297,7 @@ struct base_flag
231
297
  number = 4,
232
298
  bigint = 8,
233
299
  mask_key = string | number | bigint,
234
- mask_val = string
300
+ mask_val = string | number | bigint
235
301
  };
236
302
  int val{};
237
303
 
@@ -270,11 +336,12 @@ struct base_flag
270
336
  }
271
337
 
272
338
  static inline void validate_value(const Napi::Value& arg0, int value) {
273
- if (value == 0 || value == string) {
339
+ if (value == 0 || value == string ||
340
+ value == number || value == bigint) {
274
341
  return;
275
342
  }
276
343
  throw Napi::Error::New(arg0.Env(),
277
- "valueFlag must be 0 or string");
344
+ "valueFlag must be 0, string, number or bigint");
278
345
  }
279
346
 
280
347
  static inline base_flag parse_key(const Napi::Value& arg0) {
@@ -287,7 +354,11 @@ struct base_flag
287
354
  const Napi::Value& arg0) {
288
355
  auto value = arg0.As<Napi::Number>().Int32Value() & mask_val;
289
356
  validate_value(arg0, value);
290
- return {value};
357
+ base_flag rc{value};
358
+ if (is_ordinal(mode) && !rc.is_numeric()) {
359
+ rc = number;
360
+ }
361
+ return rc;
291
362
  }
292
363
 
293
364
  static inline base_flag parse_value(const Napi::Value& arg0) {
@@ -327,6 +398,15 @@ static inline key_mode parse_key_mode(Napi::Env env, const Napi::Value& arg0, ba
327
398
  return mode;
328
399
  }
329
400
 
401
+ static inline value_mode parse_value_mode(const Napi::Value& arg0, base_flag& value_flag)
402
+ {
403
+ auto mode = value_mode::parse(arg0);
404
+ if (is_ordinal(mode) && !value_flag.is_numeric()) {
405
+ value_flag = base_flag::number;
406
+ }
407
+ return mode;
408
+ }
409
+
330
410
  struct db_mode
331
411
  {
332
412
  enum type : int {
package/src/valuemou.hpp CHANGED
@@ -29,6 +29,10 @@ struct valuemou
29
29
  : mdbx::slice{arg0.data(), arg0.size()}
30
30
  { }
31
31
 
32
+ valuemou(const std::uint64_t& arg0) noexcept
33
+ : mdbx::slice{&arg0, sizeof(arg0)}
34
+ { }
35
+
32
36
  valuemou(const Napi::Buffer<char>& arg0, buffer_type& mem)
33
37
  {
34
38
  auto ptr = arg0.Data();
@@ -59,6 +63,28 @@ struct valuemou
59
63
  assign(mem.data(), mem.size());
60
64
  }
61
65
 
66
+ valuemou(const Napi::Number& arg0,
67
+ const Napi::Env& env, std::uint64_t& mem)
68
+ {
69
+ auto value = arg0.Int64Value();
70
+ if (value < 0) {
71
+ throw Napi::Error::New(env, "Number negative");
72
+ }
73
+ mem = static_cast<std::uint64_t>(value);
74
+ assign(&mem, sizeof(mem));
75
+ }
76
+
77
+ valuemou(const Napi::BigInt& arg0,
78
+ const Napi::Env& env, std::uint64_t& mem)
79
+ {
80
+ bool looseless;
81
+ mem = arg0.Uint64Value(&looseless);
82
+ if (!looseless) {
83
+ throw Napi::Error::New(env, "BigInt !looseless");
84
+ }
85
+ assign(&mem, sizeof(mem));
86
+ }
87
+
62
88
  static inline valuemou from(const Napi::Value& arg0,
63
89
  const Napi::Env& env, buffer_type& mem)
64
90
  {
@@ -70,6 +96,23 @@ struct valuemou
70
96
  return {arg0.As<Napi::String>(), env, mem};
71
97
  }
72
98
 
99
+ static inline valuemou from(const Napi::Value& arg0,
100
+ const Napi::Env& env, std::uint64_t& mem)
101
+ {
102
+ if (arg0.IsBigInt()) {
103
+ return {arg0.As<Napi::BigInt>(), env, mem};
104
+ } else if (!arg0.IsNumber()) {
105
+ throw Napi::Error::New(env, "value must be a Number or BigInt");
106
+ }
107
+ return {arg0.As<Napi::Number>(), env, mem};
108
+ }
109
+
110
+ static inline valuemou from(const Napi::Value& arg0,
111
+ const Napi::Env& env, buffer_type& buf, std::uint64_t& num, bool ordinal)
112
+ {
113
+ return ordinal ? from(arg0, env, num) : from(arg0, env, buf);
114
+ }
115
+
73
116
  Napi::Value to_string(const Napi::Env& env) const
74
117
  {
75
118
  return Napi::String::New(env,
@@ -81,92 +124,38 @@ struct valuemou
81
124
  return Napi::Buffer<char>::Copy(env,
82
125
  char_ptr(), length());
83
126
  }
127
+
128
+ Napi::Value to_number(const Napi::Env& env) const
129
+ {
130
+ return Napi::Number::New(env, static_cast<double>(as_int64()));
131
+ }
132
+
133
+ Napi::Value to_bigint(const Napi::Env& env) const
134
+ {
135
+ return Napi::BigInt::New(env, as_uint64());
136
+ }
84
137
  };
85
138
 
86
139
  struct keymou final
87
140
  : valuemou
88
141
  {
142
+ using valuemou::valuemou;
143
+
89
144
  keymou() = default;
90
145
  keymou(const keymou&) = default;
91
146
  keymou& operator=(const keymou&) = default;
92
147
  keymou(keymou&&) = default;
93
148
  keymou& operator=(keymou&&) = default;
94
149
 
95
- keymou(const valuemou& arg0) noexcept
96
- : valuemou{arg0}
97
- { }
98
-
99
- keymou(const mdbx::slice& arg0) noexcept
100
- : valuemou{arg0}
101
- { }
102
-
103
- keymou(const Napi::Buffer<char>& arg0)
104
- : valuemou{arg0}
105
- { }
106
-
107
- keymou(const buffer_type& arg0) noexcept
108
- : valuemou{arg0}
109
- { }
110
-
111
- keymou(const std::uint64_t& arg0) noexcept
112
- : valuemou{mdbx::slice{&arg0, sizeof(arg0)}}
113
- { }
114
-
115
- keymou(const Napi::Buffer<char>& arg0, buffer_type& mem)
116
- : valuemou{arg0, mem}
117
- { }
118
-
119
- keymou(const Napi::String& arg0,
120
- const Napi::Env& env, buffer_type& mem)
121
- : valuemou{arg0, env, mem}
122
- { }
123
-
124
- keymou(const Napi::Number& arg0, std::uint64_t& mem)
125
- {
126
- auto value = arg0.Int64Value();
127
- if (value < 0) {
128
- throw std::runtime_error("Number negative");
129
- }
130
- mem = static_cast<std::uint64_t>(value);
131
- assign(&mem, sizeof(mem));
132
- }
133
-
134
- keymou(const Napi::Number& arg0,
135
- const Napi::Env& env, std::uint64_t& mem)
136
- {
137
- auto value = arg0.Int64Value();
138
- if (value < 0) {
139
- throw Napi::Error::New(env, "Number negative");
140
- }
141
- mem = static_cast<std::uint64_t>(value);
142
- assign(&mem, sizeof(mem));
143
- }
144
-
145
- keymou(const Napi::BigInt& arg0,std::uint64_t& mem)
146
- {
147
- bool looseless;
148
- mem = arg0.Uint64Value(&looseless);
149
- if (!looseless) {
150
- throw std::runtime_error("BigInt !looseless");
151
- }
152
- assign(&mem, sizeof(mem));
153
- }
154
-
155
- keymou(const Napi::BigInt& arg0,
156
- const Napi::Env& env, std::uint64_t& mem)
157
- {
158
- bool looseless;
159
- mem = arg0.Uint64Value(&looseless);
160
- if (!looseless) {
161
- throw Napi::Error::New(env, "BigInt !looseless");
162
- }
163
- assign(&mem, sizeof(mem));
164
- }
165
-
166
150
  static inline keymou from(const Napi::Value& arg0,
167
151
  const Napi::Env& env, buffer_type& mem)
168
152
  {
169
- return {valuemou::from(arg0, env, mem)};
153
+ if (arg0.IsBuffer()) {
154
+ return {arg0.As<Napi::Buffer<char>>()};
155
+ } else if (!arg0.IsString()) {
156
+ throw Napi::Error::New(env, "key must be a Buffer or String");
157
+ }
158
+ return {arg0.As<Napi::String>(), env, mem};
170
159
  }
171
160
 
172
161
  static inline keymou from(const Napi::Value& arg0,
@@ -190,16 +179,6 @@ struct keymou final
190
179
  }
191
180
  return from(arg0, env, num);
192
181
  }
193
-
194
- Napi::Value to_number(const Napi::Env& env) const
195
- {
196
- return Napi::Number::New(env, static_cast<double>(as_int64()));
197
- }
198
-
199
- Napi::Value to_bigint(const Napi::Env& env) const
200
- {
201
- return Napi::BigInt::New(env, as_uint64());
202
- }
203
182
  };
204
183
 
205
- } // mdbxmou
184
+ } // mdbxmou