pogocache-ruby 0.1.0
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/CHANGELOG.md +5 -0
- data/LICENSE +662 -0
- data/README.md +121 -0
- data/Rakefile +33 -0
- data/ext/pogocache_ruby/extconf.rb +4 -0
- data/ext/pogocache_ruby/pogocache.c +1990 -0
- data/ext/pogocache_ruby/pogocache.h +209 -0
- data/ext/pogocache_ruby/pogocache_ruby.c +526 -0
- data/lib/pogocache-ruby/cache.rb +84 -0
- data/lib/pogocache-ruby/configuration.rb +12 -0
- data/lib/pogocache-ruby/version.rb +5 -0
- data/lib/pogocache-ruby.rb +18 -0
- metadata +127 -0
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
#include <ruby.h>
|
|
2
|
+
#include "pogocache.h"
|
|
3
|
+
|
|
4
|
+
// Ruby module and class definitions
|
|
5
|
+
static VALUE mPogocache;
|
|
6
|
+
static VALUE cExtension;
|
|
7
|
+
static VALUE ePogoError;
|
|
8
|
+
|
|
9
|
+
// Data type for wrapping pogocache struct
|
|
10
|
+
typedef struct {
|
|
11
|
+
struct pogocache *cache;
|
|
12
|
+
} pogocache_wrapper_t;
|
|
13
|
+
|
|
14
|
+
// Cleanup function for garbage collection
|
|
15
|
+
static void pogocache_free_wrapper(void *ptr) {
|
|
16
|
+
pogocache_wrapper_t *wrapper = (pogocache_wrapper_t*)ptr;
|
|
17
|
+
if (wrapper && wrapper->cache) {
|
|
18
|
+
pogocache_free(wrapper->cache);
|
|
19
|
+
wrapper->cache = NULL;
|
|
20
|
+
}
|
|
21
|
+
free(wrapper);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static const rb_data_type_t pogocache_data_type = {
|
|
25
|
+
"Pogocache",
|
|
26
|
+
{0, pogocache_free_wrapper, 0},
|
|
27
|
+
0, 0,
|
|
28
|
+
RUBY_TYPED_FREE_IMMEDIATELY
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Helper function to get pogocache from Ruby object
|
|
32
|
+
static pogocache_wrapper_t* get_pogocache_wrapper(VALUE self) {
|
|
33
|
+
pogocache_wrapper_t *wrapper;
|
|
34
|
+
TypedData_Get_Struct(self, pogocache_wrapper_t, &pogocache_data_type, wrapper);
|
|
35
|
+
if (!wrapper->cache) {
|
|
36
|
+
rb_raise(ePogoError, "Pogocache instance has been freed");
|
|
37
|
+
}
|
|
38
|
+
return wrapper;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Helper function to convert Ruby hash to pogocache_opts
|
|
42
|
+
static struct pogocache_opts* hash_to_opts(VALUE opts_hash) {
|
|
43
|
+
static struct pogocache_opts opts;
|
|
44
|
+
memset(&opts, 0, sizeof(opts));
|
|
45
|
+
|
|
46
|
+
// Set defaults
|
|
47
|
+
opts.nshards = 65536;
|
|
48
|
+
opts.loadfactor = 75;
|
|
49
|
+
|
|
50
|
+
if (NIL_P(opts_hash)) {
|
|
51
|
+
return &opts;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
Check_Type(opts_hash, T_HASH);
|
|
55
|
+
|
|
56
|
+
VALUE val;
|
|
57
|
+
|
|
58
|
+
// usecas
|
|
59
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("usecas")));
|
|
60
|
+
if (!NIL_P(val)) opts.usecas = RTEST(val);
|
|
61
|
+
|
|
62
|
+
// nosixpack
|
|
63
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("nosixpack")));
|
|
64
|
+
if (!NIL_P(val)) opts.nosixpack = RTEST(val);
|
|
65
|
+
|
|
66
|
+
// noevict
|
|
67
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("noevict")));
|
|
68
|
+
if (!NIL_P(val)) opts.noevict = RTEST(val);
|
|
69
|
+
|
|
70
|
+
// allowshrink
|
|
71
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("allowshrink")));
|
|
72
|
+
if (!NIL_P(val)) opts.allowshrink = RTEST(val);
|
|
73
|
+
|
|
74
|
+
// usethreadbatch
|
|
75
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("usethreadbatch")));
|
|
76
|
+
if (!NIL_P(val)) opts.usethreadbatch = RTEST(val);
|
|
77
|
+
|
|
78
|
+
// nshards
|
|
79
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("nshards")));
|
|
80
|
+
if (!NIL_P(val)) opts.nshards = NUM2INT(val);
|
|
81
|
+
|
|
82
|
+
// loadfactor
|
|
83
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("loadfactor")));
|
|
84
|
+
if (!NIL_P(val)) opts.loadfactor = NUM2INT(val);
|
|
85
|
+
|
|
86
|
+
// seed
|
|
87
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("seed")));
|
|
88
|
+
if (!NIL_P(val)) opts.seed = NUM2ULL(val);
|
|
89
|
+
|
|
90
|
+
return &opts;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Initialize a new Pogocache instance
|
|
94
|
+
// Pogocache.new(opts = {})
|
|
95
|
+
static VALUE pogocache_initialize(int argc, VALUE *argv, VALUE self) {
|
|
96
|
+
VALUE opts_hash;
|
|
97
|
+
rb_scan_args(argc, argv, "01", &opts_hash);
|
|
98
|
+
|
|
99
|
+
struct pogocache_opts *opts = hash_to_opts(opts_hash);
|
|
100
|
+
struct pogocache *cache = pogocache_new(opts);
|
|
101
|
+
|
|
102
|
+
if (!cache) {
|
|
103
|
+
rb_raise(ePogoError, "Failed to create pogocache instance");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
pogocache_wrapper_t *wrapper = malloc(sizeof(pogocache_wrapper_t));
|
|
107
|
+
wrapper->cache = cache;
|
|
108
|
+
|
|
109
|
+
DATA_PTR(self) = wrapper;
|
|
110
|
+
return self;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Store a key-value pair
|
|
114
|
+
// extension.store(key, value, opts = {})
|
|
115
|
+
static VALUE pogocache_store_rb(int argc, VALUE *argv, VALUE self) {
|
|
116
|
+
VALUE key, value, opts_hash;
|
|
117
|
+
rb_scan_args(argc, argv, "21", &key, &value, &opts_hash);
|
|
118
|
+
|
|
119
|
+
pogocache_wrapper_t *wrapper = get_pogocache_wrapper(self);
|
|
120
|
+
|
|
121
|
+
// Convert Ruby strings to C strings
|
|
122
|
+
Check_Type(key, T_STRING);
|
|
123
|
+
Check_Type(value, T_STRING);
|
|
124
|
+
|
|
125
|
+
const char *key_ptr = RSTRING_PTR(key);
|
|
126
|
+
size_t key_len = RSTRING_LEN(key);
|
|
127
|
+
const char *value_ptr = RSTRING_PTR(value);
|
|
128
|
+
size_t value_len = RSTRING_LEN(value);
|
|
129
|
+
|
|
130
|
+
// Parse options
|
|
131
|
+
struct pogocache_store_opts opts;
|
|
132
|
+
memset(&opts, 0, sizeof(opts));
|
|
133
|
+
|
|
134
|
+
if (!NIL_P(opts_hash)) {
|
|
135
|
+
Check_Type(opts_hash, T_HASH);
|
|
136
|
+
|
|
137
|
+
VALUE val;
|
|
138
|
+
|
|
139
|
+
// ttl
|
|
140
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("ttl")));
|
|
141
|
+
if (!NIL_P(val)) opts.ttl = NUM2LL(val);
|
|
142
|
+
|
|
143
|
+
// expires
|
|
144
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("expires")));
|
|
145
|
+
if (!NIL_P(val)) opts.expires = NUM2LL(val);
|
|
146
|
+
|
|
147
|
+
// flags
|
|
148
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("flags")));
|
|
149
|
+
if (!NIL_P(val)) opts.flags = NUM2UINT(val);
|
|
150
|
+
|
|
151
|
+
// cas
|
|
152
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("cas")));
|
|
153
|
+
if (!NIL_P(val)) opts.cas = NUM2ULL(val);
|
|
154
|
+
|
|
155
|
+
// nx (only set if not exists)
|
|
156
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("nx")));
|
|
157
|
+
if (!NIL_P(val)) opts.nx = RTEST(val);
|
|
158
|
+
|
|
159
|
+
// xx (only set if exists)
|
|
160
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("xx")));
|
|
161
|
+
if (!NIL_P(val)) opts.xx = RTEST(val);
|
|
162
|
+
|
|
163
|
+
// keepttl
|
|
164
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("keepttl")));
|
|
165
|
+
if (!NIL_P(val)) opts.keepttl = RTEST(val);
|
|
166
|
+
|
|
167
|
+
// casop
|
|
168
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("casop")));
|
|
169
|
+
if (!NIL_P(val)) opts.casop = RTEST(val);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
int result = pogocache_store(wrapper->cache, key_ptr, key_len, value_ptr, value_len, &opts);
|
|
173
|
+
return INT2NUM(result);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Load callback data structure
|
|
177
|
+
typedef struct {
|
|
178
|
+
VALUE result_hash;
|
|
179
|
+
bool found;
|
|
180
|
+
} load_callback_data_t;
|
|
181
|
+
|
|
182
|
+
// Callback function for pogocache_load
|
|
183
|
+
static void load_entry_callback(int shard, int64_t time, const void *key, size_t keylen,
|
|
184
|
+
const void *value, size_t valuelen, int64_t expires,
|
|
185
|
+
uint32_t flags, uint64_t cas, struct pogocache_update **update,
|
|
186
|
+
void *udata) {
|
|
187
|
+
load_callback_data_t *data = (load_callback_data_t*)udata;
|
|
188
|
+
|
|
189
|
+
data->result_hash = rb_hash_new();
|
|
190
|
+
data->found = true;
|
|
191
|
+
|
|
192
|
+
rb_hash_aset(data->result_hash, ID2SYM(rb_intern("key")),
|
|
193
|
+
rb_str_new((const char*)key, keylen));
|
|
194
|
+
rb_hash_aset(data->result_hash, ID2SYM(rb_intern("value")),
|
|
195
|
+
rb_str_new((const char*)value, valuelen));
|
|
196
|
+
rb_hash_aset(data->result_hash, ID2SYM(rb_intern("expires")), LL2NUM(expires));
|
|
197
|
+
rb_hash_aset(data->result_hash, ID2SYM(rb_intern("flags")), UINT2NUM(flags));
|
|
198
|
+
rb_hash_aset(data->result_hash, ID2SYM(rb_intern("cas")), ULL2NUM(cas));
|
|
199
|
+
rb_hash_aset(data->result_hash, ID2SYM(rb_intern("shard")), INT2NUM(shard));
|
|
200
|
+
rb_hash_aset(data->result_hash, ID2SYM(rb_intern("time")), LL2NUM(time));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Load a value by key
|
|
204
|
+
// extension.load(key, opts = {})
|
|
205
|
+
static VALUE pogocache_load_rb(int argc, VALUE *argv, VALUE self) {
|
|
206
|
+
VALUE key, opts_hash;
|
|
207
|
+
rb_scan_args(argc, argv, "11", &key, &opts_hash);
|
|
208
|
+
|
|
209
|
+
pogocache_wrapper_t *wrapper = get_pogocache_wrapper(self);
|
|
210
|
+
|
|
211
|
+
Check_Type(key, T_STRING);
|
|
212
|
+
const char *key_ptr = RSTRING_PTR(key);
|
|
213
|
+
size_t key_len = RSTRING_LEN(key);
|
|
214
|
+
|
|
215
|
+
struct pogocache_load_opts opts;
|
|
216
|
+
memset(&opts, 0, sizeof(opts));
|
|
217
|
+
|
|
218
|
+
load_callback_data_t callback_data;
|
|
219
|
+
callback_data.result_hash = Qnil;
|
|
220
|
+
callback_data.found = false;
|
|
221
|
+
|
|
222
|
+
opts.entry = load_entry_callback;
|
|
223
|
+
opts.udata = &callback_data;
|
|
224
|
+
|
|
225
|
+
if (!NIL_P(opts_hash)) {
|
|
226
|
+
Check_Type(opts_hash, T_HASH);
|
|
227
|
+
|
|
228
|
+
VALUE val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("notouch")));
|
|
229
|
+
if (!NIL_P(val)) opts.notouch = RTEST(val);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
int result = pogocache_load(wrapper->cache, key_ptr, key_len, &opts);
|
|
233
|
+
if (result != POGOCACHE_FOUND) {
|
|
234
|
+
return Qnil;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (callback_data.found) {
|
|
238
|
+
return callback_data.result_hash;
|
|
239
|
+
} else {
|
|
240
|
+
return Qnil;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Delete a key
|
|
245
|
+
// extension.delete(key, opts = {})
|
|
246
|
+
static VALUE pogocache_delete_rb(int argc, VALUE *argv, VALUE self) {
|
|
247
|
+
VALUE key, opts_hash;
|
|
248
|
+
rb_scan_args(argc, argv, "11", &key, &opts_hash);
|
|
249
|
+
|
|
250
|
+
pogocache_wrapper_t *wrapper = get_pogocache_wrapper(self);
|
|
251
|
+
|
|
252
|
+
Check_Type(key, T_STRING);
|
|
253
|
+
const char *key_ptr = RSTRING_PTR(key);
|
|
254
|
+
size_t key_len = RSTRING_LEN(key);
|
|
255
|
+
|
|
256
|
+
struct pogocache_delete_opts opts;
|
|
257
|
+
memset(&opts, 0, sizeof(opts));
|
|
258
|
+
|
|
259
|
+
int result = pogocache_delete(wrapper->cache, key_ptr, key_len, &opts);
|
|
260
|
+
return INT2NUM(result);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Get cache count
|
|
264
|
+
// extension.count(opts = {})
|
|
265
|
+
static VALUE pogocache_count_rb(int argc, VALUE *argv, VALUE self) {
|
|
266
|
+
VALUE opts_hash;
|
|
267
|
+
rb_scan_args(argc, argv, "01", &opts_hash);
|
|
268
|
+
|
|
269
|
+
pogocache_wrapper_t *wrapper = get_pogocache_wrapper(self);
|
|
270
|
+
|
|
271
|
+
struct pogocache_count_opts opts;
|
|
272
|
+
memset(&opts, 0, sizeof(opts));
|
|
273
|
+
|
|
274
|
+
if (!NIL_P(opts_hash)) {
|
|
275
|
+
Check_Type(opts_hash, T_HASH);
|
|
276
|
+
|
|
277
|
+
VALUE val;
|
|
278
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("oneshard")));
|
|
279
|
+
if (!NIL_P(val)) opts.oneshard = RTEST(val);
|
|
280
|
+
|
|
281
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("oneshardidx")));
|
|
282
|
+
if (!NIL_P(val)) opts.oneshardidx = NUM2INT(val);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
size_t count = pogocache_count(wrapper->cache, &opts);
|
|
286
|
+
return SIZET2NUM(count);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Get cache memory size
|
|
290
|
+
// extension.size(opts = {})
|
|
291
|
+
static VALUE pogocache_size_rb(int argc, VALUE *argv, VALUE self) {
|
|
292
|
+
VALUE opts_hash;
|
|
293
|
+
rb_scan_args(argc, argv, "01", &opts_hash);
|
|
294
|
+
|
|
295
|
+
pogocache_wrapper_t *wrapper = get_pogocache_wrapper(self);
|
|
296
|
+
|
|
297
|
+
struct pogocache_size_opts opts;
|
|
298
|
+
memset(&opts, 0, sizeof(opts));
|
|
299
|
+
|
|
300
|
+
if (!NIL_P(opts_hash)) {
|
|
301
|
+
Check_Type(opts_hash, T_HASH);
|
|
302
|
+
|
|
303
|
+
VALUE val;
|
|
304
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("oneshard")));
|
|
305
|
+
if (!NIL_P(val)) opts.oneshard = RTEST(val);
|
|
306
|
+
|
|
307
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("oneshardidx")));
|
|
308
|
+
if (!NIL_P(val)) opts.oneshardidx = NUM2INT(val);
|
|
309
|
+
|
|
310
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("entriesonly")));
|
|
311
|
+
if (!NIL_P(val)) opts.entriesonly = RTEST(val);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
size_t size = pogocache_size(wrapper->cache, &opts);
|
|
315
|
+
return SIZET2NUM(size);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Clear the cache
|
|
319
|
+
// extension.clear(opts = {})
|
|
320
|
+
static VALUE pogocache_clear_rb(int argc, VALUE *argv, VALUE self) {
|
|
321
|
+
VALUE opts_hash;
|
|
322
|
+
rb_scan_args(argc, argv, "01", &opts_hash);
|
|
323
|
+
|
|
324
|
+
pogocache_wrapper_t *wrapper = get_pogocache_wrapper(self);
|
|
325
|
+
|
|
326
|
+
struct pogocache_clear_opts opts;
|
|
327
|
+
memset(&opts, 0, sizeof(opts));
|
|
328
|
+
|
|
329
|
+
if (!NIL_P(opts_hash)) {
|
|
330
|
+
Check_Type(opts_hash, T_HASH);
|
|
331
|
+
|
|
332
|
+
VALUE val;
|
|
333
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("oneshard")));
|
|
334
|
+
if (!NIL_P(val)) opts.oneshard = RTEST(val);
|
|
335
|
+
|
|
336
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("oneshardidx")));
|
|
337
|
+
if (!NIL_P(val)) opts.oneshardidx = NUM2INT(val);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
pogocache_clear(wrapper->cache, &opts);
|
|
341
|
+
return Qnil;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Sweep expired entries
|
|
345
|
+
// extension.sweep(opts = {})
|
|
346
|
+
static VALUE pogocache_sweep_rb(int argc, VALUE *argv, VALUE self) {
|
|
347
|
+
VALUE opts_hash;
|
|
348
|
+
rb_scan_args(argc, argv, "01", &opts_hash);
|
|
349
|
+
|
|
350
|
+
pogocache_wrapper_t *wrapper = get_pogocache_wrapper(self);
|
|
351
|
+
|
|
352
|
+
struct pogocache_sweep_opts opts;
|
|
353
|
+
memset(&opts, 0, sizeof(opts));
|
|
354
|
+
|
|
355
|
+
if (!NIL_P(opts_hash)) {
|
|
356
|
+
Check_Type(opts_hash, T_HASH);
|
|
357
|
+
|
|
358
|
+
VALUE val;
|
|
359
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("oneshard")));
|
|
360
|
+
if (!NIL_P(val)) opts.oneshard = RTEST(val);
|
|
361
|
+
|
|
362
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("oneshardidx")));
|
|
363
|
+
if (!NIL_P(val)) opts.oneshardidx = NUM2INT(val);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
size_t swept = 0, kept = 0;
|
|
367
|
+
pogocache_sweep(wrapper->cache, &swept, &kept, &opts);
|
|
368
|
+
|
|
369
|
+
VALUE result = rb_hash_new();
|
|
370
|
+
rb_hash_aset(result, ID2SYM(rb_intern("swept")), SIZET2NUM(swept));
|
|
371
|
+
rb_hash_aset(result, ID2SYM(rb_intern("kept")), SIZET2NUM(kept));
|
|
372
|
+
|
|
373
|
+
return result;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Iterator callback data
|
|
377
|
+
typedef struct {
|
|
378
|
+
VALUE result_array;
|
|
379
|
+
VALUE proc;
|
|
380
|
+
} iter_callback_data_t;
|
|
381
|
+
|
|
382
|
+
// Callback function for iteration
|
|
383
|
+
static int iter_entry_callback(int shard, int64_t time, const void *key, size_t keylen,
|
|
384
|
+
const void *value, size_t valuelen, int64_t expires,
|
|
385
|
+
uint32_t flags, uint64_t cas, void *udata) {
|
|
386
|
+
iter_callback_data_t *data = (iter_callback_data_t*)udata;
|
|
387
|
+
|
|
388
|
+
VALUE entry_hash = rb_hash_new();
|
|
389
|
+
rb_hash_aset(entry_hash, ID2SYM(rb_intern("key")),
|
|
390
|
+
rb_str_new((const char*)key, keylen));
|
|
391
|
+
rb_hash_aset(entry_hash, ID2SYM(rb_intern("value")),
|
|
392
|
+
rb_str_new((const char*)value, valuelen));
|
|
393
|
+
rb_hash_aset(entry_hash, ID2SYM(rb_intern("expires")), LL2NUM(expires));
|
|
394
|
+
rb_hash_aset(entry_hash, ID2SYM(rb_intern("flags")), UINT2NUM(flags));
|
|
395
|
+
rb_hash_aset(entry_hash, ID2SYM(rb_intern("cas")), ULL2NUM(cas));
|
|
396
|
+
rb_hash_aset(entry_hash, ID2SYM(rb_intern("shard")), INT2NUM(shard));
|
|
397
|
+
rb_hash_aset(entry_hash, ID2SYM(rb_intern("time")), LL2NUM(time));
|
|
398
|
+
|
|
399
|
+
if (!NIL_P(data->proc)) {
|
|
400
|
+
VALUE result = rb_funcall(data->proc, rb_intern("call"), 1, entry_hash);
|
|
401
|
+
return NUM2INT(result);
|
|
402
|
+
} else {
|
|
403
|
+
rb_ary_push(data->result_array, entry_hash);
|
|
404
|
+
return POGOCACHE_ITER_CONTINUE;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Iterate over cache entries
|
|
409
|
+
// extension.each(opts = {}) { |entry| ... }
|
|
410
|
+
// extension.each(opts = {}) # returns array
|
|
411
|
+
static VALUE pogocache_each_rb(int argc, VALUE *argv, VALUE self) {
|
|
412
|
+
VALUE opts_hash, proc;
|
|
413
|
+
rb_scan_args(argc, argv, "01&", &opts_hash, &proc);
|
|
414
|
+
|
|
415
|
+
pogocache_wrapper_t *wrapper = get_pogocache_wrapper(self);
|
|
416
|
+
|
|
417
|
+
struct pogocache_iter_opts opts;
|
|
418
|
+
memset(&opts, 0, sizeof(opts));
|
|
419
|
+
|
|
420
|
+
iter_callback_data_t callback_data;
|
|
421
|
+
callback_data.result_array = rb_ary_new();
|
|
422
|
+
callback_data.proc = proc;
|
|
423
|
+
|
|
424
|
+
opts.entry = iter_entry_callback;
|
|
425
|
+
opts.udata = &callback_data;
|
|
426
|
+
|
|
427
|
+
if (!NIL_P(opts_hash)) {
|
|
428
|
+
Check_Type(opts_hash, T_HASH);
|
|
429
|
+
|
|
430
|
+
VALUE val;
|
|
431
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("oneshard")));
|
|
432
|
+
if (!NIL_P(val)) opts.oneshard = RTEST(val);
|
|
433
|
+
|
|
434
|
+
val = rb_hash_aref(opts_hash, ID2SYM(rb_intern("oneshardidx")));
|
|
435
|
+
if (!NIL_P(val)) opts.oneshardidx = NUM2INT(val);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
pogocache_iter(wrapper->cache, &opts);
|
|
439
|
+
|
|
440
|
+
if (!NIL_P(proc)) {
|
|
441
|
+
return self;
|
|
442
|
+
} else {
|
|
443
|
+
return callback_data.result_array;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Get number of shards
|
|
448
|
+
// extension.nshards
|
|
449
|
+
static VALUE pogocache_nshards_rb(VALUE self) {
|
|
450
|
+
pogocache_wrapper_t *wrapper = get_pogocache_wrapper(self);
|
|
451
|
+
int nshards = pogocache_nshards(wrapper->cache);
|
|
452
|
+
return INT2NUM(nshards);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Get current time
|
|
456
|
+
// Pogocache.now
|
|
457
|
+
static VALUE pogocache_now_rb(VALUE self) {
|
|
458
|
+
int64_t now = pogocache_now();
|
|
459
|
+
return LL2NUM(now);
|
|
460
|
+
}
|
|
461
|
+
static VALUE pogocache_alloc(VALUE klass) {
|
|
462
|
+
pogocache_wrapper_t *wrapper = malloc(sizeof(pogocache_wrapper_t));
|
|
463
|
+
wrapper->cache = NULL;
|
|
464
|
+
return TypedData_Wrap_Struct(klass, &pogocache_data_type, wrapper);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Extension initialization
|
|
468
|
+
void Init_pogocache_ruby(void) {
|
|
469
|
+
// Define module
|
|
470
|
+
mPogocache = rb_define_module("Pogocache");
|
|
471
|
+
|
|
472
|
+
// Define Cache class
|
|
473
|
+
cExtension = rb_define_class_under(mPogocache, "Extension", rb_cObject);
|
|
474
|
+
rb_define_alloc_func(cExtension, pogocache_alloc);
|
|
475
|
+
|
|
476
|
+
// Define exception class
|
|
477
|
+
ePogoError = rb_define_class_under(mPogocache, "Error", rb_eStandardError);
|
|
478
|
+
|
|
479
|
+
// Instance methods
|
|
480
|
+
rb_define_method(cExtension, "initialize", pogocache_initialize, -1);
|
|
481
|
+
rb_define_method(cExtension, "store", pogocache_store_rb, -1);
|
|
482
|
+
rb_define_method(cExtension, "load", pogocache_load_rb, -1);
|
|
483
|
+
rb_define_method(cExtension, "delete", pogocache_delete_rb, -1);
|
|
484
|
+
rb_define_method(cExtension, "count", pogocache_count_rb, -1);
|
|
485
|
+
rb_define_method(cExtension, "size", pogocache_size_rb, -1);
|
|
486
|
+
rb_define_method(cExtension, "clear", pogocache_clear_rb, -1);
|
|
487
|
+
rb_define_method(cExtension, "sweep", pogocache_sweep_rb, -1);
|
|
488
|
+
rb_define_method(cExtension, "each", pogocache_each_rb, -1);
|
|
489
|
+
rb_define_method(cExtension, "nshards", pogocache_nshards_rb, 0);
|
|
490
|
+
|
|
491
|
+
// Convenience aliases
|
|
492
|
+
rb_define_alias(cExtension, "[]", "load");
|
|
493
|
+
rb_define_alias(cExtension, "[]=", "store");
|
|
494
|
+
rb_define_alias(cExtension, "length", "count");
|
|
495
|
+
|
|
496
|
+
// Module methods
|
|
497
|
+
rb_define_module_function(mPogocache, "now", pogocache_now_rb, 0);
|
|
498
|
+
|
|
499
|
+
// Constants
|
|
500
|
+
rb_define_const(mPogocache, "INSERTED", INT2NUM(POGOCACHE_INSERTED));
|
|
501
|
+
rb_define_const(mPogocache, "REPLACED", INT2NUM(POGOCACHE_REPLACED));
|
|
502
|
+
rb_define_const(mPogocache, "FOUND", INT2NUM(POGOCACHE_FOUND));
|
|
503
|
+
rb_define_const(mPogocache, "NOTFOUND", INT2NUM(POGOCACHE_NOTFOUND));
|
|
504
|
+
rb_define_const(mPogocache, "DELETED", INT2NUM(POGOCACHE_DELETED));
|
|
505
|
+
rb_define_const(mPogocache, "FINISHED", INT2NUM(POGOCACHE_FINISHED));
|
|
506
|
+
rb_define_const(mPogocache, "CANCELED", INT2NUM(POGOCACHE_CANCELED));
|
|
507
|
+
rb_define_const(mPogocache, "NOMEM", INT2NUM(POGOCACHE_NOMEM));
|
|
508
|
+
|
|
509
|
+
// Time constants
|
|
510
|
+
rb_define_const(mPogocache, "NANOSECOND", LL2NUM(POGOCACHE_NANOSECOND));
|
|
511
|
+
rb_define_const(mPogocache, "MICROSECOND", LL2NUM(POGOCACHE_MICROSECOND));
|
|
512
|
+
rb_define_const(mPogocache, "MILLISECOND", LL2NUM(POGOCACHE_MILLISECOND));
|
|
513
|
+
rb_define_const(mPogocache, "SECOND", LL2NUM(POGOCACHE_SECOND));
|
|
514
|
+
rb_define_const(mPogocache, "MINUTE", LL2NUM(POGOCACHE_MINUTE));
|
|
515
|
+
rb_define_const(mPogocache, "HOUR", LL2NUM(POGOCACHE_HOUR));
|
|
516
|
+
|
|
517
|
+
// Iterator constants
|
|
518
|
+
rb_define_const(mPogocache, "ITER_CONTINUE", INT2NUM(POGOCACHE_ITER_CONTINUE));
|
|
519
|
+
rb_define_const(mPogocache, "ITER_STOP", INT2NUM(POGOCACHE_ITER_STOP));
|
|
520
|
+
rb_define_const(mPogocache, "ITER_DELETE", INT2NUM(POGOCACHE_ITER_DELETE));
|
|
521
|
+
|
|
522
|
+
// Eviction reason constants
|
|
523
|
+
rb_define_const(mPogocache, "REASON_EXPIRED", INT2NUM(POGOCACHE_REASON_EXPIRED));
|
|
524
|
+
rb_define_const(mPogocache, "REASON_LOWMEM", INT2NUM(POGOCACHE_REASON_LOWMEM));
|
|
525
|
+
rb_define_const(mPogocache, "REASON_CLEARED", INT2NUM(POGOCACHE_REASON_CLEARED));
|
|
526
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
class Pogocache::Cache
|
|
2
|
+
def initialize(options = {})
|
|
3
|
+
@extension = Pogocache::Extension.new(Pogocache.configuration.default_opts.merge(options))
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def set(key, value, options = {})
|
|
7
|
+
@extension.store(encode(key), encode(value), options)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def get(key)
|
|
11
|
+
decode(@extension.load(encode(key))&.dig(:value))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def entry(key)
|
|
15
|
+
@extension.load(encode(key))&.tap do
|
|
16
|
+
it[:key] = decode(it[:key])
|
|
17
|
+
it[:value] = decode(it[:value])
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def delete(key)
|
|
22
|
+
@extension.delete(encode(key))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def [](key)
|
|
26
|
+
get(key)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def []=(key, value)
|
|
30
|
+
set(key, value)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def fetch(key, &block)
|
|
34
|
+
value = get(key)
|
|
35
|
+
return value if value
|
|
36
|
+
|
|
37
|
+
block&.call
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def count
|
|
41
|
+
@extension.count
|
|
42
|
+
end
|
|
43
|
+
alias_method(:size, :count)
|
|
44
|
+
|
|
45
|
+
def bytesize
|
|
46
|
+
@extension.size
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def clear
|
|
50
|
+
@extension.clear
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def sweep
|
|
54
|
+
@extension.sweep
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def each(opts = {}, &block)
|
|
58
|
+
if block_given?
|
|
59
|
+
retval = []
|
|
60
|
+
@extension.each(opts) do
|
|
61
|
+
retval << yield(decode(it[:key]), decode(it[:value]))
|
|
62
|
+
|
|
63
|
+
Pogocache::ITER_CONTINUE # standard:disable Lint/Void
|
|
64
|
+
end
|
|
65
|
+
retval
|
|
66
|
+
else
|
|
67
|
+
@extension.each(opts)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def nshards = @extension.nshards
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def encode(obj)
|
|
76
|
+
Marshal.dump(obj)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def decode(str)
|
|
80
|
+
return nil if str.nil? || str.empty?
|
|
81
|
+
|
|
82
|
+
Marshal.load(str)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "pogocache_ruby"
|
|
4
|
+
require_relative "pogocache-ruby/version"
|
|
5
|
+
require_relative "pogocache-ruby/configuration"
|
|
6
|
+
require_relative "pogocache-ruby/cache"
|
|
7
|
+
|
|
8
|
+
module Pogocache
|
|
9
|
+
class << self
|
|
10
|
+
def configuration
|
|
11
|
+
@configuration ||= Configuration.new
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def configure
|
|
15
|
+
yield(configuration)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|