mdbx 0.1.0.pre.20201217111933

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: be5af4b8718ccda99471808092ab76ed497758add8a8c429d6eae16b9279c007
4
+ data.tar.gz: a183a3ba5019cc1b8cbda9ae45f803342c02011de762202728b6ab96c8f7d133
5
+ SHA512:
6
+ metadata.gz: e7e463a36ed38653930e9894404431de494cc39e6efa2ae6993681d4198b28c9ecf0c160b09f01353d6296699e9a2ea780973f983a7a32b91b6630d76ad10e2a
7
+ data.tar.gz: 300534e3d3c342019d803840e956afdc7d4e3bb114d8fe4df95918cfb85808df146979ff56af539e23ae8bfbacf9f7acab1aaaef4742b0b16a162ad6364d7bba
Binary file
Binary file
@@ -0,0 +1,9 @@
1
+ # Release History for mdbx
2
+
3
+ ---
4
+
5
+ ## v0.0.1 [2020-12-16] Mahlon E. Smith <mahlon@martini.nu>
6
+
7
+ Early release, initial basic functionality.
8
+
9
+
@@ -0,0 +1,129 @@
1
+ # Ruby MDBX
2
+
3
+ home
4
+ : https://code.martini.nu/ruby-mdbx
5
+
6
+ code
7
+ : https://code.martini.nu/ruby-mdbx
8
+
9
+ docs
10
+ : https://martini.nu/docs/ruby-mdbx
11
+
12
+ github
13
+ : https://github.com/mahlon/ruby-mdbx
14
+
15
+ gitlab
16
+ : https://gitlab.com/mahlon/ruby-mdbx
17
+
18
+ sourcehut:
19
+ : https://hg.sr.ht/~mahlon/ruby-mdbx
20
+
21
+
22
+ ## Description
23
+
24
+ This is a Ruby (MRI) binding for the libmdbx database library.
25
+
26
+ libmdbx is an extremely fast, compact, powerful, embedded, transactional
27
+ key-value database, with permissive license. libmdbx has a specific set
28
+ of properties and capabilities, focused on creating unique lightweight
29
+ solutions.
30
+
31
+ - Allows a swarm of multi-threaded processes to ACIDly read and update
32
+ several key-value maps and multimaps in a locally-shared database.
33
+
34
+ - Provides extraordinary performance, minimal overhead through
35
+ Memory-Mapping and Olog(N) operations costs by virtue of B+ tree.
36
+
37
+ - Requires no maintenance and no crash recovery since it doesn't use
38
+ WAL, but that might be a caveat for write-intensive workloads with
39
+ durability requirements.
40
+
41
+ - Compact and friendly for fully embedding. Only ≈25KLOC of C11,
42
+ ≈64K x86 binary code of core, no internal threads neither server
43
+ process(es), but implements a simplified variant of the Berkeley DB
44
+ and dbm API.
45
+
46
+ - Enforces serializability for writers just by single mutex and
47
+ affords wait-free for parallel readers without atomic/interlocked
48
+ operations, while writing and reading transactions do not block each
49
+ other.
50
+
51
+ - Guarantee data integrity after crash unless this was explicitly
52
+ neglected in favour of write performance.
53
+
54
+ - Supports Linux, Windows, MacOS, Android, iOS, FreeBSD, DragonFly,
55
+ Solaris, OpenSolaris, OpenIndiana, NetBSD, OpenBSD and other systems
56
+ compliant with POSIX.1-2008.
57
+
58
+ - Historically, libmdbx is a deeply revised and extended descendant
59
+ of the amazing Lightning Memory-Mapped Database. libmdbx inherits
60
+ all benefits from LMDB, but resolves some issues and adds a set of
61
+ improvements.
62
+
63
+
64
+ ### Examples
65
+
66
+ [forthcoming]
67
+
68
+
69
+ ## Prerequisites
70
+
71
+ * Ruby 2.6+
72
+ * libmdbx (https://github.com/erthink/libmdbx)
73
+
74
+
75
+ ## Installation
76
+
77
+ $ gem install mdbx
78
+
79
+
80
+ ## Contributing
81
+
82
+ You can check out the current development source with Mercurial via its
83
+ [home repo](https://code.martini.nu/ruby-mdbx), or with Git at its
84
+ [project page](https://gitlab.com/mahlon/ruby-mdbx).
85
+
86
+ After checking out the source, run:
87
+
88
+ $ gem install -Ng
89
+ $ rake setup
90
+
91
+ This will install dependencies, and do any other necessary setup for
92
+ development.
93
+
94
+
95
+ ## Authors
96
+
97
+ - Mahlon E. Smith <mahlon@martini.nu>
98
+
99
+
100
+ ## License
101
+
102
+ Copyright (c) 2020, Mahlon E. Smith
103
+ All rights reserved.
104
+
105
+ Redistribution and use in source and binary forms, with or without
106
+ modification, are permitted provided that the following conditions are met:
107
+
108
+ * Redistributions of source code must retain the above copyright notice,
109
+ this list of conditions and the following disclaimer.
110
+
111
+ * Redistributions in binary form must reproduce the above copyright notice,
112
+ this list of conditions and the following disclaimer in the documentation
113
+ and/or other materials provided with the distribution.
114
+
115
+ * Neither the name of the author/s, nor the names of the project's
116
+ contributors may be used to endorse or promote products derived from this
117
+ software without specific prior written permission.
118
+
119
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
120
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
121
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
122
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
123
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
124
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
125
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
126
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
127
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
128
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
129
+
@@ -0,0 +1,511 @@
1
+ /* vim: set noet sta sw=4 ts=4 : */
2
+
3
+ #include "mdbx_ext.h"
4
+
5
+ /* VALUE str = rb_sprintf( "path: %+"PRIsVALUE", opts: %+"PRIsVALUE, path, opts ); */
6
+ /* printf( "%s\n", StringValueCStr(str) ); */
7
+
8
+ VALUE rmdbx_cDatabase;
9
+
10
+
11
+ /* Shortcut for fetching current DB variables.
12
+ *
13
+ */
14
+ #define UNWRAP_DB( val, db ) \
15
+ rmdbx_db_t *db; \
16
+ TypedData_Get_Struct( val, rmdbx_db_t, &rmdbx_db_data, db );
17
+
18
+
19
+ /*
20
+ * A struct encapsulating an instance's DB state.
21
+ */
22
+ struct rmdbx_db {
23
+ MDBX_env *env;
24
+ MDBX_dbi dbi;
25
+ MDBX_txn *txn;
26
+ MDBX_cursor *cursor;
27
+ int env_flags;
28
+ int mode;
29
+ int open;
30
+ int max_collections;
31
+ char *path;
32
+ char *subdb;
33
+ };
34
+ typedef struct rmdbx_db rmdbx_db_t;
35
+
36
+
37
+ /*
38
+ * Ruby allocation hook.
39
+ */
40
+ void rmdbx_free( void *db ); /* forward declaration */
41
+ static const rb_data_type_t rmdbx_db_data = {
42
+ .wrap_struct_name = "MDBX::Database::Data",
43
+ .function = { .dfree = rmdbx_free },
44
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
45
+ };
46
+
47
+
48
+ /*
49
+ * Allocate a DB environment onto the stack.
50
+ */
51
+ VALUE
52
+ rmdbx_alloc( VALUE klass )
53
+ {
54
+ rmdbx_db_t *new = RB_ALLOC( rmdbx_db_t );
55
+ return TypedData_Make_Struct( klass, rmdbx_db_t, &rmdbx_db_data, new );
56
+ }
57
+
58
+
59
+ /*
60
+ * Ensure all database file descriptors are collected and
61
+ * removed.
62
+ */
63
+ void
64
+ rmdbx_close_all( rmdbx_db_t* db )
65
+ {
66
+ if ( db->cursor ) mdbx_cursor_close( db->cursor );
67
+ if ( db->txn ) mdbx_txn_abort( db->txn );
68
+ if ( db->dbi ) mdbx_dbi_close( db->env, db->dbi );
69
+ if ( db->env ) mdbx_env_close( db->env );
70
+ db->open = 0;
71
+ }
72
+
73
+
74
+ /*
75
+ * Cleanup a previously allocated DB environment.
76
+ */
77
+ void
78
+ rmdbx_free( void *db )
79
+ {
80
+ if ( db ) {
81
+ rmdbx_close_all( db );
82
+ free( db );
83
+ }
84
+ }
85
+
86
+
87
+ /*
88
+ * Cleanly close an opened database from Ruby.
89
+ */
90
+ VALUE
91
+ rmdbx_close( VALUE self )
92
+ {
93
+ UNWRAP_DB( self, db );
94
+ rmdbx_close_all( db );
95
+ return Qtrue;
96
+ }
97
+
98
+
99
+ /*
100
+ * Open the DB environment handle.
101
+ */
102
+ VALUE
103
+ rmdbx_open_env( VALUE self )
104
+ {
105
+ int rc;
106
+ UNWRAP_DB( self, db );
107
+ rmdbx_close_all( db );
108
+
109
+ /* Allocate an mdbx environment.
110
+ */
111
+ rc = mdbx_env_create( &db->env );
112
+ if ( rc != MDBX_SUCCESS )
113
+ rb_raise( rmdbx_eDatabaseError, "mdbx_env_create: (%d) %s", rc, mdbx_strerror(rc) );
114
+
115
+ /* Set the maximum number of named databases for the environment. */
116
+ // FIXME: potenially more env setups here? maxreaders, pagesize?
117
+ mdbx_env_set_maxdbs( db->env, db->max_collections );
118
+
119
+ rc = mdbx_env_open( db->env, db->path, db->env_flags, db->mode );
120
+ if ( rc != MDBX_SUCCESS ) {
121
+ rmdbx_close( self );
122
+ rb_raise( rmdbx_eDatabaseError, "mdbx_env_open: (%d) %s", rc, mdbx_strerror(rc) );
123
+ }
124
+ db->open = 1;
125
+
126
+ return Qtrue;
127
+ }
128
+
129
+
130
+ /*
131
+ * call-seq:
132
+ * db.closed? #=> false
133
+ *
134
+ * Predicate: return true if the database environment is closed.
135
+ */
136
+ VALUE
137
+ rmdbx_closed_p( VALUE self )
138
+ {
139
+ UNWRAP_DB( self, db );
140
+ return db->open == 1 ? Qfalse : Qtrue;
141
+ }
142
+
143
+
144
+ /*
145
+ * Open a new database transaction.
146
+ *
147
+ * +rwflag+ must be either MDBX_TXN_RDONLY or MDBX_TXN_READWRITE.
148
+ */
149
+ void
150
+ rmdbx_open_txn( VALUE self, int rwflag )
151
+ {
152
+ int rc;
153
+ UNWRAP_DB( self, db );
154
+
155
+ rc = mdbx_txn_begin( db->env, NULL, rwflag, &db->txn);
156
+ if ( rc != MDBX_SUCCESS ) {
157
+ rmdbx_close( self );
158
+ rb_raise( rmdbx_eDatabaseError, "mdbx_txn_begin: (%d) %s", rc, mdbx_strerror(rc) );
159
+ }
160
+
161
+ if ( db->dbi == 0 ) {
162
+ // FIXME: dbi_flags
163
+ rc = mdbx_dbi_open( db->txn, db->subdb, MDBX_CREATE, &db->dbi );
164
+ if ( rc != MDBX_SUCCESS ) {
165
+ rmdbx_close( self );
166
+ rb_raise( rmdbx_eDatabaseError, "mdbx_dbi_open: (%d) %s", rc, mdbx_strerror(rc) );
167
+ }
168
+ }
169
+
170
+ return;
171
+ }
172
+
173
+
174
+ /*
175
+ * call-seq:
176
+ * db.clear
177
+ *
178
+ * Empty the database (or collection) on disk. Unrecoverable!
179
+ */
180
+ VALUE
181
+ rmdbx_clear( VALUE self )
182
+ {
183
+ UNWRAP_DB( self, db );
184
+
185
+ rmdbx_open_txn( self, MDBX_TXN_READWRITE );
186
+ int rc = mdbx_drop( db->txn, db->dbi, true );
187
+
188
+ if ( rc != MDBX_SUCCESS )
189
+ rb_raise( rmdbx_eDatabaseError, "mdbx_drop: (%d) %s", rc, mdbx_strerror(rc) );
190
+
191
+ mdbx_txn_commit( db->txn );
192
+
193
+ /* Refresh the environment handles. */
194
+ rmdbx_open_env( self );
195
+
196
+ return Qnil;
197
+ }
198
+
199
+
200
+ /*
201
+ * Given a ruby +arg+, convert and return a structure
202
+ * suitable for usage as a key for mdbx. All keys are explicitly
203
+ * converted to strings.
204
+ */
205
+ MDBX_val
206
+ rmdbx_key_for( VALUE arg )
207
+ {
208
+ MDBX_val rv;
209
+
210
+ arg = rb_funcall( arg, rb_intern("to_s"), 0 );
211
+ rv.iov_len = RSTRING_LEN( arg );
212
+ rv.iov_base = StringValuePtr( arg );
213
+
214
+ return rv;
215
+ }
216
+
217
+
218
+ /*
219
+ * Given a ruby +arg+, convert and return a structure
220
+ * suitable for usage as a value for mdbx.
221
+ */
222
+ MDBX_val
223
+ rmdbx_val_for( VALUE self, VALUE arg )
224
+ {
225
+ MDBX_val rv;
226
+ VALUE serialize_proc;
227
+
228
+ serialize_proc = rb_iv_get( self, "@serializer" );
229
+ if ( ! NIL_P( serialize_proc ) )
230
+ arg = rb_funcall( serialize_proc, rb_intern("call"), 1, arg );
231
+
232
+ rv.iov_len = RSTRING_LEN( arg );
233
+ rv.iov_base = StringValuePtr( arg );
234
+
235
+ return rv;
236
+ }
237
+
238
+
239
+ /* call-seq:
240
+ * db.keys #=> [ 'key1', 'key2', ... ]
241
+ *
242
+ * Return an array of all keys in the current collection.
243
+ */
244
+ VALUE
245
+ rmdbx_keys( VALUE self )
246
+ {
247
+ UNWRAP_DB( self, db );
248
+ VALUE rv = rb_ary_new();
249
+ MDBX_val key, data;
250
+ int rc;
251
+
252
+ if ( ! db->open ) rb_raise( rmdbx_eDatabaseError, "Closed database." );
253
+
254
+ rmdbx_open_txn( self, MDBX_TXN_RDONLY );
255
+ rc = mdbx_cursor_open( db->txn, db->dbi, &db->cursor);
256
+
257
+ if ( rc != MDBX_SUCCESS ) {
258
+ rmdbx_close( self );
259
+ rb_raise( rmdbx_eDatabaseError, "Unable to open cursor: (%d) %s", rc, mdbx_strerror(rc) );
260
+ }
261
+
262
+ rc = mdbx_cursor_get( db->cursor, &key, &data, MDBX_FIRST );
263
+ if ( rc == MDBX_SUCCESS ) {
264
+ rb_ary_push( rv, rb_str_new( key.iov_base, key.iov_len ) );
265
+ while ( mdbx_cursor_get( db->cursor, &key, &data, MDBX_NEXT ) == 0 ) {
266
+ rb_ary_push( rv, rb_str_new( key.iov_base, key.iov_len ) );
267
+ }
268
+ }
269
+
270
+ mdbx_cursor_close( db->cursor );
271
+ db->cursor = NULL;
272
+ mdbx_txn_abort( db->txn );
273
+ return rv;
274
+ }
275
+
276
+
277
+ /* call-seq:
278
+ * db[ 'key' ] #=> value
279
+ *
280
+ * Convenience method: return a single value for +key+ immediately.
281
+ */
282
+ VALUE
283
+ rmdbx_get_val( VALUE self, VALUE key )
284
+ {
285
+ int rc;
286
+ VALUE deserialize_proc;
287
+ UNWRAP_DB( self, db );
288
+
289
+ if ( ! db->open ) rb_raise( rmdbx_eDatabaseError, "Closed database." );
290
+
291
+ rmdbx_open_txn( self, MDBX_TXN_RDONLY );
292
+
293
+ MDBX_val ckey = rmdbx_key_for( key );
294
+ MDBX_val data;
295
+ rc = mdbx_get( db->txn, db->dbi, &ckey, &data );
296
+ mdbx_txn_abort( db->txn );
297
+
298
+ switch ( rc ) {
299
+ case MDBX_SUCCESS:
300
+ deserialize_proc = rb_iv_get( self, "@deserializer" );
301
+ VALUE rv = rb_str_new( data.iov_base, data.iov_len );
302
+ if ( ! NIL_P( deserialize_proc ) )
303
+ return rb_funcall( deserialize_proc, rb_intern("call"), 1, rv );
304
+ return rv;
305
+
306
+ case MDBX_NOTFOUND:
307
+ return Qnil;
308
+
309
+ default:
310
+ rmdbx_close( self );
311
+ rb_raise( rmdbx_eDatabaseError, "Unable to fetch value: (%d) %s", rc, mdbx_strerror(rc) );
312
+ }
313
+ }
314
+
315
+
316
+ /* call-seq:
317
+ * db[ 'key' ] = value #=> value
318
+ *
319
+ * Convenience method: set a single value for +key+
320
+ */
321
+ VALUE
322
+ rmdbx_put_val( VALUE self, VALUE key, VALUE val )
323
+ {
324
+ int rc;
325
+ UNWRAP_DB( self, db );
326
+
327
+ if ( ! db->open ) rb_raise( rmdbx_eDatabaseError, "Closed database." );
328
+
329
+ rmdbx_open_txn( self, MDBX_TXN_READWRITE );
330
+
331
+ MDBX_val ckey = rmdbx_key_for( key );
332
+
333
+ // FIXME: DUPSORT is enabled -- different api?
334
+ // See: MDBX_NODUPDATA / MDBX_NOOVERWRITE
335
+ if ( NIL_P(val) ) { /* remove if set to nil */
336
+ rc = mdbx_del( db->txn, db->dbi, &ckey, NULL );
337
+ }
338
+ else {
339
+ MDBX_val old;
340
+ MDBX_val data = rmdbx_val_for( self, val );
341
+ rc = mdbx_replace( db->txn, db->dbi, &ckey, &data, &old, 0 );
342
+ }
343
+
344
+ mdbx_txn_commit( db->txn );
345
+
346
+ switch ( rc ) {
347
+ case MDBX_SUCCESS:
348
+ return val;
349
+ case MDBX_NOTFOUND:
350
+ return Qnil;
351
+ default:
352
+ rb_raise( rmdbx_eDatabaseError, "Unable to store value: (%d) %s", rc, mdbx_strerror(rc) );
353
+ }
354
+ }
355
+
356
+
357
+ /*
358
+ * call-seq:
359
+ * db.collection( 'collection_name' ) # => db
360
+ * db.collection( nil ) # => db (main)
361
+ *
362
+ * Operate on a sub-database "collection". Passing +nil+
363
+ * sets the database to the main, top-level namespace.
364
+ *
365
+ */
366
+ VALUE
367
+ rmdbx_set_subdb( int argc, VALUE *argv, VALUE self )
368
+ {
369
+ UNWRAP_DB( self, db );
370
+ VALUE subdb;
371
+
372
+ rb_scan_args( argc, argv, "01", &subdb );
373
+ if ( argc == 0 ) {
374
+ if ( db->subdb == NULL ) return Qnil;
375
+ return rb_str_new_cstr( db->subdb );
376
+ }
377
+
378
+ rb_iv_set( self, "@collection", subdb );
379
+ db->subdb = NIL_P( subdb ) ? NULL : StringValueCStr( subdb );
380
+
381
+ /* Close any currently open dbi handle, to be re-opened with
382
+ * the new collection on next access.
383
+ *
384
+ FIXME: Immediate transaction write to auto-create new env?
385
+ Fetching from here at the moment causes an error if you
386
+ haven't written anything yet.
387
+ */
388
+ if ( db->dbi ) {
389
+ mdbx_dbi_close( db->env, db->dbi );
390
+ db->dbi = 0;
391
+ }
392
+
393
+ return self;
394
+ }
395
+
396
+
397
+ /*
398
+ * call-seq:
399
+ * MDBX::Database.open( path ) -> db
400
+ * MDBX::Database.open( path, options ) -> db
401
+ * MDBX::Database.open( path, options ) do |db|
402
+ * db...
403
+ * end
404
+ *
405
+ * Open an existing (or create a new) mdbx database at filesystem
406
+ * +path+. In block form, the database is automatically closed.
407
+ *
408
+ */
409
+ VALUE
410
+ rmdbx_database_initialize( int argc, VALUE *argv, VALUE self )
411
+ {
412
+ int mode = 0644;
413
+ int max_collections = 0;
414
+ int env_flags = MDBX_ENV_DEFAULTS;
415
+ VALUE path, opts, opt;
416
+
417
+ rb_scan_args( argc, argv, "11", &path, &opts );
418
+
419
+ /* Ensure options is a hash if it was passed in.
420
+ */
421
+ if ( NIL_P(opts) ) {
422
+ opts = rb_hash_new();
423
+ }
424
+ else {
425
+ Check_Type( opts, T_HASH );
426
+ }
427
+ rb_hash_freeze( opts );
428
+
429
+ /* Options setup, overrides.
430
+ */
431
+ opt = rb_hash_aref( opts, ID2SYM( rb_intern("mode") ) );
432
+ if ( ! NIL_P(opt) ) mode = FIX2INT( opt );
433
+ opt = rb_hash_aref( opts, ID2SYM( rb_intern("max_collections") ) );
434
+ if ( ! NIL_P(opt) ) max_collections = FIX2INT( opt );
435
+ opt = rb_hash_aref( opts, ID2SYM( rb_intern("nosubdir") ) );
436
+ if ( RTEST(opt) ) env_flags = env_flags | MDBX_NOSUBDIR;
437
+ opt = rb_hash_aref( opts, ID2SYM( rb_intern("readonly") ) );
438
+ if ( RTEST(opt) ) env_flags = env_flags | MDBX_RDONLY;
439
+ opt = rb_hash_aref( opts, ID2SYM( rb_intern("exclusive") ) );
440
+ if ( RTEST(opt) ) env_flags = env_flags | MDBX_EXCLUSIVE;
441
+ opt = rb_hash_aref( opts, ID2SYM( rb_intern("compat") ) );
442
+ if ( RTEST(opt) ) env_flags = env_flags | MDBX_ACCEDE;
443
+ opt = rb_hash_aref( opts, ID2SYM( rb_intern("writemap") ) );
444
+ if ( RTEST(opt) ) env_flags = env_flags | MDBX_WRITEMAP;
445
+ opt = rb_hash_aref( opts, ID2SYM( rb_intern("no_threadlocal") ) );
446
+ if ( RTEST(opt) ) env_flags = env_flags | MDBX_NOTLS;
447
+ opt = rb_hash_aref( opts, ID2SYM( rb_intern("no_readahead") ) );
448
+ if ( RTEST(opt) ) env_flags = env_flags | MDBX_NORDAHEAD;
449
+ opt = rb_hash_aref( opts, ID2SYM( rb_intern("no_memory_init") ) );
450
+ if ( RTEST(opt) ) env_flags = env_flags | MDBX_NOMEMINIT;
451
+ opt = rb_hash_aref( opts, ID2SYM( rb_intern("coalesce") ) );
452
+ if ( RTEST(opt) ) env_flags = env_flags | MDBX_COALESCE;
453
+ opt = rb_hash_aref( opts, ID2SYM( rb_intern("lifo_reclaim") ) );
454
+ if ( RTEST(opt) ) env_flags = env_flags | MDBX_LIFORECLAIM;
455
+ opt = rb_hash_aref( opts, ID2SYM( rb_intern("no_metasync") ) );
456
+ if ( RTEST(opt) ) env_flags = env_flags | MDBX_NOMETASYNC;
457
+
458
+ /* Duplicate keys, on mdbx_dbi_open, maybe set here? */
459
+ /* MDBX_DUPSORT = UINT32_C(0x04), */
460
+
461
+ /* Initialize the DB vals.
462
+ */
463
+ UNWRAP_DB( self, db );
464
+ db->env = NULL;
465
+ db->dbi = 0;
466
+ db->txn = NULL;
467
+ db->cursor = NULL;
468
+ db->env_flags = env_flags;
469
+ db->mode = mode;
470
+ db->max_collections = max_collections;
471
+ db->path = StringValueCStr( path );
472
+ db->open = 0;
473
+ db->subdb = NULL;
474
+
475
+ /* Set instance variables.
476
+ */
477
+ rb_iv_set( self, "@path", path );
478
+ rb_iv_set( self, "@options", opts );
479
+
480
+ rmdbx_open_env( self );
481
+ return self;
482
+ }
483
+
484
+
485
+ /*
486
+ * Initialization for the MDBX::Database class.
487
+ */
488
+ void
489
+ rmdbx_init_database()
490
+ {
491
+ rmdbx_cDatabase = rb_define_class_under( rmdbx_mMDBX, "Database", rb_cData );
492
+
493
+ #ifdef FOR_RDOC
494
+ rmdbx_mMDBX = rb_define_module( "MDBX" );
495
+ #endif
496
+
497
+ rb_define_alloc_func( rmdbx_cDatabase, rmdbx_alloc );
498
+
499
+ rb_define_protected_method( rmdbx_cDatabase, "initialize", rmdbx_database_initialize, -1 );
500
+ rb_define_method( rmdbx_cDatabase, "collection", rmdbx_set_subdb, -1 );
501
+ rb_define_method( rmdbx_cDatabase, "close", rmdbx_close, 0 );
502
+ rb_define_method( rmdbx_cDatabase, "reopen", rmdbx_open_env, 0 );
503
+ rb_define_method( rmdbx_cDatabase, "closed?", rmdbx_closed_p, 0 );
504
+ rb_define_method( rmdbx_cDatabase, "clear", rmdbx_clear, 0 );
505
+ rb_define_method( rmdbx_cDatabase, "keys", rmdbx_keys, 0 );
506
+ rb_define_method( rmdbx_cDatabase, "[]", rmdbx_get_val, 1 );
507
+ rb_define_method( rmdbx_cDatabase, "[]=", rmdbx_put_val, 2 );
508
+
509
+ rb_require( "mdbx/database" );
510
+ }
511
+
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: set noet sta sw=4 ts=4 :
3
+
4
+ require 'mkmf'
5
+
6
+ have_library( 'mdbx' ) or abort "No mdbx library!"
7
+ have_header( 'mdbx.h' ) or abort "No mdbx.h header!"
8
+
9
+ create_header()
10
+ create_makefile( 'mdbx_ext' )
11
+
@@ -0,0 +1,26 @@
1
+ /* vim: set noet sta sw=4 ts=4 : */
2
+
3
+ #include "mdbx_ext.h"
4
+
5
+ VALUE rmdbx_mMDBX;
6
+ VALUE rmdbx_eDatabaseError;
7
+ VALUE rmdbx_eRollback;
8
+
9
+ /*
10
+ * MDBX initialization
11
+ */
12
+ void
13
+ Init_mdbx_ext()
14
+ {
15
+ rmdbx_mMDBX = rb_define_module( "MDBX" );
16
+
17
+ /* The backend library version. */
18
+ VALUE version = rb_str_new_cstr( mdbx_version.git.describe );
19
+ rb_define_const( rmdbx_mMDBX, "LIBRARY_VERSION", version );
20
+
21
+ rmdbx_eDatabaseError = rb_define_class_under( rmdbx_mMDBX, "DatabaseError", rb_eRuntimeError );
22
+ rmdbx_eRollback = rb_define_class_under( rmdbx_mMDBX, "Rollback", rb_eRuntimeError );
23
+
24
+ rmdbx_init_database();
25
+ }
26
+
@@ -0,0 +1,29 @@
1
+
2
+ #include <ruby.h>
3
+ #include "extconf.h"
4
+
5
+ #include "mdbx.h"
6
+
7
+ #ifndef MDBX_EXT_0_9_2
8
+ #define MDBX_EXT_0_9_2
9
+
10
+
11
+ /* ------------------------------------------------------------
12
+ * Globals
13
+ * ------------------------------------------------------------ */
14
+
15
+ extern VALUE rmdbx_mMDBX;
16
+ extern VALUE rmdbx_cDatabase;
17
+ extern VALUE rmdbx_eDatabaseError;
18
+ extern VALUE rmdbx_eRollback;
19
+
20
+
21
+ /* ------------------------------------------------------------
22
+ * Functions
23
+ * ------------------------------------------------------------ */
24
+ extern void Init_rmdbx ( void );
25
+ extern void rmdbx_init_database ( void );
26
+
27
+
28
+ #endif /* define MDBX_EXT_0_9_2 */
29
+
@@ -0,0 +1,20 @@
1
+ # -*- ruby -*-
2
+ # vim: set nosta noet ts=4 sw=4 ft=ruby:
3
+ # encoding: utf-8
4
+
5
+ require 'mdbx_ext'
6
+
7
+
8
+ # Top level namespace for MDBX.
9
+ #
10
+ module MDBX
11
+
12
+ # The version of this gem.
13
+ #
14
+ # Note: the MDBX library version this gem was built
15
+ # against can be found in the 'LIBRARY_VERSION' constant.
16
+ #
17
+ VERSION = '0.0.1'
18
+
19
+ end # module MDBX
20
+
@@ -0,0 +1,68 @@
1
+ # -*- ruby -*-
2
+ # vim: set nosta noet ts=4 sw=4 ft=ruby:
3
+ # encoding: utf-8
4
+
5
+ require 'mdbx' unless defined?( MDBX )
6
+
7
+
8
+ # TODO: rdoc
9
+ #
10
+ class MDBX::Database
11
+
12
+ ### Open an existing (or create a new) mdbx database at filesystem
13
+ ### +path+. In block form, the database is automatically closed.
14
+ ###
15
+ ### MDBX::Database.open( path ) -> db
16
+ ### MDBX::Database.open( path, options ) -> db
17
+ ### MDBX::Database.open( path, options ) do |db|
18
+ ### db[ 'key' ] #=> value
19
+ ### end
20
+ ###
21
+ ### FIXME: options!
22
+ ###
23
+ def self::open( *args, &block )
24
+ db = new( *args )
25
+
26
+ db.serializer = ->( v ) { Marshal.dump( v ) }
27
+ db.deserializer = ->( v ) { Marshal.load( v ) }
28
+
29
+ if block_given?
30
+ begin
31
+ yield db
32
+ ensure
33
+ db.close
34
+ end
35
+ end
36
+
37
+ return db
38
+ end
39
+
40
+
41
+ # Only instantiate Database objects via #open.
42
+ private_class_method :new
43
+
44
+
45
+ # The options used to instantiate this database.
46
+ attr_reader :options
47
+
48
+ # The path on disk of the database.
49
+ attr_reader :path
50
+
51
+ # A Proc for automatically serializing values.
52
+ attr_accessor :serializer
53
+
54
+ # A Proc for automatically deserializing values.
55
+ attr_accessor :deserializer
56
+
57
+
58
+ ### Switch to the top-level collection.
59
+ ###
60
+ def main
61
+ return self.collection( nil )
62
+ end
63
+
64
+ # Allow for some common nomenclature.
65
+ alias_method :namespace, :collection
66
+
67
+ end # class MDBX::Database
68
+
Binary file
metadata ADDED
@@ -0,0 +1,208 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mdbx
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.pre.20201217111933
5
+ platform: ruby
6
+ authors:
7
+ - Mahlon E. Smith
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIENDCCApygAwIBAgIBATANBgkqhkiG9w0BAQsFADAiMSAwHgYDVQQDDBdtYWhs
14
+ b24vREM9bWFydGluaS9EQz1udTAeFw0yMDEyMjIwMDQwNDBaFw0zMDEyMjAwMDQw
15
+ NDBaMCIxIDAeBgNVBAMMF21haGxvbi9EQz1tYXJ0aW5pL0RDPW51MIIBojANBgkq
16
+ hkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA2aRhRIb+Cb3OX58q1VHxmeeFM0R0Tyvh
17
+ rSRequJbgH05Lph3yPqm9Gdvcvqb+5HE4LdTTdRiQuDGE9y1lxaw7CpNxFHgEqDD
18
+ iBFDClzExQIosju9hzJv7VVEgBobd4da3I37tQH4bJEweOrpoiPESkezGyyeFxVU
19
+ Sk4ncvMdzk3/XVhiILln5d+s2kB9H6gKE7UTToUay/Faeoo8Ob6TvYfDmVmRR4Mg
20
+ AJ/SZs1+XY9FrVesyiU81qVo0k7SfA4nsXAOGeJZ7Fku9lG2EoGAepVRwthMu6nb
21
+ L+vgc2Yc8FE3gLdxgnnqXyuWy7ivK3RmqXkGq0JR3O3+lXedan9eHVEQjIJTio+i
22
+ QTo9dwtfMKE8EjzT8uN025xe/PJ8XZOtc3AkO5rGVkRcseuZRL8C6v7Ypr4jA454
23
+ ZGvaNmaKzLlKl6b7caPaeb+AAeVIyBm3CLVdlUdqibttUrm/N+LmeJA+enp8VYTA
24
+ g8yHOp2C/uyUHLAwmBt41tZqO6hFmMilAgMBAAGjdTBzMAkGA1UdEwQCMAAwCwYD
25
+ VR0PBAQDAgSwMB0GA1UdDgQWBBRLTYPDE5cpv/oQ3PVlpuCuDXs5uDAcBgNVHREE
26
+ FTATgRFtYWhsb25AbWFydGluaS5udTAcBgNVHRIEFTATgRFtYWhsb25AbWFydGlu
27
+ aS5udTANBgkqhkiG9w0BAQsFAAOCAYEAAKlzY37tbOIEGy/gAu5Z6Y4+u6ph2p0C
28
+ SeveRaItIYHHk870qpFZEpiYah6iupPGMmOXV3uAYNywQb6MGg/3ZguTIF8fQdJh
29
+ zb3ltiUNsuCCURNsaJ5XpmifO5xoyM3jqb+LuDUnCuDL1qfM/JNXrBguFOfE1Pgb
30
+ Hv4Asf3a9o7P9V7lBSr1VAF9UTIEqcsV9ZouxTdP9tHtzUpUMaGXQP1myVUJSE35
31
+ cwSRJbCf8E0NX0iH31nhboVZODZgwgYiyjv4sbjDgI/1V8q7EaI9IdDWsoi8fm4o
32
+ KdDjQCJ3z7nUXe0ojb0v9xikanWH2S70UX46A/awTwLCh1nXbRc1SwSzfPWVUZpv
33
+ f46eTlAa9H9cl8GC1tHzl3NUxQ5/bZcSw8FzJXjGivhsyYJ+rL53Skefy5Jdn7Vs
34
+ AWgz1mmbCTaQJS+NuCrrtq0NbfgluSB/fGvf9ADVPi9ImWpPY3BQE0s6TmJgBI74
35
+ /KwqC+7jPv0TJhph1OPgnOvSM/sflGsi
36
+ -----END CERTIFICATE-----
37
+ date: 2020-12-17 00:00:00.000000000 Z
38
+ dependencies:
39
+ - !ruby/object:Gem::Dependency
40
+ name: pry
41
+ requirement: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - "~>"
44
+ - !ruby/object:Gem::Version
45
+ version: '0.13'
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - "~>"
51
+ - !ruby/object:Gem::Version
52
+ version: '0.13'
53
+ - !ruby/object:Gem::Dependency
54
+ name: rake
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '13.0'
60
+ type: :development
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '13.0'
67
+ - !ruby/object:Gem::Dependency
68
+ name: rake-compiler
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '1.1'
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '1.1'
81
+ - !ruby/object:Gem::Dependency
82
+ name: rake-deveiate
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: '0.15'
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: 0.15.1
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '0.15'
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: 0.15.1
101
+ - !ruby/object:Gem::Dependency
102
+ name: rdoc-generator-fivefish
103
+ requirement: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - "~>"
106
+ - !ruby/object:Gem::Version
107
+ version: '0.4'
108
+ type: :development
109
+ prerelease: false
110
+ version_requirements: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - "~>"
113
+ - !ruby/object:Gem::Version
114
+ version: '0.4'
115
+ - !ruby/object:Gem::Dependency
116
+ name: rspec
117
+ requirement: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - "~>"
120
+ - !ruby/object:Gem::Version
121
+ version: '3.9'
122
+ type: :development
123
+ prerelease: false
124
+ version_requirements: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - "~>"
127
+ - !ruby/object:Gem::Version
128
+ version: '3.9'
129
+ - !ruby/object:Gem::Dependency
130
+ name: rubocop
131
+ requirement: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - "~>"
134
+ - !ruby/object:Gem::Version
135
+ version: '0.93'
136
+ type: :development
137
+ prerelease: false
138
+ version_requirements: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - "~>"
141
+ - !ruby/object:Gem::Version
142
+ version: '0.93'
143
+ - !ruby/object:Gem::Dependency
144
+ name: simplecov
145
+ requirement: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - "~>"
148
+ - !ruby/object:Gem::Version
149
+ version: '0.12'
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - "~>"
155
+ - !ruby/object:Gem::Version
156
+ version: '0.12'
157
+ description: |
158
+ This is a native ruby binding to libmdbx, an improved version
159
+ of the Lightning Memory Mapped Database.
160
+
161
+ libmdbx is an extremely fast, compact, powerful, embedded,
162
+ transactional key-value database, with permissive license.
163
+ libmdbx has a specific set of properties and capabilities,
164
+ focused on creating unique lightweight solutions.
165
+ email:
166
+ - mahlon@martini.nu
167
+ executables: []
168
+ extensions:
169
+ - ext/mdbx_ext/extconf.rb
170
+ extra_rdoc_files: []
171
+ files:
172
+ - History.md
173
+ - README.md
174
+ - ext/mdbx_ext/database.c
175
+ - ext/mdbx_ext/extconf.rb
176
+ - ext/mdbx_ext/mdbx_ext.c
177
+ - ext/mdbx_ext/mdbx_ext.h
178
+ - lib/mdbx.rb
179
+ - lib/mdbx/database.rb
180
+ - lib/mdbx_ext.so
181
+ homepage: https://code.martini.nu/ruby-mdbx
182
+ licenses:
183
+ - BSD-3-Clause
184
+ metadata:
185
+ changelog_uri: https://martini.nu/docs/ruby-mdbx/History_md.html
186
+ documentation_uri: https://martini.nu/docs/ruby-mdbx
187
+ homepage_uri: https://code.martini.nu/ruby-mdbx
188
+ post_install_message:
189
+ rdoc_options: []
190
+ require_paths:
191
+ - lib
192
+ required_ruby_version: !ruby/object:Gem::Requirement
193
+ requirements:
194
+ - - ">="
195
+ - !ruby/object:Gem::Version
196
+ version: '0'
197
+ required_rubygems_version: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">"
200
+ - !ruby/object:Gem::Version
201
+ version: 1.3.1
202
+ requirements: []
203
+ rubygems_version: 3.1.4
204
+ signing_key:
205
+ specification_version: 4
206
+ summary: A ruby binding to libmdbx, an improved version of the Lightning Memory Mapped
207
+ Database.
208
+ test_files: []
@@ -0,0 +1,2 @@
1
+ ^/Ʌ>�e��j��LKlē�%�2$��[Y�~��!6d��O�t�0K*�&�B熣��N Z�k����>��sa��pdq�']Z��-��E��K�����P�ǚ3����<����>����{�:��PT���\�-E�n>�[V4��{^���yh?T�+����hRu��N�\�@���儈aV�Sܽ�X��y?�/�QFR�ɣ"���؛�`/���x��j��L��mpdS�wпDq�T������c"��޸�$���kD�W���;(�<:��.���Y +�E ���F�Óɣ _�px�4.+�% z�*���9
2
+ E��Uh�&Y�? ]zȷ�Z<�FKa��֬��*����72�dn��`%