mdbxmou 0.2.6 → 0.2.7

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/package.json CHANGED
@@ -46,7 +46,8 @@
46
46
  ],
47
47
  "dependencies": {
48
48
  "cmake-js": "^7.3.1",
49
- "node-addon-api": "^8.5.0"
49
+ "node-addon-api": "^8.5.0",
50
+ "uuid": "^13.0.0"
50
51
  },
51
52
  "devDependencies": {
52
53
  "@types/node": "^22.10.2",
@@ -65,7 +66,7 @@
65
66
  },
66
67
  "gypfile": true,
67
68
  "name": "mdbxmou",
68
- "version": "0.2.6",
69
+ "version": "0.2.7",
69
70
  "description": "Node bindings for mdbx",
70
71
  "repository": {
71
72
  "type": "git",
package/src/dbimou.cpp CHANGED
@@ -51,6 +51,7 @@ Napi::Value dbimou::put(const Napi::CallbackInfo& info)
51
51
  auto key = (key_mode_.val & key_mode::ordinal) ?
52
52
  keymou::from(info[1], env, t) :
53
53
  keymou::from(info[1], env, key_buf_);
54
+
54
55
  auto val = valuemou::from(info[2], env, val_buf_);
55
56
  dbi::put(*txn, key, val, *this);
56
57
  } catch (const std::exception& e) {
@@ -139,7 +140,7 @@ Napi::Value dbimou::has(const Napi::CallbackInfo& info) {
139
140
  auto key = (key_mode_.val & key_mode::ordinal) ?
140
141
  keymou::from(info[1], env, t) :
141
142
  keymou::from(info[1], env, key_buf_);
142
-
143
+
143
144
  bool result = dbi::has(*txn, key);
144
145
  return Napi::Value::From(env, result);
145
146
  } catch (const std::exception& e) {
@@ -177,7 +178,6 @@ Napi::Value dbimou::for_each(const Napi::CallbackInfo& info) {
177
178
  }
178
179
 
179
180
  uint32_t index{};
180
-
181
181
  if (key_mode_.val & key_mode::ordinal) {
182
182
  cursor.scan([&](const mdbx::pair& f) {
183
183
  keymou key{f.key};
package/src/dbimou.hpp CHANGED
@@ -33,9 +33,6 @@ public:
33
33
  , dbi{}
34
34
  { }
35
35
 
36
- //~dbimou() { fprintf(stderr, "dbimou::~dbimou %p key_cap=%zu val_cap=%zu\n",this, key_buf_.capacity(), val_buf_.capacity()); }
37
- //void Finalize(Napi::Env env) { fprintf(stderr, "~dbimou %p key_cap=%zu val_cap=%zu\n",this, key_buf_.capacity(), val_buf_.capacity()); }
38
-
39
36
  Napi::Value get_id(const Napi::CallbackInfo& info) {
40
37
  return Napi::BigInt::New(info.Env(), static_cast<int64_t>(id_));
41
38
  }
package/src/env_arg0.hpp CHANGED
@@ -1,9 +1,6 @@
1
1
  #pragma once
2
2
 
3
3
  #include "valuemou.hpp"
4
- #ifdef _WIN32
5
- typedef unsigned short mode_t;
6
- #endif
7
4
 
8
5
  namespace mdbxmou {
9
6
 
@@ -12,7 +9,7 @@ struct env_arg0 {
12
9
  MDBX_dbi max_dbi{32};
13
10
  mdbx::env::geometry geom{};
14
11
  env_flag flag{};
15
- mode_t file_mode{0664};
12
+ mdbx_mode_t file_mode{0664};
16
13
  std::uint32_t max_readers{128};
17
14
  base_flag key_flag{};
18
15
  base_flag value_flag{};
package/src/envmou.cpp CHANGED
@@ -103,7 +103,7 @@ env_arg0 envmou::parse(const Napi::Value& arg0)
103
103
 
104
104
  if (obj.Has("mode")) {
105
105
  auto value = obj.Get("mode").As<Napi::Number>();
106
- rc.file_mode = static_cast<mode_t>(value.Int32Value());
106
+ rc.file_mode = static_cast<mdbx_mode_t>(value.Int32Value());
107
107
  }
108
108
 
109
109
  if (obj.Has("keyFlag")) {
@@ -234,7 +234,7 @@ Napi::Value envmou::close(const Napi::CallbackInfo& info)
234
234
  return env.Undefined();
235
235
  }
236
236
 
237
- if (trx_count_.load() > 0) {
237
+ if (trx_count_ > 0) {
238
238
  throw std::runtime_error("active transactions");
239
239
  }
240
240
 
@@ -392,10 +392,10 @@ Napi::Value envmou::query(const Napi::CallbackInfo& info)
392
392
  auto* worker = new async_query(env, *this, mode,
393
393
  std::move(query), arg0.IsObject());
394
394
  auto promise = worker->GetPromise();
395
- worker->Queue();
396
395
 
397
- // Увеличиваем счетчик транзакций после успешного создания
396
+ // Увеличиваем счетчик ДО Queue() worker гарантированно вызовет --env_
398
397
  ++(*this);
398
+ worker->Queue();
399
399
 
400
400
  return promise;
401
401
  } catch (const std::exception& e) {
@@ -433,10 +433,10 @@ Napi::Value envmou::keys(const Napi::CallbackInfo& info)
433
433
  auto* worker = new async_keys(env, *this, mode,
434
434
  std::move(query), arg0.IsObject());
435
435
  auto promise = worker->GetPromise();
436
- worker->Queue();
437
436
 
438
- // Увеличиваем счетчик транзакций после успешного создания
437
+ // Увеличиваем счетчик ДО Queue() worker гарантированно вызовет --env_
439
438
  ++(*this);
439
+ worker->Queue();
440
440
 
441
441
  return promise;
442
442
  } catch (const std::exception& e) {
package/src/envmou.hpp CHANGED
@@ -27,7 +27,8 @@ class envmou final
27
27
  };
28
28
 
29
29
  std::unique_ptr<MDBX_env, free_env> env_{};
30
- std::atomic<std::size_t> trx_count_{};
30
+ // счетчик транзакицй, не требующий атомарности
31
+ std::size_t trx_count_{};
31
32
  env_arg0 arg0_{};
32
33
  std::mutex lock_{};
33
34
 
@@ -106,7 +107,7 @@ public:
106
107
  }
107
108
 
108
109
  void do_close() {
109
- if (trx_count_.load() > 0) {
110
+ if (trx_count_ > 0) {
110
111
  throw std::runtime_error("transaction in progress");
111
112
  }
112
113
  env_.reset();
package/src/txnmou.cpp CHANGED
@@ -21,18 +21,14 @@ void txnmou::init(const char *class_name, Napi::Env env) {
21
21
  Napi::Value txnmou::commit(const Napi::CallbackInfo& info) {
22
22
  Napi::Env env = info.Env();
23
23
 
24
- try {
25
- check();
26
-
27
- auto rc = mdbx_txn_commit(*this);
28
- if (rc != MDBX_SUCCESS) {
29
- throw Napi::Error::New(env, std::string("txn: ") + mdbx_strerror(rc));
30
- }
31
-
32
- dec_counter();
33
- txn_.release();
34
- } catch (const std::exception& e) {
35
- throw Napi::Error::New(env, e.what());
24
+ if (!txn_) {
25
+ throw Napi::Error::New(env, "txn already completed");
26
+ }
27
+
28
+ dec_counter();
29
+ auto rc = mdbx_txn_commit(txn_.release());
30
+ if (rc != MDBX_SUCCESS) {
31
+ throw Napi::Error::New(env, std::string("txn commit: ") + mdbx_strerror(rc));
36
32
  }
37
33
 
38
34
  return env.Undefined();
@@ -41,18 +37,14 @@ Napi::Value txnmou::commit(const Napi::CallbackInfo& info) {
41
37
  Napi::Value txnmou::abort(const Napi::CallbackInfo& info) {
42
38
  Napi::Env env = info.Env();
43
39
 
44
- try {
45
- check();
46
-
47
- auto rc = mdbx_txn_abort(*this);
48
- if (rc != MDBX_SUCCESS) {
49
- throw Napi::Error::New(env, std::string("txn: ") + mdbx_strerror(rc));
50
- }
51
-
52
- dec_counter();
53
- txn_.release();
54
- } catch (const std::exception& e) {
55
- throw Napi::Error::New(env, e.what());
40
+ if (!txn_) {
41
+ throw Napi::Error::New(env, "txn already completed");
42
+ }
43
+
44
+ dec_counter();
45
+ auto rc = mdbx_txn_abort(txn_.release());
46
+ if (rc != MDBX_SUCCESS) {
47
+ throw Napi::Error::New(env, std::string("txn abort: ") + mdbx_strerror(rc));
56
48
  }
57
49
 
58
50
  return env.Undefined();
@@ -70,73 +62,66 @@ Napi::Value txnmou::get_dbi(const Napi::CallbackInfo& info, db_mode db_mode)
70
62
  {
71
63
  Napi::Env env = info.Env();
72
64
 
73
- try
74
- {
75
- if ((mode_.val & txn_mode::ro) && (db_mode.val & db_mode::create)) {
76
- throw std::runtime_error("dbi: cannot open DB in read-only transaction");
77
- }
65
+ if ((mode_.val & txn_mode::ro) && (db_mode.val & db_mode::create)) {
66
+ throw Napi::Error::New(env, "dbi: cannot open DB in read-only transaction");
67
+ }
78
68
 
79
- key_mode key_mode{};
80
- value_mode value_mode{};
81
- std::string db_name{};
82
- auto conf = get_env_userctx(*env_);
83
- auto key_flag = conf->key_flag;
84
- auto value_flag = conf->value_flag;
85
- auto arg_count = info.Length();
86
- if (arg_count == 3) {
87
- auto arg0 = info[0]; // db_name
88
- auto arg1 = info[1]; // key_mode
89
- auto arg2 = info[2]; // value_mode
69
+ key_mode key_mode{};
70
+ value_mode value_mode{};
71
+ std::string db_name{};
72
+ auto conf = get_env_userctx(*env_);
73
+ auto key_flag = conf->key_flag;
74
+ auto value_flag = conf->value_flag;
75
+ auto arg_count = info.Length();
76
+ if (arg_count == 3) {
77
+ auto arg0 = info[0]; // db_name
78
+ auto arg1 = info[1]; // key_mode
79
+ auto arg2 = info[2]; // value_mode
80
+ db_name = arg0.As<Napi::String>().Utf8Value();
81
+ key_mode = parse_key_mode(env, arg1, key_flag);
82
+ value_mode = value_mode::parse(arg2);
83
+ } else if (arg_count == 2) {
84
+ // db_name + key_mode || key_mode + value_mode
85
+ auto arg0 = info[0];
86
+ auto arg1 = info[1];
87
+ if (arg0.IsString()) {
90
88
  db_name = arg0.As<Napi::String>().Utf8Value();
91
89
  key_mode = parse_key_mode(env, arg1, key_flag);
92
- value_mode = value_mode::parse(arg2);
93
- } else if (arg_count == 2) {
94
- // db_name + key_mode || key_mode + value_mode
95
- auto arg0 = info[0];
96
- auto arg1 = info[1];
97
- if (arg0.IsString()) {
98
- db_name = arg0.As<Napi::String>().Utf8Value();
99
- key_mode = parse_key_mode(env, arg1, key_flag);
100
- } else if (arg0.IsNumber() || arg0.IsBigInt()) {
101
- key_mode = parse_key_mode(env, arg0, key_flag);
102
- value_mode = value_mode::parse(arg1);
103
- } else {
104
- throw Napi::Error::New(env, "Invalid argument type for db_name or value_mode");
105
- }
106
- } else if (arg_count == 1) {
107
- // db_name || key_mode
108
- auto arg0 = info[0];
109
- if (arg0.IsString()) {
110
- db_name = arg0.As<Napi::String>().Utf8Value();
111
- } else if (arg0.IsNumber() || arg0.IsBigInt()) {
112
- key_mode = parse_key_mode(env, arg0, key_flag);
113
- } else {
114
- throw Napi::Error::New(env, "Invalid argument type: expected string (db_name) or number (key_mode)");
115
- }
90
+ } else if (arg0.IsNumber() || arg0.IsBigInt()) {
91
+ key_mode = parse_key_mode(env, arg0, key_flag);
92
+ value_mode = value_mode::parse(arg1);
93
+ } else {
94
+ throw Napi::Error::New(env, "Invalid argument type for db_name or value_mode");
116
95
  }
117
- // arg_count == 0: используем значения по умолчанию (строковый ключ, default db)
118
-
119
- check();
120
-
121
- MDBX_dbi dbi{};
122
- auto flags = static_cast<MDBX_db_flags_t>(db_mode.val|key_mode.val|value_mode.val);
123
- auto rc = mdbx_dbi_open(*this, db_name.empty() ? nullptr : db_name.c_str(), flags, &dbi);
124
- if (rc != MDBX_SUCCESS) {
125
- throw std::runtime_error(std::string("mdbx_dbi_open ") + mdbx_strerror(rc));
96
+ } else if (arg_count == 1) {
97
+ // db_name || key_mode
98
+ auto arg0 = info[0];
99
+ if (arg0.IsString()) {
100
+ db_name = arg0.As<Napi::String>().Utf8Value();
101
+ } else if (arg0.IsNumber() || arg0.IsBigInt()) {
102
+ key_mode = parse_key_mode(env, arg0, key_flag);
103
+ } else {
104
+ throw Napi::Error::New(env, "Invalid argument type: expected string (db_name) or number (key_mode)");
126
105
  }
127
- // создаем новый объект dbi
128
- auto obj = dbimou::ctor.New({});
129
- auto ptr = dbimou::Unwrap(obj);
130
- ptr->attach(dbi, db_mode, key_mode,
131
- value_mode, key_flag, value_flag);
132
- return obj;
133
106
  }
134
- catch(const std::exception& e)
135
- {
136
- throw Napi::Error::New(env, std::string("txn: get_dbi ") + e.what());
107
+ // arg_count == 0: используем значения по умолчанию (строковый ключ, default db)
108
+
109
+ if (!txn_) {
110
+ throw Napi::Error::New(env, "txn already completed");
137
111
  }
138
-
139
- return env.Undefined();
112
+
113
+ MDBX_dbi dbi{};
114
+ auto flags = static_cast<MDBX_db_flags_t>(db_mode.val|key_mode.val|value_mode.val);
115
+ auto rc = mdbx_dbi_open(*this, db_name.empty() ? nullptr : db_name.c_str(), flags, &dbi);
116
+ if (rc != MDBX_SUCCESS) {
117
+ throw Napi::Error::New(env, std::string("mdbx_dbi_open: ") + mdbx_strerror(rc));
118
+ }
119
+ // создаем новый объект dbi
120
+ auto obj = dbimou::ctor.New({});
121
+ auto ptr = dbimou::Unwrap(obj);
122
+ ptr->attach(dbi, db_mode, key_mode,
123
+ value_mode, key_flag, value_flag);
124
+ return obj;
140
125
  }
141
126
 
142
127
  Napi::Value txnmou::is_active(const Napi::CallbackInfo& info) {
package/src/txnmou.hpp CHANGED
@@ -10,17 +10,18 @@ class txnmou final
10
10
  : public Napi::ObjectWrap<txnmou>
11
11
  {
12
12
  private:
13
- envmou* env_{nullptr};
13
+ envmou* env_{nullptr};
14
+
15
+ // свободу txn
16
+ struct free_txn
17
+ {
18
+ void operator()(MDBX_txn *txn) const noexcept {
19
+ mdbx_txn_abort(txn);
20
+ }
21
+ };
14
22
 
15
- std::unique_ptr<MDBX_txn,
16
- txnmou_managed::free_txn> txn_{};
23
+ std::unique_ptr<MDBX_txn, free_txn> txn_{};
17
24
  txn_mode mode_{};
18
-
19
- void check() const {
20
- if (!txn_) {
21
- throw std::runtime_error("txn: inactive");
22
- }
23
- }
24
25
 
25
26
  // Уменьшает счетчик транзакций
26
27
  void dec_counter() noexcept;
@@ -40,8 +41,6 @@ public:
40
41
  }
41
42
  }
42
43
 
43
- //void Finalize(Napi::Env env) { fprintf(stderr, "txnmou::Finalize %p\n", this); }
44
-
45
44
  static void init(const char *class_name, Napi::Env env);
46
45
 
47
46
  Napi::Value commit(const Napi::CallbackInfo&);
@@ -53,7 +52,7 @@ public:
53
52
  return get_dbi(info, {db_mode::create});
54
53
  }
55
54
 
56
- operator MDBX_txn*() const noexcept {
55
+ operator MDBX_txn*() noexcept {
57
56
  return txn_.get();
58
57
  }
59
58
 
package/src/typemou.hpp CHANGED
@@ -4,6 +4,7 @@
4
4
  #include <mdbx.h++>
5
5
  #include <cstdint>
6
6
  #include <vector>
7
+ #include <utility>
7
8
 
8
9
  namespace mdbxmou {
9
10
 
@@ -12,57 +13,39 @@ using buffer_type = std::vector<char>;
12
13
  struct txnmou_managed final
13
14
  : mdbx::txn
14
15
  {
15
- // свободу txn
16
- struct free_txn
17
- {
18
- void operator()(MDBX_txn *txn) const noexcept {
19
- mdbx_txn_abort(txn);
20
- }
21
- };
22
-
23
- std::unique_ptr<MDBX_txn, free_txn> guard_;
24
-
25
- txnmou_managed(MDBX_txn* txn) noexcept
16
+ txnmou_managed(MDBX_txn *txn) noexcept
26
17
  : mdbx::txn(txn)
27
- , guard_(txn)
28
18
  { }
29
-
30
- // Move конструктор
31
- txnmou_managed(txnmou_managed&& other) noexcept
32
- : mdbx::txn(other.txn::handle_)
33
- , guard_(std::move(other.guard_))
34
- {
35
- other.txn::handle_ = nullptr;
36
- }
37
-
38
- // Запрет копирования
19
+
20
+ txnmou_managed(txnmou_managed &&other) noexcept
21
+ : mdbx::txn(std::exchange(other.txn::handle_, nullptr))
22
+ { }
23
+
39
24
  txnmou_managed(const txnmou_managed&) = delete;
40
25
  txnmou_managed& operator=(const txnmou_managed&) = delete;
41
26
 
42
- ~txnmou_managed() noexcept {
43
- // guard_ автоматически откатит транзакцию
44
- txn::handle_ = nullptr;
27
+ ~txnmou_managed() noexcept
28
+ {
29
+ if (txn::handle_) {
30
+ ::mdbx_txn_abort(txn::handle_);
31
+ }
45
32
  }
46
33
 
47
- // Commit транзакции
48
- void commit() {
49
- if (guard_) {
50
- auto rc = ::mdbx_txn_commit(guard_.release());
51
- txn::handle_ = nullptr;
52
- if (rc != MDBX_SUCCESS) {
53
- throw std::runtime_error(::mdbx_strerror(rc));
54
- }
34
+ void commit()
35
+ {
36
+ auto rc = ::mdbx_txn_commit(
37
+ std::exchange(txn::handle_, nullptr));
38
+ if (rc != MDBX_SUCCESS) {
39
+ throw std::runtime_error(::mdbx_strerror(rc));
55
40
  }
56
41
  }
57
42
 
58
- // Abort транзакции
59
- void abort() {
60
- if (guard_) {
61
- auto rc = ::mdbx_txn_abort(guard_.release());
62
- txn::handle_ = nullptr;
63
- if (rc != MDBX_SUCCESS) {
64
- throw std::runtime_error(::mdbx_strerror(rc));
65
- }
43
+ void abort()
44
+ {
45
+ auto rc = ::mdbx_txn_abort(
46
+ std::exchange(txn::handle_, nullptr));
47
+ if (rc != MDBX_SUCCESS) {
48
+ throw std::runtime_error(::mdbx_strerror(rc));
66
49
  }
67
50
  }
68
51
  };
@@ -70,36 +53,22 @@ struct txnmou_managed final
70
53
  struct cursormou_managed final
71
54
  : mdbx::cursor
72
55
  {
73
- // свободу курсору
74
- struct free_cursor
75
- {
76
- void operator()(MDBX_cursor* c) const noexcept {
77
- if (c) ::mdbx_cursor_close(c);
78
- }
79
- };
80
-
81
- std::unique_ptr<MDBX_cursor, free_cursor> guard_;
82
-
83
56
  explicit cursormou_managed(MDBX_cursor* cursor) noexcept
84
57
  : mdbx::cursor(cursor)
85
- , guard_(cursor)
58
+ { }
59
+
60
+ cursormou_managed(cursormou_managed &&other) noexcept
61
+ : mdbx::cursor(std::exchange(other.cursor::handle_, nullptr))
86
62
  { }
87
63
 
88
- // Move конструктор
89
- cursormou_managed(cursormou_managed&& other) noexcept
90
- : mdbx::cursor(other.cursor::handle_)
91
- , guard_(std::move(other.guard_))
92
- {
93
- other.cursor::handle_ = nullptr;
94
- }
95
-
96
- // Запрет копирования
97
64
  cursormou_managed(const cursormou_managed&) = delete;
98
65
  cursormou_managed& operator=(const cursormou_managed&) = delete;
99
66
 
100
- ~cursormou_managed() noexcept {
101
- // guard_ автоматически закроет курсор
102
- cursor::handle_ = nullptr;
67
+ ~cursormou_managed() noexcept
68
+ {
69
+ if (cursor::handle_) {
70
+ ::mdbx_cursor_close(cursor::handle_);
71
+ }
103
72
  }
104
73
  };
105
74
 
package/src/valuemou.hpp CHANGED
@@ -4,11 +4,18 @@
4
4
 
5
5
  namespace mdbxmou {
6
6
 
7
- class valuemou
8
- : public mdbx::slice
7
+ // Обёртка над mdbx::slice — НЕ ВЛАДЕЕТ памятью.
8
+ // Хранит указатель на внешний буфер (buffer_type или stack).
9
+ // Copy и move эквивалентны — просто копируют указатель и размер.
10
+ // ВАЖНО: не использовать после уничтожения буфера!
11
+ struct valuemou
12
+ : mdbx::slice
9
13
  {
10
- public:
11
14
  valuemou() = default;
15
+ valuemou(const valuemou&) = default;
16
+ valuemou& operator=(const valuemou&) = default;
17
+ valuemou(valuemou&&) = default;
18
+ valuemou& operator=(valuemou&&) = default;
12
19
 
13
20
  valuemou(const mdbx::slice& arg0) noexcept
14
21
  : mdbx::slice{arg0}
@@ -76,11 +83,14 @@ public:
76
83
  }
77
84
  };
78
85
 
79
- class keymou final
80
- : public valuemou
86
+ struct keymou final
87
+ : valuemou
81
88
  {
82
- public:
83
89
  keymou() = default;
90
+ keymou(const keymou&) = default;
91
+ keymou& operator=(const keymou&) = default;
92
+ keymou(keymou&&) = default;
93
+ keymou& operator=(keymou&&) = default;
84
94
 
85
95
  keymou(const valuemou& arg0) noexcept
86
96
  : valuemou{arg0}
@@ -1,450 +0,0 @@
1
- #include "envmou.hpp"
2
- #include "txnmou.hpp"
3
- #include "async/envmou_copy_to.hpp"
4
- #include "async/envmou_query.hpp"
5
- #include "async/envmou_open.hpp"
6
- #include "async/envmou_keys.hpp"
7
- #include "async/envmou_close.hpp"
8
-
9
- #ifdef _WIN32
10
- #include <windows.h>
11
- #else
12
- #include <pthread.h>
13
- #endif
14
-
15
- namespace mdbxmou {
16
-
17
- Napi::FunctionReference envmou::ctor{};
18
-
19
- void envmou::init(const char *class_name, Napi::Env env, Napi::Object exports)
20
- {
21
- auto func = DefineClass(env, class_name, {
22
- InstanceMethod("open", &envmou::open),
23
- InstanceMethod("openSync", &envmou::open_sync),
24
- InstanceMethod("close", &envmou::close),
25
- InstanceMethod("closeSync", &envmou::close_sync),
26
- InstanceMethod("copyTo", &envmou::copy_to),
27
- InstanceMethod("copyToSync", &envmou::copy_to_sync),
28
- InstanceMethod("version", &envmou::get_version),
29
- InstanceMethod("startRead", &envmou::start_read),
30
- InstanceMethod("startWrite", &envmou::start_write),
31
- InstanceMethod("query", &envmou::query),
32
- InstanceMethod("keys", &envmou::keys)
33
- });
34
- ctor = Napi::Persistent(func);
35
- ctor.SuppressDestruct();
36
- exports.Set(class_name, func);
37
- }
38
-
39
- mdbx::env::geometry envmou::parse_geometry(const Napi::Value& arg0)
40
- {
41
- mdbx::env::geometry geom{};
42
-
43
- auto obj = arg0.As<Napi::Object>();
44
-
45
- if (obj.Has("fixedSize")) {
46
- auto value = obj.Get("fixedSize").As<Napi::Number>();
47
- auto fixed_size = static_cast<intptr_t>(value.Int64Value());
48
- return geom.make_fixed(fixed_size);
49
- } else if (obj.Has("dynamicSize")) {
50
- auto arr = obj.Get("dynamicSize").As<Napi::Array>();
51
- if (arr.Length() != 2) {
52
- throw Napi::TypeError::New(obj.Env(), "dynamicSize must be an array of two numbers");
53
- }
54
- auto v1 = arr.Get(0u).As<Napi::Number>();
55
- auto v2 = arr.Get(1u).As<Napi::Number>();
56
- auto size_lower = static_cast<intptr_t>(v1.Int64Value());
57
- auto size_upper = static_cast<intptr_t>(v2.Int64Value());
58
- return geom.make_dynamic(size_lower, size_upper);
59
- } else {
60
- if (obj.Has("sizeNow")) {
61
- auto value = obj.Get("sizeNow").As<Napi::Number>();
62
- geom.size_now = static_cast<intptr_t>(value.Int64Value());
63
- }
64
- if (obj.Has("sizeUpper")) {
65
- auto value = obj.Get("sizeUpper").As<Napi::Number>();
66
- geom.size_upper = static_cast<intptr_t>(value.Int64Value());
67
- }
68
- if (obj.Has("growthStep")) {
69
- auto value = obj.Get("growthStep").As<Napi::Number>();
70
- geom.growth_step = static_cast<intptr_t>(value.Int64Value());
71
- }
72
- if (obj.Has("shrinkThreshold")) {
73
- auto value = obj.Get("shrinkThreshold").As<Napi::Number>();
74
- geom.shrink_threshold = static_cast<intptr_t>(value.Int64Value());
75
- }
76
- if (obj.Has("pageSize")) {
77
- auto value = obj.Get("pageSize").As<Napi::Number>();
78
- geom.pagesize = static_cast<intptr_t>(value.Int64Value());
79
- }
80
- }
81
- return geom;
82
- }
83
-
84
- env_arg0 envmou::parse(const Napi::Value& arg0)
85
- {
86
- env_arg0 rc;
87
-
88
- auto obj = arg0.As<Napi::Object>();
89
-
90
- rc.path = obj.Get("path").As<Napi::String>().Utf8Value();
91
- if (obj.Has("maxDbi")) {
92
- auto value = obj.Get("maxDbi").As<Napi::Number>();
93
- rc.max_dbi = static_cast<MDBX_dbi>(value.Uint32Value());
94
- }
95
-
96
- if (obj.Has("geometry")) {
97
- rc.geom = parse_geometry(obj.Get("geometry"));
98
- }
99
-
100
- if (obj.Has("flags")) {
101
- rc.flag = env_flag::parse(obj.Get("flags"));
102
- }
103
-
104
- if (obj.Has("mode")) {
105
- auto value = obj.Get("mode").As<Napi::Number>();
106
- rc.file_mode = static_cast<mdbx_mode_t>(value.Int32Value());
107
- }
108
-
109
- if (obj.Has("keyFlag")) {
110
- rc.key_flag = base_flag::parse_key(obj.Get("keyFlag"));
111
- }
112
-
113
- if (obj.Has("valueFlag")) {
114
- rc.value_flag = base_flag::parse_value(obj.Get("valueFlag"));
115
- }
116
-
117
- return rc;
118
- }
119
-
120
- Napi::Value envmou::open(const Napi::CallbackInfo& info)
121
- {
122
- auto env = info.Env();
123
- auto arg0 = parse(info[0].As<Napi::Object>());
124
-
125
- try {
126
- // асинхронный вызов разлочится внутри worker'a
127
- if (!try_lock()) {
128
- throw std::runtime_error("in progress");
129
- }
130
-
131
- if (is_open()) {
132
- throw std::runtime_error("already opened");
133
- }
134
-
135
- auto* worker = new async_open(env, *this, arg0);
136
- Napi::Promise promise = worker->GetPromise();
137
- worker->Queue();
138
- return promise;
139
- } catch (const std::exception& e) {
140
- unlock();
141
- throw Napi::Error::New(env, e.what());
142
- } catch (...) {
143
- unlock();
144
- throw;
145
- }
146
-
147
- return env.Undefined();
148
- }
149
-
150
- Napi::Value envmou::open_sync(const Napi::CallbackInfo& info)
151
- {
152
- auto env = info.Env();
153
- auto arg0 = parse(info[0].As<Napi::Object>());
154
-
155
- try {
156
- lock_guard l(*this);
157
- if (is_open()) {
158
- throw std::runtime_error("already opened");
159
- }
160
- attach(create_and_open(arg0), arg0);
161
- } catch (const std::exception& e) {
162
- throw Napi::Error::New(env, e.what());
163
- }
164
-
165
- return env.Undefined();
166
- }
167
-
168
- MDBX_env* envmou::create_and_open(const env_arg0& arg0)
169
- {
170
- MDBX_env *env;
171
- auto rc = mdbx_env_create(&env);
172
- if (rc != MDBX_SUCCESS) {
173
- throw std::runtime_error(mdbx_strerror(rc));
174
- }
175
-
176
- rc = mdbx_env_set_maxdbs(env, arg0.max_dbi);
177
- if (rc != MDBX_SUCCESS) {
178
- mdbx_env_close(env);
179
- throw std::runtime_error(mdbx_strerror(rc));
180
- }
181
-
182
- rc = mdbx_env_set_maxreaders(env, arg0.max_readers);
183
- if (rc != MDBX_SUCCESS) {
184
- mdbx_env_close(env);
185
- throw std::runtime_error(mdbx_strerror(rc));
186
- }
187
-
188
- auto& geom = arg0.geom;
189
- rc = mdbx_env_set_geometry(env, geom.size_lower, geom.size_now,
190
- geom.size_upper, geom.growth_step, geom.shrink_threshold, geom.pagesize);
191
- if (rc != MDBX_SUCCESS) {
192
- mdbx_env_close(env);
193
- throw std::runtime_error(mdbx_strerror(rc));
194
- }
195
-
196
- #ifdef _WIN32
197
- auto id = static_cast<std::uint32_t>(GetCurrentThreadId());
198
- #else
199
- auto id = static_cast<std::uint32_t>(pthread_self());
200
- #endif
201
- // выдадим параметры mode, flag и id потока в котором открывается env
202
- rc = mdbx_env_open(env, arg0.path.c_str(), arg0.flag, arg0.file_mode);
203
- if (rc != MDBX_SUCCESS) {
204
- mdbx_env_close(env);
205
- throw std::runtime_error(mdbx_strerror(rc));
206
- }
207
-
208
- return env;
209
- }
210
-
211
- void envmou::attach(MDBX_env* env, const env_arg0& arg0)
212
- {
213
- arg0_ = arg0;
214
-
215
- auto rc = mdbx_env_set_userctx(env, &arg0_);
216
- if (rc != MDBX_SUCCESS) {
217
- mdbx_env_close(env);
218
- throw std::runtime_error(mdbx_strerror(rc));
219
- }
220
-
221
- env_.reset(env);
222
- }
223
-
224
- Napi::Value envmou::close(const Napi::CallbackInfo& info)
225
- {
226
- auto env = info.Env();
227
- try {
228
- // асинхронный вызов разлочится внутри worker'a
229
- if (!try_lock()) {
230
- throw std::runtime_error("in progress");
231
- }
232
-
233
- if (!is_open()) {
234
- return env.Undefined();
235
- }
236
-
237
- if (trx_count_.load() > 0) {
238
- throw std::runtime_error("active transactions");
239
- }
240
-
241
- auto* worker = new async_close(env, *this);
242
- Napi::Promise promise = worker->GetPromise();
243
- worker->Queue();
244
- return promise;
245
- } catch (const std::exception& e) {
246
- unlock();
247
- throw Napi::Error::New(env, e.what());
248
- } catch (...) {
249
- unlock();
250
- throw;
251
- }
252
-
253
- return env.Undefined();
254
- }
255
-
256
- Napi::Value envmou::close_sync(const Napi::CallbackInfo& info)
257
- {
258
- auto env = info.Env();
259
- try {
260
- lock_guard l(*this);
261
-
262
- do_close();
263
-
264
- } catch (const std::exception& e) {
265
- throw Napi::Error::New(env, e.what());
266
- }
267
- return env.Undefined();
268
- }
269
-
270
- Napi::Value envmou::copy_to_sync(const Napi::CallbackInfo& info)
271
- {
272
- auto env = info.Env();
273
-
274
- if (info.Length() < 1 || !info[0].IsString()) {
275
- throw Napi::TypeError::New(env, "expected a string argument for the destination path");
276
- }
277
-
278
- MDBX_copy_flags_t flags{MDBX_CP_COMPACT};
279
- if ((info.Length() > 1) && info[1].IsNumber()) {
280
- flags = static_cast<MDBX_copy_flags_t>(info[1].As<Napi::Number>().Uint32Value());
281
- }
282
-
283
- try {
284
- auto dest_path = info[0].As<Napi::String>().Utf8Value();
285
-
286
- lock_guard l(*this);
287
-
288
- check();
289
-
290
- auto rc = mdbx_env_copy(*this, dest_path.c_str(), flags);
291
- if (rc != MDBX_SUCCESS) {
292
- throw Napi::Error::New(env, mdbx_strerror(rc));
293
- }
294
-
295
- } catch (const std::exception& e) {
296
- throw Napi::Error::New(env, e.what());
297
- }
298
-
299
- return env.Undefined();
300
- }
301
-
302
- Napi::Value envmou::copy_to(const Napi::CallbackInfo& info)
303
- {
304
- Napi::Env env = info.Env();
305
-
306
- if (info.Length() < 1 || !info[0].IsString()) {
307
- throw Napi::TypeError::New(env, "copyTo(path: string[, flags?: number]) -> Promise<void>");
308
- }
309
-
310
- MDBX_copy_flags_t flags{MDBX_CP_COMPACT};
311
- if (info.Length() > 1 && info[1].IsNumber()) {
312
- flags = static_cast<MDBX_copy_flags_t>(info[1].As<Napi::Number>().Uint32Value());
313
- }
314
-
315
- try {
316
- auto dest = info[0].As<Napi::String>().Utf8Value();
317
-
318
- if (!try_lock()) {
319
- throw std::runtime_error("in progress");
320
- }
321
-
322
- check();
323
-
324
- auto* worker = new mdbxmou::async_copy(env, *this, std::move(dest), flags);
325
- Napi::Promise promise = worker->GetPromise();
326
- worker->Queue();
327
- return promise;
328
- } catch (const std::exception& e) {
329
- throw Napi::Error::New(env, e.what());
330
- }
331
-
332
- return env.Undefined();
333
- }
334
-
335
- Napi::Value envmou::get_version(const Napi::CallbackInfo& info)
336
- {
337
- std::string version = "mdbx v" + std::to_string(MDBX_VERSION_MAJOR);
338
- version += "." + std::to_string(MDBX_VERSION_MINOR);
339
- return Napi::Value::From(info.Env(), version);
340
- }
341
-
342
- Napi::Value envmou::start_transaction(const Napi::CallbackInfo& info, txn_mode mode)
343
- {
344
- auto env = info.Env();
345
-
346
- try {
347
- lock_guard l(*this);
348
-
349
- check();
350
-
351
- MDBX_txn* txn;
352
- auto rc = mdbx_txn_begin(*this, nullptr, mode, &txn);
353
- if (rc != MDBX_SUCCESS) {
354
- throw Napi::Error::New(env, std::string("Env: ") + mdbx_strerror(rc));
355
- }
356
-
357
- // Создаем новый объект txnmou
358
- auto txn_obj = txnmou::ctor.New({});
359
- auto txn_wrapper = txnmou::Unwrap(txn_obj);
360
- txn_wrapper->attach(*this, txn, mode, nullptr);
361
-
362
- return txn_obj;
363
- } catch (const std::exception& e) {
364
- throw Napi::Error::New(env, e.what());
365
- }
366
- }
367
-
368
- Napi::Value envmou::query(const Napi::CallbackInfo& info)
369
- {
370
- Napi::Env env = info.Env();
371
-
372
- txn_mode mode{};
373
-
374
- if (info.Length() < 1) {
375
- throw Napi::TypeError::New(env, "expected array of requests");
376
- }
377
-
378
- if (info.Length() > 1 || info[1].IsNumber()) {
379
- mode = txn_mode::parse(info[1].As<Napi::Number>());
380
- }
381
-
382
- try
383
- {
384
- lock_guard lock(*this);
385
-
386
- check();
387
-
388
- auto conf = get_env_userctx(*this);
389
-
390
- auto arg0 = info[0];
391
- query_request query = parse_query(mode, arg0);
392
- auto* worker = new async_query(env, *this, mode,
393
- std::move(query), arg0.IsObject());
394
- auto promise = worker->GetPromise();
395
- worker->Queue();
396
-
397
- // Увеличиваем счетчик транзакций после успешного создания
398
- ++(*this);
399
-
400
- return promise;
401
- } catch (const std::exception& e) {
402
- throw Napi::Error::New(env, e.what());
403
- } catch (...) {
404
- throw Napi::Error::New(env, "envmou::query");
405
- }
406
- return env.Undefined();
407
- }
408
-
409
- Napi::Value envmou::keys(const Napi::CallbackInfo& info)
410
- {
411
- Napi::Env env = info.Env();
412
-
413
- txn_mode mode{};
414
-
415
- if (info.Length() < 1) {
416
- throw Napi::TypeError::New(env,
417
- "expected array of requests: [{ db: String, db_mode: Number, key_mode: Number, key_flag: Number, value_mode: Number, value_flag: Number }, ...]");
418
- }
419
-
420
- if (info.Length() > 1 || info[1].IsNumber()) {
421
- mode = txn_mode::parse(info[1].As<Napi::Number>());
422
- }
423
-
424
- try
425
- {
426
- lock_guard lock(*this);
427
-
428
- check();
429
-
430
- auto arg0 = info[0];
431
- keys_request query = parse_keys(arg0);
432
-
433
- auto* worker = new async_keys(env, *this, mode,
434
- std::move(query), arg0.IsObject());
435
- auto promise = worker->GetPromise();
436
- worker->Queue();
437
-
438
- // Увеличиваем счетчик транзакций после успешного создания
439
- ++(*this);
440
-
441
- return promise;
442
- } catch (const std::exception& e) {
443
- throw Napi::Error::New(env, e.what());
444
- } catch (...) {
445
- throw Napi::Error::New(env, "envmou::keys");
446
- }
447
- return env.Undefined();
448
- }
449
-
450
- } // namespace mdbxmou
@@ -1,116 +0,0 @@
1
- #pragma once
2
-
3
- #include "txnmou.hpp"
4
- #include <memory>
5
- #include <atomic>
6
- #include <mutex>
7
-
8
- namespace mdbxmou {
9
-
10
- // Forward declaration
11
- class txnmou;
12
-
13
- class envmou final
14
- : public Napi::ObjectWrap<envmou>
15
- {
16
- static Napi::FunctionReference ctor;
17
- static mdbx::env::geometry parse_geometry(const Napi::Value& obj);
18
- static env_arg0 parse(const Napi::Value& obj);
19
-
20
- struct free_env {
21
- void operator()(MDBX_env* env) const {
22
- auto rc = mdbx_env_close(env);
23
- if (rc != MDBX_SUCCESS) {
24
- fprintf(stderr, "mdbx_env_close %s\n", mdbx_strerror(rc));
25
- }
26
- }
27
- };
28
-
29
- std::unique_ptr<MDBX_env, free_env> env_{};
30
- std::atomic<std::size_t> trx_count_{};
31
- env_arg0 arg0_{};
32
- std::mutex lock_{};
33
-
34
- bool is_open() const {
35
- return env_ != nullptr;
36
- }
37
-
38
- // Inline метод для проверки что база данных открыта
39
- void check() const {
40
- if (!is_open()) {
41
- throw std::runtime_error("closed");
42
- }
43
- }
44
-
45
- // Общий метод для создания транзакций
46
- Napi::Value start_transaction(const Napi::CallbackInfo& info, txn_mode mode);
47
-
48
- public:
49
- envmou(const Napi::CallbackInfo& i)
50
- : ObjectWrap{i}
51
- { }
52
-
53
- static MDBX_env* create_and_open(const env_arg0& arg0);
54
- void attach(MDBX_env* env, const env_arg0& arg0);
55
-
56
- operator MDBX_env*() const noexcept {
57
- return env_.get();
58
- }
59
-
60
- static void init(const char *class_name, Napi::Env env, Napi::Object exports);
61
-
62
- envmou& operator++() noexcept {
63
- ++trx_count_;
64
- return *this;
65
- }
66
-
67
- envmou& operator--() noexcept {
68
- --trx_count_;
69
- return *this;
70
- }
71
-
72
- Napi::Value open(const Napi::CallbackInfo&);
73
- Napi::Value open_sync(const Napi::CallbackInfo&);
74
- Napi::Value close(const Napi::CallbackInfo&);
75
- Napi::Value close_sync(const Napi::CallbackInfo&);
76
- Napi::Value get_version(const Napi::CallbackInfo&);
77
- Napi::Value copy_to_sync(const Napi::CallbackInfo&);
78
- Napi::Value copy_to(const Napi::CallbackInfo&);
79
- // метод для групповых вставок или чтения
80
- // внутри транзакция, получение db и чтение/запись
81
- Napi::Value query(const Napi::CallbackInfo&);
82
- Napi::Value keys(const Napi::CallbackInfo&);
83
-
84
- // Методы для создания транзакций
85
- Napi::Value start_read(const Napi::CallbackInfo& info) {
86
- return start_transaction(info, {txn_mode::ro});
87
- }
88
- Napi::Value start_write(const Napi::CallbackInfo& info) {
89
- return start_transaction(info, {});
90
- }
91
-
92
- using lock_guard = std::lock_guard<envmou>;
93
- // для защиты асинхронных операций
94
- void lock() {
95
- auto rc = lock_.try_lock();
96
- if (!rc) {
97
- throw std::runtime_error("operation in progress");
98
- }
99
- }
100
- void unlock() {
101
- lock_.unlock();
102
- }
103
- bool try_lock() {
104
- bool locked = lock_.try_lock();
105
- return locked;
106
- }
107
-
108
- void do_close() {
109
- if (trx_count_.load() > 0) {
110
- throw std::runtime_error("transaction in progress");
111
- }
112
- env_.reset();
113
- }
114
- };
115
-
116
- } // namespace mdbxmou