sophia-ruby 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.gitmodules +3 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +22 -0
- data/ext/extconf.rb +13 -0
- data/ext/sophia.c +220 -0
- data/lib/sophia-ruby.rb +1 -0
- data/lib/sophia/version.rb +3 -0
- data/sophia-ruby.gemspec +47 -0
- data/test/test_sophia.rb +33 -0
- data/vendor/sophia/.gitignore +18 -0
- data/vendor/sophia/COPYRIGHT +29 -0
- data/vendor/sophia/README +5 -0
- data/vendor/sophia/db/a.h +58 -0
- data/vendor/sophia/db/cat.c +195 -0
- data/vendor/sophia/db/cat.h +32 -0
- data/vendor/sophia/db/core.h +129 -0
- data/vendor/sophia/db/crc.c +343 -0
- data/vendor/sophia/db/crc.h +14 -0
- data/vendor/sophia/db/cursor.c +551 -0
- data/vendor/sophia/db/cursor.h +47 -0
- data/vendor/sophia/db/e.c +49 -0
- data/vendor/sophia/db/e.h +49 -0
- data/vendor/sophia/db/file.c +355 -0
- data/vendor/sophia/db/file.h +106 -0
- data/vendor/sophia/db/gc.c +71 -0
- data/vendor/sophia/db/gc.h +14 -0
- data/vendor/sophia/db/i.c +368 -0
- data/vendor/sophia/db/i.h +155 -0
- data/vendor/sophia/db/list.h +91 -0
- data/vendor/sophia/db/lock.h +77 -0
- data/vendor/sophia/db/macro.h +20 -0
- data/vendor/sophia/db/makefile +44 -0
- data/vendor/sophia/db/merge.c +662 -0
- data/vendor/sophia/db/merge.h +14 -0
- data/vendor/sophia/db/meta.h +87 -0
- data/vendor/sophia/db/recover.c +433 -0
- data/vendor/sophia/db/recover.h +14 -0
- data/vendor/sophia/db/ref.h +111 -0
- data/vendor/sophia/db/rep.c +128 -0
- data/vendor/sophia/db/rep.h +120 -0
- data/vendor/sophia/db/sophia.h +84 -0
- data/vendor/sophia/db/sp.c +626 -0
- data/vendor/sophia/db/sp.h +50 -0
- data/vendor/sophia/db/task.h +70 -0
- data/vendor/sophia/db/track.h +99 -0
- data/vendor/sophia/db/util.c +105 -0
- data/vendor/sophia/db/util.h +25 -0
- data/vendor/sophia/makefile +7 -0
- data/vendor/sophia/sophia.gyp +30 -0
- data/vendor/sophia/test/common.c +870 -0
- data/vendor/sophia/test/crash.c +492 -0
- data/vendor/sophia/test/i.c +403 -0
- data/vendor/sophia/test/limit.c +65 -0
- data/vendor/sophia/test/makefile +30 -0
- data/vendor/sophia/test/merge.c +890 -0
- data/vendor/sophia/test/recover.c +1550 -0
- data/vendor/sophia/test/test.h +66 -0
- metadata +134 -0
@@ -0,0 +1,626 @@
|
|
1
|
+
|
2
|
+
/*
|
3
|
+
* sophia database
|
4
|
+
* sphia.org
|
5
|
+
*
|
6
|
+
* Copyright (c) Dmitry Simonenko
|
7
|
+
* BSD License
|
8
|
+
*/
|
9
|
+
|
10
|
+
#include <sp.h>
|
11
|
+
|
12
|
+
static inline int sphot
|
13
|
+
cmpstd(char *a, size_t asz, char *b, size_t bsz, void *arg spunused) {
|
14
|
+
register size_t sz = (asz < bsz ? asz : bsz);
|
15
|
+
register int rc = memcmp(a, b, sz);
|
16
|
+
return (rc == 0 ? rc : (rc > 0 ? 1 : -1));
|
17
|
+
}
|
18
|
+
|
19
|
+
static inline void sp_envinit(spenv *e) {
|
20
|
+
e->m = SPMENV;
|
21
|
+
e->inuse = 0;
|
22
|
+
sp_einit(&e->e);
|
23
|
+
e->alloc = sp_allocstd;
|
24
|
+
e->allocarg = NULL;
|
25
|
+
e->cmp = cmpstd;
|
26
|
+
e->cmparg = NULL;
|
27
|
+
e->page = 2048;
|
28
|
+
e->dir = NULL;
|
29
|
+
e->flags = 0;
|
30
|
+
e->mergewm = 100000;
|
31
|
+
e->merge = 1;
|
32
|
+
e->dbnewsize = 2 * 1024 * 1024;
|
33
|
+
e->dbgrow = 1.4;
|
34
|
+
e->gc = 1;
|
35
|
+
e->gcfactor = 0.5;
|
36
|
+
}
|
37
|
+
|
38
|
+
static inline void sp_envfree(spenv *e) {
|
39
|
+
if (e->dir) {
|
40
|
+
free(e->dir);
|
41
|
+
e->dir = NULL;
|
42
|
+
}
|
43
|
+
sp_efree(&e->e);
|
44
|
+
}
|
45
|
+
|
46
|
+
static inline int sp_envvalidate(spenv *e)
|
47
|
+
{
|
48
|
+
/* check if environment is not already
|
49
|
+
* in use.
|
50
|
+
* do not set other environment error status
|
51
|
+
* in that case.
|
52
|
+
*/
|
53
|
+
if (e->inuse)
|
54
|
+
return -1;
|
55
|
+
if (e->dir == NULL)
|
56
|
+
return sp_ee(e, SPE, "directory is not specified");
|
57
|
+
if (e->mergewm < 2)
|
58
|
+
return sp_ee(e, SPE, "bad merge watermark count");
|
59
|
+
if (e->page < 2)
|
60
|
+
return sp_ee(e, SPE, "bad page size");
|
61
|
+
if ((e->page % 2) > 0)
|
62
|
+
return sp_ee(e, SPE, "bad page size must be even");
|
63
|
+
return 0;
|
64
|
+
}
|
65
|
+
|
66
|
+
void *sp_env(void) {
|
67
|
+
spenv *e = malloc(sizeof(spenv));
|
68
|
+
if (spunlikely(e == NULL))
|
69
|
+
return NULL;
|
70
|
+
sp_envinit(e);
|
71
|
+
return e;
|
72
|
+
}
|
73
|
+
|
74
|
+
static int sp_ctlenv(spenv *e, spopt opt, va_list args)
|
75
|
+
{
|
76
|
+
if (e->inuse)
|
77
|
+
return sp_ee(e, SPEOOM, "can't change env opts while in-use");
|
78
|
+
switch (opt) {
|
79
|
+
case SPDIR: {
|
80
|
+
uint32_t flags = va_arg(args, uint32_t);
|
81
|
+
char *path = va_arg(args, char*);
|
82
|
+
char *p = strdup(path);
|
83
|
+
if (spunlikely(p == NULL))
|
84
|
+
return sp_ee(e, SPEOOM, "failed to allocate memory");
|
85
|
+
if (spunlikely(e->dir)) {
|
86
|
+
free(e->dir);
|
87
|
+
e->dir = NULL;
|
88
|
+
}
|
89
|
+
e->dir = p;
|
90
|
+
e->flags = flags;
|
91
|
+
break;
|
92
|
+
}
|
93
|
+
case SPALLOC:
|
94
|
+
e->alloc = va_arg(args, spallocf);
|
95
|
+
e->allocarg = va_arg(args, void*);
|
96
|
+
break;
|
97
|
+
case SPCMP:
|
98
|
+
e->cmp = va_arg(args, spcmpf);
|
99
|
+
e->cmparg = va_arg(args, void*);
|
100
|
+
break;
|
101
|
+
case SPPAGE:
|
102
|
+
e->page = va_arg(args, uint32_t);
|
103
|
+
break;
|
104
|
+
case SPGC:
|
105
|
+
e->gc = va_arg(args, int);
|
106
|
+
break;
|
107
|
+
case SPGCF:
|
108
|
+
e->gcfactor = va_arg(args, double);
|
109
|
+
break;
|
110
|
+
case SPGROW:
|
111
|
+
e->dbnewsize = va_arg(args, uint32_t);
|
112
|
+
e->dbgrow = va_arg(args, double);
|
113
|
+
break;
|
114
|
+
case SPMERGE:
|
115
|
+
e->merge = va_arg(args, int);
|
116
|
+
break;
|
117
|
+
case SPMERGEWM:
|
118
|
+
e->mergewm = va_arg(args, uint32_t);
|
119
|
+
break;
|
120
|
+
default:
|
121
|
+
return sp_ee(e, SPE, "bad arguments");
|
122
|
+
}
|
123
|
+
return 0;
|
124
|
+
}
|
125
|
+
|
126
|
+
static int sp_ctldb(sp *s, spopt opt, va_list args spunused)
|
127
|
+
{
|
128
|
+
switch (opt) {
|
129
|
+
case SPMERGEFORCE:
|
130
|
+
if (s->e->merge)
|
131
|
+
return sp_e(s, SPE, "force merge doesn't work with merger thread active");
|
132
|
+
return sp_merge(s);
|
133
|
+
default:
|
134
|
+
return sp_e(s, SPE, "bad arguments");
|
135
|
+
}
|
136
|
+
return 0;
|
137
|
+
}
|
138
|
+
|
139
|
+
int sp_ctl(void *o, spopt opt, ...)
|
140
|
+
{
|
141
|
+
va_list args;
|
142
|
+
va_start(args, opt);
|
143
|
+
spmagic *magic = (spmagic*)o;
|
144
|
+
int rc;
|
145
|
+
if (opt == SPVERSION) {
|
146
|
+
uint32_t *major = va_arg(args, uint32_t*);
|
147
|
+
uint32_t *minor = va_arg(args, uint32_t*);
|
148
|
+
*major = SP_VERSION_MAJOR;
|
149
|
+
*minor = SP_VERSION_MINOR;
|
150
|
+
return 0;
|
151
|
+
}
|
152
|
+
switch (*magic) {
|
153
|
+
case SPMENV: rc = sp_ctlenv(o, opt, args);
|
154
|
+
break;
|
155
|
+
case SPMDB: rc = sp_ctldb(o, opt, args);
|
156
|
+
break;
|
157
|
+
default: rc = -1;
|
158
|
+
break;
|
159
|
+
}
|
160
|
+
va_end(args);
|
161
|
+
return rc;
|
162
|
+
}
|
163
|
+
|
164
|
+
int sp_rotate(sp *s)
|
165
|
+
{
|
166
|
+
int rc;
|
167
|
+
sp_repepochincrement(&s->rep);
|
168
|
+
/* allocate new epoch */
|
169
|
+
spepoch *e = sp_repalloc(&s->rep, sp_repepoch(&s->rep));
|
170
|
+
if (spunlikely(s == NULL))
|
171
|
+
return sp_e(s, SPEOOM, "failed to allocate repository");
|
172
|
+
/* create log file */
|
173
|
+
rc = sp_lognew(&e->log, s->e->dir, sp_repepoch(&s->rep));
|
174
|
+
if (spunlikely(rc == -1)) {
|
175
|
+
sp_free(&s->a, e);
|
176
|
+
return sp_e(s, SPEIO, "failed to create log file", e->epoch);
|
177
|
+
}
|
178
|
+
splogh h;
|
179
|
+
h.magic = SPMAGIC;
|
180
|
+
h.version[0] = SP_VERSION_MAJOR;
|
181
|
+
h.version[1] = SP_VERSION_MINOR;
|
182
|
+
sp_logadd(&e->log, (char*)&h, sizeof(h));
|
183
|
+
rc = sp_logflush(&e->log);
|
184
|
+
if (spunlikely(rc == -1)) {
|
185
|
+
sp_logclose(&e->log);
|
186
|
+
sp_free(&s->a, e);
|
187
|
+
return sp_e(s, SPEIO, "failed to write log file", e->epoch);
|
188
|
+
}
|
189
|
+
/* attach epoch and mark it is as live */
|
190
|
+
sp_repattach(&s->rep, e);
|
191
|
+
sp_repset(&s->rep, e, SPLIVE);
|
192
|
+
return 0;
|
193
|
+
}
|
194
|
+
|
195
|
+
static inline int sp_closerep(sp *s)
|
196
|
+
{
|
197
|
+
int rcret = 0;
|
198
|
+
int rc = 0;
|
199
|
+
splist *i, *n;
|
200
|
+
sp_listforeach_safe(&s->rep.l, i, n) {
|
201
|
+
spepoch *e = spcast(i, spepoch, link);
|
202
|
+
switch (e->type) {
|
203
|
+
case SPUNDEF:
|
204
|
+
/* this type is true to a epoch that has beed
|
205
|
+
* scheduled for a recovery, but not happen to
|
206
|
+
* proceed yet. */
|
207
|
+
break;
|
208
|
+
case SPLIVE:
|
209
|
+
if (e->nupdate == 0) {
|
210
|
+
rc = sp_logunlink(&e->log);
|
211
|
+
if (spunlikely(rc == -1))
|
212
|
+
rcret = sp_e(s, SPEIO, "failed to unlink log file", e->epoch);
|
213
|
+
rc = sp_logclose(&e->log);
|
214
|
+
if (spunlikely(rc == -1))
|
215
|
+
rcret = sp_e(s, SPEIO, "failed to close log file", e->epoch);
|
216
|
+
break;
|
217
|
+
} else {
|
218
|
+
rc = sp_logeof(&e->log);
|
219
|
+
if (spunlikely(rc == -1))
|
220
|
+
rcret = sp_e(s, SPEIO, "failed to write eof marker", e->epoch);
|
221
|
+
}
|
222
|
+
case SPXFER:
|
223
|
+
rc = sp_logcomplete(&e->log);
|
224
|
+
if (spunlikely(rc == -1))
|
225
|
+
rcret = sp_e(s, SPEIO, "failed to complete log file", e->epoch);
|
226
|
+
rc = sp_logclose(&e->log);
|
227
|
+
if (spunlikely(rc == -1))
|
228
|
+
rcret = sp_e(s, SPEIO, "failed to close log file", e->epoch);
|
229
|
+
break;
|
230
|
+
case SPDB:
|
231
|
+
rc = sp_mapclose(&e->db);
|
232
|
+
if (spunlikely(rc == -1))
|
233
|
+
rcret = sp_e(s, SPEIO, "failed to close db file", e->epoch);
|
234
|
+
break;
|
235
|
+
}
|
236
|
+
sp_free(&s->a, e);
|
237
|
+
}
|
238
|
+
return rcret;
|
239
|
+
}
|
240
|
+
|
241
|
+
static inline int sp_close(sp *s)
|
242
|
+
{
|
243
|
+
int rcret = 0;
|
244
|
+
int rc = 0;
|
245
|
+
s->stop = 1;
|
246
|
+
if (s->e->merge) {
|
247
|
+
rc = sp_taskstop(&s->merger);
|
248
|
+
if (spunlikely(rc == -1))
|
249
|
+
rcret = sp_e(s, SPESYS, "failed to stop merger thread");
|
250
|
+
}
|
251
|
+
sp_refsetfree(&s->refs, &s->a);
|
252
|
+
rc = sp_closerep(s);
|
253
|
+
if (spunlikely(rc == -1))
|
254
|
+
rcret = -1;
|
255
|
+
sp_ifree(&s->i0);
|
256
|
+
sp_ifree(&s->i1);
|
257
|
+
sp_catfree(&s->s);
|
258
|
+
s->e->inuse = 0;
|
259
|
+
sp_lockfree(&s->lockr);
|
260
|
+
sp_lockfree(&s->locks);
|
261
|
+
sp_lockfree(&s->locki);
|
262
|
+
return rcret;
|
263
|
+
}
|
264
|
+
|
265
|
+
static void *merger(void *arg)
|
266
|
+
{
|
267
|
+
sptask *self = arg;
|
268
|
+
sp *s = self->arg;
|
269
|
+
do {
|
270
|
+
sp_lock(&s->locki);
|
271
|
+
int merge = s->i->count > s->e->mergewm;
|
272
|
+
sp_unlock(&s->locki);
|
273
|
+
if (! merge)
|
274
|
+
continue;
|
275
|
+
int rc = sp_merge(s);
|
276
|
+
if (spunlikely(rc == -1)) {
|
277
|
+
sp_taskdone(self);
|
278
|
+
return NULL;
|
279
|
+
}
|
280
|
+
} while (sp_taskwait(self));
|
281
|
+
|
282
|
+
return NULL;
|
283
|
+
}
|
284
|
+
|
285
|
+
void *sp_open(void *e)
|
286
|
+
{
|
287
|
+
spenv *env = e;
|
288
|
+
assert(env->m == SPMENV);
|
289
|
+
int rc = sp_envvalidate(env);
|
290
|
+
if (spunlikely(rc == -1))
|
291
|
+
return NULL;
|
292
|
+
spa a;
|
293
|
+
sp_allocinit(&a, env->alloc, env->allocarg);
|
294
|
+
sp *s = sp_malloc(&a, sizeof(sp));
|
295
|
+
if (spunlikely(s == NULL)) {
|
296
|
+
sp_e(s, SPEOOM, "failed to allocate db handle");
|
297
|
+
return NULL;
|
298
|
+
}
|
299
|
+
memset(s, 0, sizeof(sp));
|
300
|
+
s->m = SPMDB;
|
301
|
+
s->e = env;
|
302
|
+
s->e->inuse = 1;
|
303
|
+
memcpy(&s->a, &a, sizeof(s->a));
|
304
|
+
/* init locks */
|
305
|
+
sp_lockinit(&s->lockr);
|
306
|
+
sp_lockinit(&s->locks);
|
307
|
+
sp_lockinit(&s->locki);
|
308
|
+
s->lockc = 0;
|
309
|
+
/* init key index */
|
310
|
+
rc = sp_iinit(&s->i0, &s->a, 1024, s->e->cmp, s->e->cmparg);
|
311
|
+
if (spunlikely(rc == -1)) {
|
312
|
+
sp_e(s, SPEOOM, "failed to allocate key index");
|
313
|
+
goto e0;
|
314
|
+
}
|
315
|
+
rc = sp_iinit(&s->i1, &s->a, 1024, s->e->cmp, s->e->cmparg);
|
316
|
+
if (spunlikely(rc == -1)) {
|
317
|
+
sp_e(s, SPEOOM, "failed to allocate key index");
|
318
|
+
goto e1;
|
319
|
+
}
|
320
|
+
s->i = &s->i0;
|
321
|
+
/* init page index */
|
322
|
+
s->psn = 0;
|
323
|
+
rc = sp_catinit(&s->s, &s->a, 512, s->e->cmp, s->e->cmparg);
|
324
|
+
if (spunlikely(rc == -1)) {
|
325
|
+
sp_e(s, SPEOOM, "failed to allocate page index");
|
326
|
+
goto e2;
|
327
|
+
}
|
328
|
+
sp_repinit(&s->rep, &s->a);
|
329
|
+
rc = sp_recover(s);
|
330
|
+
if (spunlikely(rc == -1))
|
331
|
+
goto e3;
|
332
|
+
/* do not create new live epoch in read-only mode */
|
333
|
+
if (! (s->e->flags & SPO_RDONLY)) {
|
334
|
+
rc = sp_rotate(s);
|
335
|
+
if (spunlikely(rc == -1))
|
336
|
+
goto e3;
|
337
|
+
}
|
338
|
+
s->stop = 0;
|
339
|
+
rc = sp_refsetinit(&s->refs, &s->a, s->e->page);
|
340
|
+
if (spunlikely(rc == -1)) {
|
341
|
+
sp_e(s, SPEOOM, "failed to allocate key buffer");
|
342
|
+
goto e3;
|
343
|
+
}
|
344
|
+
if (s->e->merge) {
|
345
|
+
rc = sp_taskstart(&s->merger, merger, s);
|
346
|
+
if (spunlikely(rc == -1)) {
|
347
|
+
sp_e(s, SPESYS, "failed to start merger thread");
|
348
|
+
goto e4;
|
349
|
+
}
|
350
|
+
sp_taskwakeup(&s->merger);
|
351
|
+
}
|
352
|
+
return s;
|
353
|
+
e4: sp_refsetfree(&s->refs, &s->a);
|
354
|
+
e3: sp_closerep(s);
|
355
|
+
sp_catfree(&s->s);
|
356
|
+
e2: sp_ifree(&s->i1);
|
357
|
+
e1: sp_ifree(&s->i0);
|
358
|
+
e0: s->e->inuse = 0;
|
359
|
+
sp_lockfree(&s->lockr);
|
360
|
+
sp_lockfree(&s->locks);
|
361
|
+
sp_lockfree(&s->locki);
|
362
|
+
sp_free(&a, s);
|
363
|
+
return NULL;
|
364
|
+
}
|
365
|
+
|
366
|
+
int sp_destroy(void *o)
|
367
|
+
{
|
368
|
+
spmagic *magic = (spmagic*)o;
|
369
|
+
spa *a = NULL;
|
370
|
+
int rc = 0;
|
371
|
+
switch (*magic) {
|
372
|
+
case SPMNONE:
|
373
|
+
assert(0);
|
374
|
+
return -1;
|
375
|
+
case SPMENV: {
|
376
|
+
spenv *env = (spenv*)o;
|
377
|
+
if (env->inuse)
|
378
|
+
return -1;
|
379
|
+
sp_envfree(env);
|
380
|
+
*magic = SPMNONE;
|
381
|
+
free(o);
|
382
|
+
return 0;
|
383
|
+
}
|
384
|
+
case SPMCUR: {
|
385
|
+
spc *c = (spc*)o;
|
386
|
+
a = &c->s->a;
|
387
|
+
sp_cursorclose(c);
|
388
|
+
break;
|
389
|
+
}
|
390
|
+
case SPMDB: {
|
391
|
+
sp *s = (sp*)o;
|
392
|
+
a = &s->a;
|
393
|
+
rc = sp_close(s);
|
394
|
+
break;
|
395
|
+
}
|
396
|
+
default:
|
397
|
+
return -1;
|
398
|
+
}
|
399
|
+
*magic = SPMNONE;
|
400
|
+
sp_free(a, o);
|
401
|
+
return rc;
|
402
|
+
}
|
403
|
+
|
404
|
+
char *sp_error(void *o)
|
405
|
+
{
|
406
|
+
spmagic *magic = (spmagic*)o;
|
407
|
+
spe *e = NULL;
|
408
|
+
switch (*magic) {
|
409
|
+
case SPMDB: {
|
410
|
+
sp *s = o;
|
411
|
+
e = &s->e->e;
|
412
|
+
break;
|
413
|
+
}
|
414
|
+
case SPMENV: {
|
415
|
+
spenv *env = o;
|
416
|
+
e = &env->e;
|
417
|
+
break;
|
418
|
+
}
|
419
|
+
default:
|
420
|
+
assert(0);
|
421
|
+
return NULL;
|
422
|
+
}
|
423
|
+
if (! sp_eis(e))
|
424
|
+
return NULL;
|
425
|
+
return e->e;
|
426
|
+
}
|
427
|
+
|
428
|
+
static inline int
|
429
|
+
sp_do(sp *s, int op, void *k, size_t ksize, void *v, size_t vsize)
|
430
|
+
{
|
431
|
+
/* allocate new version.
|
432
|
+
*
|
433
|
+
* try to reduce lock contention by making the alloc and
|
434
|
+
* the crc calculation before log write.
|
435
|
+
*/
|
436
|
+
spv *n = sp_vnewv(s, k, ksize, v, vsize);
|
437
|
+
if (spunlikely(n == NULL))
|
438
|
+
return sp_e(s, SPEOOM, "failed to allocate version");
|
439
|
+
/* prepare log record */
|
440
|
+
spvh h = {
|
441
|
+
.crc = 0,
|
442
|
+
.size = ksize,
|
443
|
+
.voffset = 0,
|
444
|
+
.vsize = vsize,
|
445
|
+
.flags = op
|
446
|
+
};
|
447
|
+
/* calculate crc */
|
448
|
+
uint32_t crc;
|
449
|
+
crc = sp_crc32c(0, k, ksize);
|
450
|
+
crc = sp_crc32c(crc, v, vsize);
|
451
|
+
h.crc = sp_crc32c(crc, &h.size, sizeof(spvh) - sizeof(uint32_t));
|
452
|
+
|
453
|
+
sp_lock(&s->lockr);
|
454
|
+
sp_lock(&s->locki);
|
455
|
+
|
456
|
+
/* write to current live epoch log */
|
457
|
+
spepoch *live = sp_replive(&s->rep);
|
458
|
+
sp_filesvp(&live->log);
|
459
|
+
sp_logadd(&live->log, &h, sizeof(spvh));
|
460
|
+
sp_logadd(&live->log, k, ksize);
|
461
|
+
sp_logadd(&live->log, v, vsize);
|
462
|
+
int rc = sp_logflush(&live->log);
|
463
|
+
if (spunlikely(rc == -1)) {
|
464
|
+
sp_free(&s->a, n);
|
465
|
+
sp_logrlb(&live->log);
|
466
|
+
sp_unlock(&s->locki);
|
467
|
+
sp_unlock(&s->lockr);
|
468
|
+
return sp_e(s, SPEIO, "failed to write log file", live->epoch);
|
469
|
+
}
|
470
|
+
|
471
|
+
/* add new version to the index */
|
472
|
+
spv *old = NULL;
|
473
|
+
n->epoch = live->epoch;
|
474
|
+
n->flags = op;
|
475
|
+
n->crc = crc;
|
476
|
+
rc = sp_iset(s->i, n, &old);
|
477
|
+
if (spunlikely(rc == -1)) {
|
478
|
+
sp_free(&s->a, n);
|
479
|
+
sp_unlock(&s->locki);
|
480
|
+
sp_unlock(&s->lockr);
|
481
|
+
return sp_e(s, SPEOOM, "failed to allocate key index page");
|
482
|
+
}
|
483
|
+
|
484
|
+
sp_unlock(&s->locki);
|
485
|
+
sp_unlock(&s->lockr);
|
486
|
+
|
487
|
+
if (old)
|
488
|
+
sp_free(&s->a, old);
|
489
|
+
|
490
|
+
/* wake up merger on merge watermark reached */
|
491
|
+
live->nupdate++;
|
492
|
+
if ((live->nupdate % s->e->mergewm) == 0) {
|
493
|
+
if (splikely(s->e->merge))
|
494
|
+
sp_taskwakeup(&s->merger);
|
495
|
+
}
|
496
|
+
return 0;
|
497
|
+
}
|
498
|
+
|
499
|
+
int sp_set(void *o, const void *k, size_t ksize, const void *v, size_t vsize)
|
500
|
+
{
|
501
|
+
sp *s = o;
|
502
|
+
assert(s->m == SPMDB);
|
503
|
+
if (spunlikely(sp_eis(&s->e->e)))
|
504
|
+
return -1;
|
505
|
+
if (spunlikely(s->e->flags & SPO_RDONLY))
|
506
|
+
return sp_e(s, SPE, "db handle is read-only");
|
507
|
+
if (spunlikely(ksize > UINT16_MAX))
|
508
|
+
return sp_e(s, SPE, "key size limit reached");
|
509
|
+
if (spunlikely(vsize > UINT32_MAX))
|
510
|
+
return sp_e(s, SPE, "value size limit reached");
|
511
|
+
if (spunlikely(s->lockc))
|
512
|
+
return sp_e(s, SPE, "modify with open cursor");
|
513
|
+
return sp_do(s, SPSET, (char*)k, ksize, (char*)v, vsize);
|
514
|
+
}
|
515
|
+
|
516
|
+
int sp_delete(void *o, const void *k, size_t ksize)
|
517
|
+
{
|
518
|
+
sp *s = o;
|
519
|
+
assert(s->m == SPMDB);
|
520
|
+
if (spunlikely(sp_eis(&s->e->e)))
|
521
|
+
return -1;
|
522
|
+
if (spunlikely(s->e->flags & SPO_RDONLY))
|
523
|
+
return sp_e(s, SPE, "db handle is read-only");
|
524
|
+
if (spunlikely(ksize > UINT16_MAX))
|
525
|
+
return sp_e(s, SPE, "key size limit reached");
|
526
|
+
if (spunlikely(s->lockc))
|
527
|
+
return sp_e(s, SPE, "modify with open cursor");
|
528
|
+
return sp_do(s, SPDEL, (char*)k, ksize, NULL, 0);
|
529
|
+
}
|
530
|
+
|
531
|
+
static inline int
|
532
|
+
sp_checkro(sp *s, size_t ksize)
|
533
|
+
{
|
534
|
+
if (spunlikely(sp_eis(&s->e->e)))
|
535
|
+
return -1;
|
536
|
+
if (spunlikely(ksize > UINT16_MAX))
|
537
|
+
return sp_e(s, SPE, "key size limit reached");
|
538
|
+
return 0;
|
539
|
+
}
|
540
|
+
|
541
|
+
int sp_get(void *o, const void *k, size_t ksize, void **v, size_t *vsize)
|
542
|
+
{
|
543
|
+
sp *s = o;
|
544
|
+
assert(s->m == SPMDB);
|
545
|
+
if (spunlikely(sp_checkro(s, ksize) == -1))
|
546
|
+
return -1;
|
547
|
+
return sp_match(s, (char*)k, ksize, v, vsize);
|
548
|
+
}
|
549
|
+
|
550
|
+
void *sp_cursor(void *o, sporder order, const void *k, size_t ksize)
|
551
|
+
{
|
552
|
+
sp *s = o;
|
553
|
+
assert(s->m == SPMDB);
|
554
|
+
if (spunlikely(sp_checkro(s, ksize) == -1))
|
555
|
+
return NULL;
|
556
|
+
spc *c = sp_malloc(&s->a, sizeof(spc));
|
557
|
+
if (spunlikely(c == NULL)) {
|
558
|
+
sp_e(s, SPEOOM, "failed to allocate cursor handle");
|
559
|
+
return NULL;
|
560
|
+
}
|
561
|
+
memset(c, 0, sizeof(spc));
|
562
|
+
sp_cursoropen(c, s, order, (char*)k, ksize);
|
563
|
+
return c;
|
564
|
+
}
|
565
|
+
|
566
|
+
int sp_fetch(void *o) {
|
567
|
+
spc *c = o;
|
568
|
+
assert(c->m == SPMCUR);
|
569
|
+
if (spunlikely(sp_eis(&c->s->e->e)))
|
570
|
+
return -1;
|
571
|
+
return sp_iterate(c);
|
572
|
+
}
|
573
|
+
|
574
|
+
const char *sp_key(void *o)
|
575
|
+
{
|
576
|
+
spc *c = o;
|
577
|
+
assert(c->m == SPMCUR);
|
578
|
+
return sp_refk(&c->r);
|
579
|
+
}
|
580
|
+
|
581
|
+
size_t sp_keysize(void *o)
|
582
|
+
{
|
583
|
+
spc *c = o;
|
584
|
+
assert(c->m == SPMCUR);
|
585
|
+
return sp_refksize(&c->r);
|
586
|
+
}
|
587
|
+
|
588
|
+
const char *sp_value(void *o)
|
589
|
+
{
|
590
|
+
spc *c = o;
|
591
|
+
assert(c->m == SPMCUR);
|
592
|
+
return sp_refv(&c->r, (char*)c->ph);
|
593
|
+
}
|
594
|
+
|
595
|
+
size_t sp_valuesize(void *o)
|
596
|
+
{
|
597
|
+
spc *c = o;
|
598
|
+
assert(c->m == SPMCUR);
|
599
|
+
return sp_refvsize(&c->r);
|
600
|
+
}
|
601
|
+
|
602
|
+
void sp_stat(void *o, spstat *stat)
|
603
|
+
{
|
604
|
+
spmagic *magic = (spmagic*)o;
|
605
|
+
if (*magic != SPMDB) {
|
606
|
+
memset(stat, 0, sizeof(*stat));
|
607
|
+
return;
|
608
|
+
}
|
609
|
+
sp *s = o;
|
610
|
+
sp_lock(&s->lockr);
|
611
|
+
sp_lock(&s->locki);
|
612
|
+
sp_lock(&s->locks);
|
613
|
+
|
614
|
+
stat->epoch = s->rep.epoch;
|
615
|
+
stat->psn = s->psn;
|
616
|
+
stat->repn = s->rep.n;
|
617
|
+
stat->repndb = s->rep.ndb;
|
618
|
+
stat->repnxfer = s->rep.nxfer;
|
619
|
+
stat->catn = s->s.count;
|
620
|
+
stat->indexn = s->i->count;
|
621
|
+
stat->indexpages = s->i->icount;
|
622
|
+
|
623
|
+
sp_unlock(&s->locks);
|
624
|
+
sp_unlock(&s->locki);
|
625
|
+
sp_unlock(&s->lockr);
|
626
|
+
}
|