isomorfeus-ferret 0.14.4 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +56 -6
  3. data/ext/isomorfeus_ferret_ext/extconf.rb +19 -1
  4. data/ext/isomorfeus_ferret_ext/frb_analysis.c +0 -4
  5. data/ext/isomorfeus_ferret_ext/frb_index.c +170 -52
  6. data/ext/isomorfeus_ferret_ext/frb_lazy_doc.c +4 -0
  7. data/ext/isomorfeus_ferret_ext/frb_qparser.c +1 -6
  8. data/ext/isomorfeus_ferret_ext/frb_search.c +1 -6
  9. data/ext/isomorfeus_ferret_ext/frb_store.c +246 -61
  10. data/ext/isomorfeus_ferret_ext/frb_utils.c +0 -4
  11. data/ext/isomorfeus_ferret_ext/frt_compound_io.c +5 -5
  12. data/ext/isomorfeus_ferret_ext/frt_except.c +11 -11
  13. data/ext/isomorfeus_ferret_ext/frt_hash.c +32 -70
  14. data/ext/isomorfeus_ferret_ext/frt_ind.c +20 -20
  15. data/ext/isomorfeus_ferret_ext/frt_ind.h +1 -1
  16. data/ext/isomorfeus_ferret_ext/frt_index.c +84 -90
  17. data/ext/isomorfeus_ferret_ext/frt_index.h +11 -11
  18. data/ext/isomorfeus_ferret_ext/frt_mdbx_store.c +749 -0
  19. data/ext/isomorfeus_ferret_ext/frt_q_parser.c +4 -4
  20. data/ext/isomorfeus_ferret_ext/frt_ram_store.c +9 -9
  21. data/ext/isomorfeus_ferret_ext/frt_search.c +0 -3
  22. data/ext/isomorfeus_ferret_ext/frt_search.h +1 -1
  23. data/ext/isomorfeus_ferret_ext/frt_sort.c +2 -2
  24. data/ext/isomorfeus_ferret_ext/frt_store.c +6 -8
  25. data/ext/isomorfeus_ferret_ext/frt_store.h +37 -24
  26. data/ext/isomorfeus_ferret_ext/frt_threading.h +0 -16
  27. data/ext/isomorfeus_ferret_ext/isomorfeus_ferret.c +2 -11
  28. data/ext/isomorfeus_ferret_ext/isomorfeus_ferret.h +1 -1
  29. data/ext/isomorfeus_ferret_ext/mdbx.c +33675 -0
  30. data/ext/isomorfeus_ferret_ext/mdbx.h +5495 -0
  31. data/ext/isomorfeus_ferret_ext/test.c +9 -9
  32. data/ext/isomorfeus_ferret_ext/test_index.c +3 -3
  33. data/ext/isomorfeus_ferret_ext/{test_fs_store.c → test_mdbx_store.c} +4 -10
  34. data/ext/isomorfeus_ferret_ext/test_ram_store.c +1 -1
  35. data/ext/isomorfeus_ferret_ext/test_segments.c +1 -1
  36. data/ext/isomorfeus_ferret_ext/test_sort.c +2 -2
  37. data/ext/isomorfeus_ferret_ext/test_store.c +4 -8
  38. data/ext/isomorfeus_ferret_ext/test_threading.c +4 -4
  39. data/ext/isomorfeus_ferret_ext/tests_all.h +2 -3
  40. data/lib/isomorfeus/ferret/index/index.rb +8 -9
  41. data/lib/isomorfeus/ferret/version.rb +1 -1
  42. metadata +9 -7
  43. data/ext/isomorfeus_ferret_ext/frt_fs_store.c +0 -505
