sophia-ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.gitmodules +3 -0
  4. data/Gemfile +6 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +29 -0
  7. data/Rakefile +22 -0
  8. data/ext/extconf.rb +13 -0
  9. data/ext/sophia.c +220 -0
  10. data/lib/sophia-ruby.rb +1 -0
  11. data/lib/sophia/version.rb +3 -0
  12. data/sophia-ruby.gemspec +47 -0
  13. data/test/test_sophia.rb +33 -0
  14. data/vendor/sophia/.gitignore +18 -0
  15. data/vendor/sophia/COPYRIGHT +29 -0
  16. data/vendor/sophia/README +5 -0
  17. data/vendor/sophia/db/a.h +58 -0
  18. data/vendor/sophia/db/cat.c +195 -0
  19. data/vendor/sophia/db/cat.h +32 -0
  20. data/vendor/sophia/db/core.h +129 -0
  21. data/vendor/sophia/db/crc.c +343 -0
  22. data/vendor/sophia/db/crc.h +14 -0
  23. data/vendor/sophia/db/cursor.c +551 -0
  24. data/vendor/sophia/db/cursor.h +47 -0
  25. data/vendor/sophia/db/e.c +49 -0
  26. data/vendor/sophia/db/e.h +49 -0
  27. data/vendor/sophia/db/file.c +355 -0
  28. data/vendor/sophia/db/file.h +106 -0
  29. data/vendor/sophia/db/gc.c +71 -0
  30. data/vendor/sophia/db/gc.h +14 -0
  31. data/vendor/sophia/db/i.c +368 -0
  32. data/vendor/sophia/db/i.h +155 -0
  33. data/vendor/sophia/db/list.h +91 -0
  34. data/vendor/sophia/db/lock.h +77 -0
  35. data/vendor/sophia/db/macro.h +20 -0
  36. data/vendor/sophia/db/makefile +44 -0
  37. data/vendor/sophia/db/merge.c +662 -0
  38. data/vendor/sophia/db/merge.h +14 -0
  39. data/vendor/sophia/db/meta.h +87 -0
  40. data/vendor/sophia/db/recover.c +433 -0
  41. data/vendor/sophia/db/recover.h +14 -0
  42. data/vendor/sophia/db/ref.h +111 -0
  43. data/vendor/sophia/db/rep.c +128 -0
  44. data/vendor/sophia/db/rep.h +120 -0
  45. data/vendor/sophia/db/sophia.h +84 -0
  46. data/vendor/sophia/db/sp.c +626 -0
  47. data/vendor/sophia/db/sp.h +50 -0
  48. data/vendor/sophia/db/task.h +70 -0
  49. data/vendor/sophia/db/track.h +99 -0
  50. data/vendor/sophia/db/util.c +105 -0
  51. data/vendor/sophia/db/util.h +25 -0
  52. data/vendor/sophia/makefile +7 -0
  53. data/vendor/sophia/sophia.gyp +30 -0
  54. data/vendor/sophia/test/common.c +870 -0
  55. data/vendor/sophia/test/crash.c +492 -0
  56. data/vendor/sophia/test/i.c +403 -0
  57. data/vendor/sophia/test/limit.c +65 -0
  58. data/vendor/sophia/test/makefile +30 -0
  59. data/vendor/sophia/test/merge.c +890 -0
  60. data/vendor/sophia/test/recover.c +1550 -0
  61. data/vendor/sophia/test/test.h +66 -0
  62. 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
+ }