@@ -0,0 +1,749 @@
1
+ #include "frt_store.h"
2
+ #include "frt_lang.h"
3
+ #include <time.h>
4
+ #include <sys/types.h>
5
+ #include <fcntl.h>
6
+ #include <sys/stat.h>
7
+ #include <errno.h>
8
+ #include <string.h>
9
+ #include <stdio.h>
10
+
11
+ extern void frt_micro_sleep(const int micro_seconds);
12
+
13
+ const char *dbi_name = "segmented_index";
14
+ const char *lock_s = "lock";
15
+
16
+ // helper functions
17
+
18
+ // txn
19
+
20
+ static bool mdbxs_begin_txn(FrtStore *store, MDBX_txn **txn) {
21
+ MDBX_txn *env_txn = mdbx_env_get_userctx(store->dir.mdbx->env);
22
+ if (env_txn) {
23
+ *txn = env_txn;
24
+ return false;
25
+ }
26
+ int res = mdbx_txn_begin(store->dir.mdbx->env, NULL, 0, txn);
27
+ if (res != MDBX_SUCCESS) {
28
+ FRT_RAISE(FRT_IO_ERROR, "failed to begin txn, error: %i\n", res);
29
+ }
30
+ res = mdbx_env_set_userctx(store->dir.mdbx->env, *txn);
31
+ if (res != MDBX_SUCCESS) {
32
+ mdbx_txn_abort(*txn);
33
+ FRT_RAISE(FRT_IO_ERROR, "failed to begin txn, userctx, error: %i\n", res);
34
+ }
35
+ return true;
36
+ }
37
+
38
+ static bool mdbxs_begin_ro_txn(FrtStore *store, MDBX_txn **txn) {
39
+ MDBX_txn *env_txn = mdbx_env_get_userctx(store->dir.mdbx->env);
40
+ if (env_txn) {
41
+ *txn = env_txn;
42
+ return false;
43
+ }
44
+ int res = mdbx_txn_begin(store->dir.mdbx->env, NULL, 0 | MDBX_RDONLY, txn);
45
+ if (res != MDBX_SUCCESS) {
46
+ FRT_RAISE(FRT_IO_ERROR, "failed to begin read only txn, error: %i\n", res);
47
+ }
48
+ res = mdbx_env_set_userctx(store->dir.mdbx->env, *txn);
49
+ if (res != MDBX_SUCCESS) {
50
+ mdbx_txn_abort(*txn);
51
+ FRT_RAISE(FRT_IO_ERROR, "failed to begin read only txn, userctx, error: %i\n", res);
52
+ }
53
+ return true;
54
+ }
55
+
56
+ static void mdbxs_commit_txn(FrtStore *store, MDBX_txn *txn, bool del_env_txn) {
57
+ if (del_env_txn) {
58
+ mdbx_env_set_userctx(store->dir.mdbx->env, NULL);
59
+ int res = mdbx_txn_commit(txn);
60
+ if (res != MDBX_SUCCESS) {
61
+ FRT_RAISE(FRT_IO_ERROR, "failed to commit txn, error: %i\n", res);
62
+ }
63
+ }
64
+ }
65
+
66
+ static void mdbxs_abort_txn(FrtStore *store, MDBX_txn *txn, bool del_env_txn) {
67
+ if (del_env_txn) {
68
+ mdbx_env_set_userctx(store->dir.mdbx->env, NULL);
69
+ int res = mdbx_txn_abort(txn);
70
+ if (res != MDBX_SUCCESS) {
71
+ FRT_RAISE(FRT_IO_ERROR, "failed to abort txn, error: %i\n", res);
72
+ }
73
+ }
74
+ }
75
+
76
+ // dbi
77
+
78
+ static void mdbxs_open_dbi(MDBX_txn *txn, const char *dbi_name, MDBX_dbi *dbi) {
79
+ int res = mdbx_dbi_open(txn, dbi_name, MDBX_DB_DEFAULTS | MDBX_CREATE, dbi);
80
+ if (res != MDBX_SUCCESS) {
81
+ FRT_RAISE(FRT_IO_ERROR, "failed to open dbi '%s', error: %i\n", dbi_name, res);
82
+ }
83
+ }
84
+
85
+ // cursor
86
+
87
+ static void mdbxs_open_cursor(MDBX_txn *txn, MDBX_dbi dbi, MDBX_cursor **cursor) {
88
+ int res = mdbx_cursor_open(txn, dbi, cursor);
89
+ if (res != MDBX_SUCCESS) {
90
+ FRT_RAISE(FRT_IO_ERROR, "failed to open cursor, error: %i\n", res);
91
+ }
92
+ }
93
+
94
+ // crud
95
+
96
+ static void mdbxs_del(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *val) {
97
+ int res = mdbx_del(txn, dbi, key, val);
98
+ if (res == MDBX_SUCCESS || res == MDBX_NOTFOUND) { return; }
99
+ FRT_RAISE(FRT_IO_ERROR, "failed to del, error: %i\n", res);
100
+ }
101
+
102
+ static void mdbxs_put(MDBX_txn *txn, MDBX_dbi dbi, MDBX_val *key, MDBX_val *val) {
103
+ int res = mdbx_put(txn, dbi, key, val, 0);
104
+ if (res != MDBX_SUCCESS) {
105
+ FRT_RAISE(FRT_IO_ERROR, "failed to put, error: %i\n", res);
106
+ }
107
+ }
108
+
109
+ // store API
110
+
111
+ static void mdbxs_touch(FrtStore *store, const char *filename) {
112
+ int res = 0;
113
+ MDBX_txn *txn = NULL;
114
+ MDBX_dbi dbi;
115
+
116
+ MDBX_val key, val;
117
+ key.iov_base = (char *)filename;
118
+ key.iov_len = strlen(filename);
119
+ val.iov_base = NULL;
120
+ val.iov_len = 0;
121
+
122
+ bool del_env_txn = mdbxs_begin_txn(store, &txn);
123
+ mdbxs_open_dbi(txn, dbi_name, &dbi);
124
+ res = mdbx_get(txn, dbi, &key, &val);
125
+
126
+ if (res == MDBX_NOTFOUND) {
127
+ val.iov_base = NULL;
128
+ val.iov_len = 0;
129
+ mdbxs_put(txn, dbi, &key, &val);
130
+ mdbxs_commit_txn(store, txn, del_env_txn);
131
+ } else {
132
+ mdbxs_abort_txn(store, txn, del_env_txn);
133
+ if (res != MDBX_SUCCESS) {
134
+ FRT_RAISE(FRT_IO_ERROR, "failed to touch '%s', error: %i\n", filename, res);
135
+ }
136
+ }
137
+ }
138
+
139
+ static int mdbxs_exists(FrtStore *store, const char *filename) {
140
+ MDBX_txn *txn = NULL;
141
+ MDBX_dbi dbi;
142
+
143
+ MDBX_val key, val;
144
+ key.iov_base = (char *)filename;
145
+ key.iov_len = strlen(filename);
146
+ val.iov_base = NULL;
147
+ val.iov_len = 0;
148
+
149
+ bool del_env_txn = mdbxs_begin_ro_txn(store, &txn);
150
+ mdbxs_open_dbi(txn, dbi_name, &dbi);
151
+ int res = mdbx_get(txn, dbi, &key, &val);
152
+ mdbxs_abort_txn(store, txn, del_env_txn);
153
+
154
+ if (res == MDBX_SUCCESS) { return true; }
155
+ return false;
156
+ }
157
+
158
+ static int mdbxs_remove(FrtStore *store, const char *filename) {
159
+ MDBX_txn *txn = NULL;
160
+ MDBX_dbi dbi;
161
+
162
+ MDBX_val key;
163
+ key.iov_base = (char *)filename;
164
+ key.iov_len = strlen(filename);
165
+
166
+ bool del_env_txn = mdbxs_begin_txn(store, &txn);
167
+ mdbxs_open_dbi(txn, dbi_name, &dbi);
168
+ mdbxs_del(txn, dbi, &key, NULL);
169
+ mdbxs_commit_txn(store, txn, del_env_txn);
170
+
171
+ return 0;
172
+ }
173
+
174
+ static void mdbxs_rename(FrtStore *store, const char *from, const char *to) {
175
+ int res;
176
+ MDBX_txn *txn = NULL;
177
+ MDBX_dbi dbi;
178
+
179
+ MDBX_val from_key, to_key, val;
180
+ from_key.iov_base = (char *)from;
181
+ from_key.iov_len = strlen(from);
182
+ val.iov_base = NULL;
183
+ val.iov_len = 0;
184
+
185
+ bool del_env_txn = mdbxs_begin_txn(store, &txn);
186
+ mdbxs_open_dbi(txn, dbi_name, &dbi);
187
+ res = mdbx_get(txn, dbi, &from_key, &val);
188
+
189
+ if (res == MDBX_SUCCESS) {
190
+ to_key.iov_base = (char *)to;
191
+ to_key.iov_len = strlen(to);
192
+
193
+ mdbxs_put(txn, dbi, &to_key, &val);
194
+ mdbxs_del(txn, dbi, &from_key, NULL);
195
+ mdbxs_commit_txn(store, txn, del_env_txn);
196
+ } else {
197
+ mdbxs_abort_txn(store, txn, del_env_txn);
198
+ FRT_RAISE(FRT_FILE_NOT_FOUND_ERROR, "tried to open '%s' but its not accessible, error: %i", from, res);
199
+ }
200
+ }
201
+
202
+ static int mdbxs_count(FrtStore *store) {
203
+ MDBX_txn *txn = NULL;
204
+ MDBX_dbi dbi;
205
+ MDBX_stat stat;
206
+ int res = 0;
207
+ int cnt = 0;
208
+
209
+ bool del_env_txn = mdbxs_begin_txn(store, &txn);
210
+ mdbxs_open_dbi(txn, dbi_name, &dbi);
211
+ res = mdbx_dbi_stat(txn, dbi, &stat, sizeof(MDBX_stat));
212
+ cnt = stat.ms_entries;
213
+ mdbxs_abort_txn(store, txn, del_env_txn);
214
+
215
+ if (res != MDBX_SUCCESS) {
216
+ FRT_RAISE(FRT_FILE_NOT_FOUND_ERROR, "failed to stat dbi, error: %i", res);
217
+ }
218
+ return cnt;
219
+ }
220
+
221
+ static void mdbxs_each(FrtStore *store, void (*func)(const char *fname, void *arg), void *arg) {
222
+ MDBX_txn *txn = NULL;
223
+ MDBX_dbi dbi;
224
+ MDBX_cursor *cursor = NULL;
225
+
226
+ MDBX_val key, val;
227
+ key.iov_base = NULL;
228
+ key.iov_len = 0;
229
+ val.iov_base = NULL;
230
+ val.iov_len = 0;
231
+
232
+ bool del_env_txn = mdbxs_begin_txn(store, &txn);
233
+ mdbxs_open_dbi(txn, dbi_name, &dbi);
234
+ mdbxs_open_cursor(txn, dbi, &cursor);
235
+
236
+ int op = MDBX_FIRST;
237
+ int buffer_size = FRT_MAX_WORD_SIZE;
238
+ char *name = FRT_ALLOC_N(char, buffer_size);
239
+ while(mdbx_cursor_get(cursor, &key, &val, op) == MDBX_SUCCESS) {
240
+ op = MDBX_NEXT;
241
+ if (key.iov_len >= buffer_size) {
242
+ buffer_size = key.iov_len + 1;
243
+ FRT_REALLOC_N(name, char, buffer_size);
244
+ }
245
+ memcpy(name, key.iov_base, key.iov_len);
246
+ name[key.iov_len] = '\0';
247
+ func(name, arg);
248
+ }
249
+ free(name);
250
+
251
+ mdbx_cursor_close(cursor);
252
+ mdbxs_commit_txn(store, txn, del_env_txn);
253
+ }
254
+
255
+ static void mdbxs_clear_locks(FrtStore *store) {
256
+ MDBX_txn *txn = NULL;
257
+ MDBX_dbi dbi;
258
+ MDBX_cursor *cursor = NULL;
259
+
260
+ MDBX_val key, val;
261
+ key.iov_base = NULL;
262
+ key.iov_len = 0;
263
+ val.iov_base = NULL;
264
+ val.iov_len = 0;
265
+
266
+ bool del_env_txn = mdbxs_begin_txn(store, &txn);
267
+ mdbxs_open_dbi(txn, dbi_name, &dbi);
268
+ mdbxs_open_cursor(txn, dbi, &cursor);
269
+
270
+ int op = MDBX_FIRST;
271
+ int buffer_size = FRT_MAX_WORD_SIZE;
272
+ char *name = FRT_ALLOC_N(char, buffer_size);
273
+ while(mdbx_cursor_get(cursor, &key, &val, op) == MDBX_SUCCESS) {
274
+ op = MDBX_NEXT;
275
+ if (key.iov_len >= buffer_size) {
276
+ buffer_size = key.iov_len + 1;
277
+ FRT_REALLOC_N(name, char, buffer_size);
278
+ }
279
+ memcpy(name, key.iov_base, key.iov_len);
280
+ name[key.iov_len] = '\0';
281
+ if (key.iov_len > 11) { // "ferret-" + ".lck"
282
+ if ((strncmp(FRT_LOCK_PREFIX, name, strlen(FRT_LOCK_PREFIX)) == 0) &&
283
+ (strncmp(".lck", name + (key.iov_len - 4), 4) == 0)) {
284
+ mdbx_cursor_del(cursor, MDBX_ALLDUPS);
285
+ }
286
+ }
287
+ }
288
+ free(name);
289
+
290
+ mdbx_cursor_close(cursor);
291
+ mdbxs_commit_txn(store, txn, del_env_txn);
292
+ }
293
+
294
+ static bool mdbxs_is_locked(FrtStore *store, MDBX_val *key, MDBX_txn *txn, MDBX_dbi dbi) {
295
+ MDBX_val val;
296
+ val.iov_base = NULL;
297
+ val.iov_len = 0;
298
+
299
+ if (mdbx_get(txn, dbi, key, &val) == MDBX_SUCCESS) {
300
+ return true;
301
+ }
302
+
303
+ return false;
304
+ }
305
+
306
+ static void mdbxs_clear(FrtStore *store) {
307
+ MDBX_txn *txn = NULL;
308
+ MDBX_dbi dbi;
309
+ MDBX_cursor *cursor = NULL;
310
+
311
+ MDBX_val key, val;
312
+ key.iov_base = NULL;
313
+ key.iov_len = 0;
314
+ val.iov_base = NULL;
315
+ val.iov_len = 0;
316
+
317
+ bool del_env_txn = mdbxs_begin_txn(store, &txn);
318
+ mdbxs_open_dbi(txn, dbi_name, &dbi);
319
+ mdbxs_open_cursor(txn, dbi, &cursor);
320
+
321
+ int op = MDBX_FIRST;
322
+ while(mdbx_cursor_get(cursor, &key, &val, op) == MDBX_SUCCESS) {
323
+ op = MDBX_NEXT;
324
+ if (!mdbxs_is_locked(store, &key, txn, dbi)) {
325
+ mdbx_cursor_del(cursor, MDBX_ALLDUPS);
326
+ }
327
+ }
328
+ mdbx_cursor_close(cursor);
329
+ mdbxs_commit_txn(store, txn, del_env_txn);
330
+ }
331
+
332
+ /**
333
+ * Clear all files which belong to the index. Use mdbxs_clear to clear the
334
+ * directory regardless of the files origin.
335
+ */
336
+ static void mdbxs_clear_all(FrtStore *store) {
337
+ MDBX_txn *txn = NULL;
338
+ MDBX_dbi dbi;
339
+ MDBX_cursor *cursor = NULL;
340
+
341
+ MDBX_val key, val;
342
+ key.iov_base = NULL;
343
+ key.iov_len = 0;
344
+ val.iov_base = NULL;
345
+ val.iov_len = 0;
346
+
347
+ bool del_env_txn = mdbxs_begin_txn(store, &txn);
348
+ mdbxs_open_dbi(txn, dbi_name, &dbi);
349
+ mdbxs_open_cursor(txn, dbi, &cursor);
350
+
351
+ int op = MDBX_FIRST;
352
+ while(mdbx_cursor_get(cursor, &key, &val, op) == MDBX_SUCCESS) {
353
+ op = MDBX_NEXT;
354
+ mdbx_cursor_del(cursor, MDBX_ALLDUPS);
355
+ }
356
+
357
+ mdbx_cursor_close(cursor);
358
+ mdbxs_commit_txn(store, txn, del_env_txn);
359
+
360
+ mdbxs_clear_locks(store);
361
+ }
362
+
363
+ /**
364
+ * Destroy the store.
365
+ *
366
+ * @param p the store to destroy
367
+ * @rb_raise FRT_IO_ERROR if there is an error deleting the locks
368
+ */
369
+ static void mdbxs_destroy(FrtStore *store) {
370
+ FRT_TRY
371
+ if (store->dir.mdbx) {
372
+ mdbxs_clear_locks(store);
373
+ int res = mdbx_env_close(store->dir.mdbx->env);
374
+ if (res != MDBX_SUCCESS) {
375
+ fprintf(stderr, "closing env '%s' failed, error: %i\n", store->dir.mdbx->path, res);
376
+ }
377
+ free(store->dir.mdbx->path);
378
+ free(store->dir.mdbx);
379
+ store->dir.mdbx = NULL;
380
+ }
381
+ FRT_XCATCHALL
382
+ FRT_HANDLED();
383
+ FRT_XENDTRY
384
+ }
385
+
386
+ static frt_off_t mdbxs_length(FrtStore *store, const char *filename) {
387
+ frt_off_t len = 0;
388
+ MDBX_txn *txn = NULL;
389
+ MDBX_dbi dbi;
390
+
391
+ MDBX_val key, val;
392
+ key.iov_base = (char *)filename;
393
+ key.iov_len = strlen(filename);
394
+ val.iov_base = NULL;
395
+ val.iov_len = 0;
396
+
397
+ bool del_env_txn = mdbxs_begin_ro_txn(store, &txn);
398
+ mdbxs_open_dbi(txn, dbi_name, &dbi);
399
+ int res = mdbx_get(txn, dbi, &key, &val);
400
+ if (res != MDBX_SUCCESS) {
401
+ mdbxs_abort_txn(store, txn, del_env_txn);
402
+ FRT_RAISE(FRT_FILE_NOT_FOUND_ERROR, "tried to open '%s' but it doesn't exist, error: %i", filename, res);
403
+ }
404
+ len = val.iov_len;
405
+ mdbxs_abort_txn(store, txn, del_env_txn);
406
+ return len;
407
+ }
408
+
409
+ static void mdbxso_close_i(FrtOutStream *os) {
410
+ MDBX_txn *txn = NULL;
411
+ MDBX_dbi dbi;
412
+
413
+ bool del_env_txn = mdbxs_begin_txn(os->store, &txn);
414
+ mdbxs_open_dbi(txn, dbi_name, &dbi);
415
+
416
+ FrtRAMFile *rf = os->file.rf;
417
+
418
+ MDBX_val key, val;
419
+ key.iov_base = rf->name;
420
+ key.iov_len = strlen(rf->name);
421
+
422
+ val.iov_base = frt_emalloc(FRT_BUFFER_SIZE * rf->bufcnt);
423
+ val.iov_len = rf->len;
424
+
425
+ int offset = 0;
426
+ int buffer_number, buffer_offset, bytes_in_buffer, bytes_to_copy;
427
+ int remainder = rf->len;
428
+ frt_off_t start = 0;
429
+ frt_uchar *buffer;
430
+
431
+ while (remainder > 0) {
432
+ buffer_number = (int) (start / FRT_BUFFER_SIZE);
433
+ buffer_offset = start % FRT_BUFFER_SIZE;
434
+ bytes_in_buffer = FRT_BUFFER_SIZE - buffer_offset;
435
+
436
+ if (bytes_in_buffer >= remainder) {
437
+ bytes_to_copy = remainder;
438
+ } else {
439
+ bytes_to_copy = bytes_in_buffer;
440
+ }
441
+ buffer = rf->buffers[buffer_number];
442
+ memcpy(val.iov_base + offset, buffer + buffer_offset, bytes_to_copy);
443
+ offset += bytes_to_copy;
444
+ start += bytes_to_copy;
445
+ remainder -= bytes_to_copy;
446
+ }
447
+
448
+ mdbxs_put(txn, dbi, &key, &val);
449
+ mdbxs_commit_txn(os->store, txn, del_env_txn);
450
+ free(val.iov_base);
451
+ rf_close(os->file.rf);
452
+ }
453
+
454
+ static const struct FrtOutStreamMethods MDBXS_OUT_STREAM_METHODS = {
455
+ ramo_flush_i,
456
+ ramo_seek_i,
457
+ mdbxso_close_i
458
+ };
459
+
460
+ static FrtOutStream *mdbxs_new_output(FrtStore *store, const char *filename) {
461
+ MDBX_txn *txn = NULL;
462
+ MDBX_dbi dbi;
463
+ MDBX_val key, val;
464
+ key.iov_base = (char *)filename;
465
+ key.iov_len = strlen(filename);
466
+ val.iov_base = NULL;
467
+ val.iov_len = 0;
468
+
469
+ bool del_env_txn = mdbxs_begin_txn(store, &txn);
470
+ mdbxs_open_dbi(txn, dbi_name, &dbi);
471
+ mdbxs_put(txn, dbi, &key, &val);
472
+ mdbxs_commit_txn(store, txn, del_env_txn);
473
+
474
+ FrtRAMFile *rf = rf_new(filename);
475
+ FRT_REF(rf);
476
+ FrtOutStream *os = frt_os_new();
477
+ os->pointer = 0;
478
+ os->file.rf = rf;
479
+ os->m = &MDBXS_OUT_STREAM_METHODS;
480
+ os->store = store;
481
+ return os;
482
+ }
483
+
484
+ static const struct FrtInStreamMethods RAM_IN_STREAM_METHODS = {
485
+ rami_read_i,
486
+ rami_seek_i,
487
+ rami_length_i,
488
+ rami_close_i
489
+ };
490
+
491
+ static FrtInStream *mdbxs_open_input(FrtStore *store, const char *filename) {
492
+ MDBX_txn *txn = NULL;
493
+ MDBX_dbi dbi;
494
+ MDBX_val key, val;
495
+ key.iov_base = (char *)filename;
496
+ key.iov_len = strlen(filename);
497
+ val.iov_base = NULL;
498
+ val.iov_len = 0;
499
+
500
+ FrtRAMFile *rf = NULL;
501
+ FrtInStream *is;
502
+
503
+ bool del_env_txn = mdbxs_begin_ro_txn(store, &txn);
504
+ mdbxs_open_dbi(txn, dbi_name, &dbi);
505
+ if (mdbx_get(txn, dbi, &key, &val) != MDBX_SUCCESS) {
506
+ mdbxs_abort_txn(store, txn, del_env_txn);
507
+ FRT_RAISE(FRT_FILE_NOT_FOUND_ERROR, "tried to open '%s' but it doesn't exist", filename);
508
+ }
509
+
510
+ rf = rf_new(filename);
511
+ FRT_REF(rf);
512
+
513
+ if (val.iov_len > 0) {
514
+ frt_uchar *buffer;
515
+ frt_uchar *src = val.iov_base;
516
+ int len = val.iov_len;
517
+ int buffer_number = 0;
518
+ int buffer_offset = 0;
519
+ int bytes_in_buffer = FRT_BUFFER_SIZE;
520
+ int bytes_to_copy = bytes_in_buffer < len ? bytes_in_buffer : len;
521
+ int src_offset;
522
+
523
+ rf_extend_if_necessary(rf, buffer_number);
524
+
525
+ buffer = rf->buffers[buffer_number];
526
+ memcpy(buffer + buffer_offset, src, bytes_to_copy);
527
+ src_offset = bytes_to_copy;
528
+
529
+ while (src_offset < len) {
530
+ bytes_to_copy = bytes_in_buffer < (len - src_offset) ? bytes_in_buffer : (len - src_offset);
531
+ buffer_number += 1;
532
+ rf_extend_if_necessary(rf, buffer_number);
533
+ buffer = rf->buffers[buffer_number];
534
+ memcpy(buffer, src + src_offset, bytes_to_copy);
535
+ src_offset = src_offset + bytes_to_copy;
536
+ }
537
+ rf->len = len;
538
+ }
539
+ mdbxs_abort_txn(store, txn, del_env_txn);
540
+
541
+ is = frt_is_new();
542
+ is->f->file.rf = rf;
543
+ is->f->ref_cnt = 1;
544
+ is->d.pointer = 0;
545
+ is->m = &RAM_IN_STREAM_METHODS;
546
+
547
+ return is;
548
+ }
549
+
550
+ #define LOCK_OBTAIN_TIMEOUT 50
551
+
552
+ static int mdbxs_lock_obtain(FrtLock *lock) {
553
+ MDBX_txn *txn = NULL;
554
+ MDBX_dbi dbi;
555
+ bool del_env_txn = mdbxs_begin_txn(lock->store, &txn);
556
+ mdbxs_open_dbi(txn, dbi_name, &dbi);
557
+
558
+ MDBX_val key, val;
559
+ key.iov_base = lock->name;
560
+ key.iov_len = strlen(lock->name);
561
+ int trys = LOCK_OBTAIN_TIMEOUT;
562
+ bool is_locked = mdbxs_is_locked(lock->store, &key, txn, dbi);
563
+ while (is_locked && (trys > 0)) {
564
+ mdbxs_abort_txn(lock->store, txn, del_env_txn);
565
+
566
+ trys--;
567
+ /* sleep for 10 milliseconds */
568
+ frt_micro_sleep(10000);
569
+
570
+ del_env_txn = mdbxs_begin_txn(lock->store, &txn);
571
+ mdbxs_open_dbi(txn, dbi_name, &dbi);
572
+
573
+ is_locked = mdbxs_is_locked(lock->store, &key, txn, dbi);
574
+ }
575
+ if (is_locked) {
576
+ mdbxs_abort_txn(lock->store, txn, del_env_txn);
577
+ return false;
578
+ } else {
579
+ val.iov_base = (char *)lock_s; // could be a number to count locks
580
+ val.iov_len = 4;
581
+ mdbxs_put(txn, dbi, &key, &val);
582
+ mdbxs_commit_txn(lock->store, txn, del_env_txn);
583
+ return true;
584
+ }
585
+ }
586
+
587
+ static int mdbxs_lock_is_locked(FrtLock *lock) {
588
+ int res = 0;
589
+ MDBX_txn *txn = NULL;
590
+ MDBX_dbi dbi;
591
+
592
+ MDBX_val key;
593
+ key.iov_base = lock->name;
594
+ key.iov_len = strlen(lock->name);
595
+
596
+ bool del_env_txn = mdbxs_begin_ro_txn(lock->store, &txn);
597
+ mdbxs_open_dbi(txn, dbi_name, &dbi);
598
+ res = mdbxs_is_locked(lock->store, &key, txn, dbi);
599
+ mdbxs_abort_txn(lock->store, txn, del_env_txn);
600
+
601
+ return res;
602
+ }
603
+
604
+ static void mdbxs_lock_release(FrtLock *lock) {
605
+ MDBX_txn *txn = NULL;
606
+ MDBX_dbi dbi;
607
+
608
+ MDBX_val key;
609
+ key.iov_base = lock->name;
610
+ key.iov_len = strlen(lock->name);
611
+
612
+ bool del_env_txn = mdbxs_begin_txn(lock->store, &txn);
613
+ mdbxs_open_dbi(txn, dbi_name, &dbi);
614
+ mdbxs_del(txn, dbi, &key, NULL);
615
+ mdbxs_commit_txn(lock->store, txn, del_env_txn);
616
+ }
617
+
618
+ static FrtLock *mdbxs_open_lock_i(FrtStore *store, const char *lockname) {
619
+ FrtLock *lock = FRT_ALLOC(FrtLock);
620
+ char lname[100];
621
+ snprintf(lname, 100, "%s%s.lck", FRT_LOCK_PREFIX, lockname);
622
+ lock->name = frt_estrdup(lname);
623
+ lock->store = store;
624
+ FRT_REF(store);
625
+ lock->obtain = &mdbxs_lock_obtain;
626
+ lock->release = &mdbxs_lock_release;
627
+ lock->is_locked = &mdbxs_lock_is_locked;
628
+ lock->rlock = Qnil;
629
+ return lock;
630
+ }
631
+
632
+ static void mdbxs_close_lock_i(FrtLock *lock) {
633
+ mdbxs_lock_release(lock);
634
+ frt_store_close(lock->store);
635
+ free(lock->name);
636
+ free(lock);
637
+ }
638
+
639
+ static FrtHash *mdbx_stores = NULL;
640
+
641
+ static pthread_mutex_t mdbx_stores_mutex = PTHREAD_MUTEX_INITIALIZER;
642
+
643
+ static void mdbxs_close_i(FrtStore *store) {
644
+ pthread_mutex_lock(&mdbx_stores_mutex);
645
+ if (store->dir.mdbx) {
646
+ frt_h_del(mdbx_stores, store->dir.mdbx->path);
647
+ }
648
+ pthread_mutex_unlock(&mdbx_stores_mutex);
649
+ }
650
+
651
+ static FrtStore *mdbx_store_new(const char *pathname) {
652
+ FrtStore *new_store = frt_store_new();
653
+
654
+ new_store->file_mode = S_IRUSR | S_IWUSR;
655
+ #if !defined POSH_OS_WIN32 && !defined POSH_OS_WIN64
656
+ struct stat stt;
657
+ if (!stat(pathname, &stt)) {
658
+ gid_t st_gid = stt.st_gid;
659
+ bool is_grp = (st_gid == getgid());
660
+
661
+ if (!is_grp) {
662
+ int size = getgroups(0, NULL);
663
+ gid_t list[size];
664
+
665
+ if (getgroups(size, list) != -1) {
666
+ int i = 0;
667
+ while (i < size && !(is_grp = (st_gid == list[i]))) i++;
668
+ }
669
+ }
670
+
671
+ if (is_grp) {
672
+ if (stt.st_mode & S_IWGRP) {
673
+ umask(S_IWOTH);
674
+ }
675
+ new_store->file_mode |= stt.st_mode & (S_IRGRP | S_IWGRP);
676
+ }
677
+ }
678
+ #endif
679
+
680
+ MDBX_env *env = NULL;
681
+ int res;
682
+ res = mdbx_env_create(&env);
683
+ if (res != MDBX_SUCCESS) {
684
+ FRT_RAISE(FRT_IO_ERROR, "creating env '%s' failed, error: %i\n", pathname, res);
685
+ }
686
+ res = mdbx_env_set_maxdbs(env, 2);
687
+ if (res != MDBX_SUCCESS) {
688
+ mdbx_env_close(env);
689
+ FRT_RAISE(FRT_IO_ERROR, "setting maxdbs for '%s' failed, error: %i\n", pathname, res);
690
+ }
691
+ res = mdbx_env_set_geometry(env, -1, -1, 40000000000, 4*1024*1024, 4*1024*1024, MDBX_MAX_PAGESIZE);
692
+ if (res != MDBX_SUCCESS) {
693
+ mdbx_env_close(env);
694
+ FRT_RAISE(FRT_IO_ERROR, "setting geometrye for '%s' failed, error: %i\n", pathname, res);
695
+ }
696
+ res = mdbx_env_open(env, pathname, MDBX_ENV_DEFAULTS, new_store->file_mode);
697
+ if (res != MDBX_SUCCESS) {
698
+ mdbx_env_close(env);
699
+ if (res > 0) {
700
+ FRT_RAISE(FRT_IO_ERROR, "opening env '%s' failed, error: %s\n", pathname, strerror(res));
701
+ } else {
702
+ FRT_RAISE(FRT_IO_ERROR, "opening env '%s' failed, error: %i\n", pathname, res);
703
+ }
704
+ }
705
+ MDBXInfo *mdbx = FRT_ALLOC(MDBXInfo);
706
+ mdbx->env = env;
707
+ mdbx->path = frt_estrdup(pathname);
708
+
709
+ new_store->dir.mdbx = mdbx;
710
+ new_store->touch = &mdbxs_touch;
711
+ new_store->exists = &mdbxs_exists;
712
+ new_store->remove = &mdbxs_remove;
713
+ new_store->rename = &mdbxs_rename;
714
+ new_store->count = &mdbxs_count;
715
+ new_store->close_i = &mdbxs_close_i;
716
+ new_store->clear = &mdbxs_clear;
717
+ new_store->clear_all = &mdbxs_clear_all;
718
+ new_store->clear_locks = &mdbxs_clear_locks;
719
+ new_store->length = &mdbxs_length;
720
+ new_store->each = &mdbxs_each;
721
+ new_store->new_output = &mdbxs_new_output;
722
+ new_store->open_input = &mdbxs_open_input;
723
+ new_store->open_lock_i = &mdbxs_open_lock_i;
724
+ new_store->close_lock_i = &mdbxs_close_lock_i;
725
+ return new_store;
726
+ }
727
+
728
+ FrtStore *frt_open_mdbx_store(const char *pathname) {
729
+ FrtStore *store = NULL;
730
+
731
+ if (!mdbx_stores) {
732
+ mdbx_stores = frt_h_new_str(free, (frt_free_ft)mdbxs_destroy);
733
+ frt_register_for_cleanup(mdbx_stores, (frt_free_ft)frt_h_destroy);
734
+ }
735
+
736
+ pthread_mutex_lock(&mdbx_stores_mutex);
737
+
738
+ store = (FrtStore *)frt_h_get(mdbx_stores, pathname);
739
+ if (store) {
740
+ FRT_REF(store);
741
+ } else {
742
+ store = mdbx_store_new(pathname);
743
+ frt_h_set(mdbx_stores, frt_estrdup(pathname), store);
744
+ }
745
+
746
+ pthread_mutex_unlock(&mdbx_stores_mutex);
747
+
748
+ return store;
749
+ }