hirlite 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 +15 -0
- data/LICENSE +28 -0
- data/Rakefile +51 -0
- data/ext/hirlite_ext/extconf.rb +33 -0
- data/ext/hirlite_ext/hirlite_ext.c +14 -0
- data/ext/hirlite_ext/hirlite_ext.h +38 -0
- data/ext/hirlite_ext/rlite.c +351 -0
- data/lib/hirlite/rlite.rb +1 -0
- data/lib/hirlite/version.rb +3 -0
- data/lib/hirlite.rb +2 -0
- data/vendor/rlite/Makefile +6 -0
- data/vendor/rlite/deps/crc64.c +191 -0
- data/vendor/rlite/deps/crc64.h +3 -0
- data/vendor/rlite/deps/endianconv.h +73 -0
- data/vendor/rlite/deps/hyperloglog.c +1547 -0
- data/vendor/rlite/deps/hyperloglog.h +14 -0
- data/vendor/rlite/deps/lzf.h +100 -0
- data/vendor/rlite/deps/lzfP.h +159 -0
- data/vendor/rlite/deps/lzf_c.c +295 -0
- data/vendor/rlite/deps/lzf_d.c +150 -0
- data/vendor/rlite/deps/sha1.c +227 -0
- data/vendor/rlite/deps/sha1.h +19 -0
- data/vendor/rlite/deps/utilfromredis.c +397 -0
- data/vendor/rlite/deps/utilfromredis.h +11 -0
- data/vendor/rlite/src/Makefile +79 -0
- data/vendor/rlite/src/constants.h +15 -0
- data/vendor/rlite/src/dump.c +191 -0
- data/vendor/rlite/src/dump.h +3 -0
- data/vendor/rlite/src/hirlite.c +3985 -0
- data/vendor/rlite/src/hirlite.h +186 -0
- data/vendor/rlite/src/page_btree.c +1556 -0
- data/vendor/rlite/src/page_btree.h +133 -0
- data/vendor/rlite/src/page_key.c +283 -0
- data/vendor/rlite/src/page_key.h +25 -0
- data/vendor/rlite/src/page_list.c +718 -0
- data/vendor/rlite/src/page_list.h +70 -0
- data/vendor/rlite/src/page_long.c +61 -0
- data/vendor/rlite/src/page_long.h +14 -0
- data/vendor/rlite/src/page_multi_string.c +538 -0
- data/vendor/rlite/src/page_multi_string.h +18 -0
- data/vendor/rlite/src/page_skiplist.c +689 -0
- data/vendor/rlite/src/page_skiplist.h +70 -0
- data/vendor/rlite/src/page_string.c +55 -0
- data/vendor/rlite/src/page_string.h +12 -0
- data/vendor/rlite/src/pqsort.c +185 -0
- data/vendor/rlite/src/pqsort.h +40 -0
- data/vendor/rlite/src/restore.c +401 -0
- data/vendor/rlite/src/restore.h +3 -0
- data/vendor/rlite/src/rlite.c +1309 -0
- data/vendor/rlite/src/rlite.h +159 -0
- data/vendor/rlite/src/sort.c +530 -0
- data/vendor/rlite/src/sort.h +18 -0
- data/vendor/rlite/src/status.h +19 -0
- data/vendor/rlite/src/type_hash.c +607 -0
- data/vendor/rlite/src/type_hash.h +29 -0
- data/vendor/rlite/src/type_list.c +477 -0
- data/vendor/rlite/src/type_list.h +23 -0
- data/vendor/rlite/src/type_set.c +796 -0
- data/vendor/rlite/src/type_set.h +34 -0
- data/vendor/rlite/src/type_string.c +613 -0
- data/vendor/rlite/src/type_string.h +34 -0
- data/vendor/rlite/src/type_zset.c +1147 -0
- data/vendor/rlite/src/type_zset.h +50 -0
- data/vendor/rlite/src/util.c +334 -0
- data/vendor/rlite/src/util.h +71 -0
- metadata +151 -0
@@ -0,0 +1,1309 @@
|
|
1
|
+
#include <sys/file.h>
|
2
|
+
#include <stdlib.h>
|
3
|
+
#include <string.h>
|
4
|
+
#include <unistd.h>
|
5
|
+
#include <errno.h>
|
6
|
+
#include "page_btree.h"
|
7
|
+
#include "page_list.h"
|
8
|
+
#include "page_long.h"
|
9
|
+
#include "page_string.h"
|
10
|
+
#include "page_skiplist.h"
|
11
|
+
#include "page_multi_string.h"
|
12
|
+
#include "type_string.h"
|
13
|
+
#include "type_zset.h"
|
14
|
+
#include "type_hash.h"
|
15
|
+
#include "rlite.h"
|
16
|
+
#include "util.h"
|
17
|
+
#include "../deps/sha1.h"
|
18
|
+
#ifdef DEBUG
|
19
|
+
#include <valgrind/valgrind.h>
|
20
|
+
#endif
|
21
|
+
|
22
|
+
#define DEFAULT_READ_PAGES_LEN 16
|
23
|
+
#define DEFAULT_WRITE_PAGES_LEN 8
|
24
|
+
#define DEFAULT_PAGE_SIZE 1024
|
25
|
+
#define HEADER_SIZE 100
|
26
|
+
|
27
|
+
int rl_header_serialize(struct rlite *db, void *obj, unsigned char *data);
|
28
|
+
int rl_header_deserialize(struct rlite *db, void **obj, void *context, unsigned char *data);
|
29
|
+
int rl_has_flag(rlite *db, int flag);
|
30
|
+
|
31
|
+
rl_data_type rl_data_type_btree_hash_sha1_long = {
|
32
|
+
"rl_data_type_btree_hash_sha1_long",
|
33
|
+
rl_btree_serialize,
|
34
|
+
rl_btree_deserialize,
|
35
|
+
rl_btree_destroy,
|
36
|
+
};
|
37
|
+
rl_data_type rl_data_type_btree_node_hash_sha1_long = {
|
38
|
+
"rl_data_type_btree_node_hash_sha1_long",
|
39
|
+
rl_btree_node_serialize_hash_sha1_long,
|
40
|
+
rl_btree_node_deserialize_hash_sha1_long,
|
41
|
+
rl_btree_node_destroy,
|
42
|
+
};
|
43
|
+
rl_data_type rl_data_type_btree_hash_sha1_double = {
|
44
|
+
"rl_data_type_btree_hash_sha1_double",
|
45
|
+
rl_btree_serialize,
|
46
|
+
rl_btree_deserialize,
|
47
|
+
rl_btree_destroy,
|
48
|
+
};
|
49
|
+
rl_data_type rl_data_type_btree_node_hash_sha1_double = {
|
50
|
+
"rl_data_type_btree_node_hash_sha1_double",
|
51
|
+
rl_btree_node_serialize_hash_sha1_double,
|
52
|
+
rl_btree_node_deserialize_hash_sha1_double,
|
53
|
+
rl_btree_node_destroy,
|
54
|
+
};
|
55
|
+
rl_data_type rl_data_type_btree_hash_sha1_key = {
|
56
|
+
"rl_data_type_btree_hash_sha1_key",
|
57
|
+
rl_btree_serialize,
|
58
|
+
rl_btree_deserialize,
|
59
|
+
rl_btree_destroy,
|
60
|
+
};
|
61
|
+
rl_data_type rl_data_type_btree_node_hash_sha1_key = {
|
62
|
+
"rl_data_type_btree_node_hash_sha1_key",
|
63
|
+
rl_btree_node_serialize_hash_sha1_key,
|
64
|
+
rl_btree_node_deserialize_hash_sha1_key,
|
65
|
+
rl_btree_node_destroy,
|
66
|
+
};
|
67
|
+
rl_data_type rl_data_type_btree_hash_sha1_hashkey = {
|
68
|
+
"rl_data_type_btree_hash_sha1_hashkey",
|
69
|
+
rl_btree_serialize,
|
70
|
+
rl_btree_deserialize,
|
71
|
+
rl_btree_destroy,
|
72
|
+
};
|
73
|
+
rl_data_type rl_data_type_btree_node_hash_sha1_hashkey = {
|
74
|
+
"rl_data_type_btree_node_hash_sha1_hashkey",
|
75
|
+
rl_btree_node_serialize_hash_sha1_hashkey,
|
76
|
+
rl_btree_node_deserialize_hash_sha1_hashkey,
|
77
|
+
rl_btree_node_destroy,
|
78
|
+
};
|
79
|
+
rl_data_type rl_data_type_header = {
|
80
|
+
"rl_data_type_header",
|
81
|
+
rl_header_serialize,
|
82
|
+
rl_header_deserialize,
|
83
|
+
NULL
|
84
|
+
};
|
85
|
+
rl_data_type rl_data_type_btree_set_long = {
|
86
|
+
"rl_data_type_btree_set_long",
|
87
|
+
rl_btree_serialize,
|
88
|
+
rl_btree_deserialize,
|
89
|
+
rl_btree_destroy,
|
90
|
+
};
|
91
|
+
rl_data_type rl_data_type_btree_node_set_long = {
|
92
|
+
"rl_data_type_btree_node_set_long",
|
93
|
+
rl_btree_node_serialize_set_long,
|
94
|
+
rl_btree_node_deserialize_set_long,
|
95
|
+
rl_btree_node_destroy,
|
96
|
+
};
|
97
|
+
rl_data_type rl_data_type_btree_hash_long_long = {
|
98
|
+
"rl_data_type_btree_hash_long_long",
|
99
|
+
rl_btree_serialize,
|
100
|
+
rl_btree_deserialize,
|
101
|
+
rl_btree_destroy,
|
102
|
+
};
|
103
|
+
rl_data_type rl_data_type_btree_node_hash_long_long = {
|
104
|
+
"rl_data_type_btree_node_hash_long_long",
|
105
|
+
rl_btree_node_serialize_hash_long_long,
|
106
|
+
rl_btree_node_deserialize_hash_long_long,
|
107
|
+
rl_btree_node_destroy,
|
108
|
+
};
|
109
|
+
rl_data_type rl_data_type_btree_hash_double_long = {
|
110
|
+
"rl_data_type_btree_hash_double_long",
|
111
|
+
rl_btree_serialize,
|
112
|
+
rl_btree_deserialize,
|
113
|
+
rl_btree_destroy,
|
114
|
+
};
|
115
|
+
rl_data_type rl_data_type_list_long = {
|
116
|
+
"rl_data_type_list_long",
|
117
|
+
rl_list_serialize,
|
118
|
+
rl_list_deserialize,
|
119
|
+
rl_list_destroy,
|
120
|
+
};
|
121
|
+
rl_data_type rl_data_type_list_node_long = {
|
122
|
+
"rl_data_type_list_node_long",
|
123
|
+
rl_list_node_serialize_long,
|
124
|
+
rl_list_node_deserialize_long,
|
125
|
+
rl_list_node_destroy,
|
126
|
+
};
|
127
|
+
rl_data_type rl_data_type_string = {
|
128
|
+
"rl_data_type_string",
|
129
|
+
rl_string_serialize,
|
130
|
+
rl_string_deserialize,
|
131
|
+
rl_string_destroy,
|
132
|
+
};
|
133
|
+
|
134
|
+
rl_data_type rl_data_type_skiplist = {
|
135
|
+
"rl_data_type_skiplist",
|
136
|
+
rl_skiplist_serialize,
|
137
|
+
rl_skiplist_deserialize,
|
138
|
+
rl_skiplist_destroy,
|
139
|
+
};
|
140
|
+
|
141
|
+
rl_data_type rl_data_type_skiplist_node = {
|
142
|
+
"rl_data_type_skiplist_node",
|
143
|
+
rl_skiplist_node_serialize,
|
144
|
+
rl_skiplist_node_deserialize,
|
145
|
+
rl_skiplist_node_destroy,
|
146
|
+
};
|
147
|
+
rl_data_type rl_data_type_long = {
|
148
|
+
"rl_data_type_long",
|
149
|
+
rl_long_serialize,
|
150
|
+
rl_long_deserialize,
|
151
|
+
rl_long_destroy,
|
152
|
+
};
|
153
|
+
|
154
|
+
rl_data_type rl_data_type_skiplist_node;
|
155
|
+
|
156
|
+
static const unsigned char *identifier = (unsigned char *)"rlite0.0";
|
157
|
+
|
158
|
+
static int file_driver_fp(rlite *db, int readonly)
|
159
|
+
{
|
160
|
+
int retval = RL_OK;
|
161
|
+
rl_file_driver *driver = db->driver;
|
162
|
+
if (driver->fp == NULL) {
|
163
|
+
if (!readonly && (driver->mode & RLITE_OPEN_READWRITE) == 0) {
|
164
|
+
fprintf(stderr, "Trying to write but open as readonly\n");
|
165
|
+
retval = RL_INVALID_PARAMETERS;
|
166
|
+
goto cleanup;
|
167
|
+
}
|
168
|
+
char *mode;
|
169
|
+
if (access(driver->filename, F_OK) == 0) {
|
170
|
+
if (readonly) {
|
171
|
+
mode = "r";
|
172
|
+
}
|
173
|
+
else {
|
174
|
+
mode = "r+";
|
175
|
+
}
|
176
|
+
}
|
177
|
+
else {
|
178
|
+
if ((driver->mode & RLITE_OPEN_READWRITE) == 0) {
|
179
|
+
fprintf(stderr, "Opening unexisting file in readonly mode\n");
|
180
|
+
retval = RL_INVALID_PARAMETERS;
|
181
|
+
goto cleanup;
|
182
|
+
}
|
183
|
+
mode = "w+";
|
184
|
+
}
|
185
|
+
driver->fp = fopen(driver->filename, mode);
|
186
|
+
if (driver->fp == NULL) {
|
187
|
+
fprintf(stderr, "Cannot open file %s, errno %d, mode %s\n", driver->filename, errno, mode);
|
188
|
+
perror(NULL);
|
189
|
+
retval = RL_UNEXPECTED;
|
190
|
+
goto cleanup;
|
191
|
+
}
|
192
|
+
}
|
193
|
+
cleanup:
|
194
|
+
return retval;
|
195
|
+
}
|
196
|
+
|
197
|
+
int rl_header_serialize(struct rlite *db, void *UNUSED(obj), unsigned char *data)
|
198
|
+
{
|
199
|
+
int identifier_len = strlen((char *)identifier);
|
200
|
+
memcpy(data, identifier, identifier_len);
|
201
|
+
put_4bytes(&data[identifier_len], db->page_size);
|
202
|
+
put_4bytes(&data[identifier_len + 4], db->next_empty_page);
|
203
|
+
put_4bytes(&data[identifier_len + 8], db->number_of_pages);
|
204
|
+
put_4bytes(&data[identifier_len + 12], db->number_of_databases);
|
205
|
+
long i, pos = identifier_len + 16;
|
206
|
+
for (i = 0; i < db->number_of_databases; i++) {
|
207
|
+
put_4bytes(&data[pos], db->databases[i]);
|
208
|
+
pos += 4;
|
209
|
+
}
|
210
|
+
return RL_OK;
|
211
|
+
}
|
212
|
+
|
213
|
+
int rl_header_deserialize(struct rlite *db, void **UNUSED(obj), void *UNUSED(context), unsigned char *data)
|
214
|
+
{
|
215
|
+
int retval = RL_OK;
|
216
|
+
int identifier_len = strlen((char *)identifier);
|
217
|
+
if (memcmp(data, identifier, identifier_len) != 0) {
|
218
|
+
fprintf(stderr, "Unexpected header, expecting %s\n", identifier);
|
219
|
+
return RL_INVALID_STATE;
|
220
|
+
}
|
221
|
+
db->page_size = get_4bytes(&data[identifier_len]);
|
222
|
+
db->next_empty_page = get_4bytes(&data[identifier_len + 4]);
|
223
|
+
db->initial_number_of_pages =
|
224
|
+
db->number_of_pages = get_4bytes(&data[identifier_len + 8]);
|
225
|
+
db->initial_number_of_databases =
|
226
|
+
db->number_of_databases = get_4bytes(&data[identifier_len + 12]);
|
227
|
+
RL_MALLOC(db->databases, sizeof(long) * db->number_of_databases);
|
228
|
+
RL_MALLOC(db->initial_databases, sizeof(long) * db->number_of_databases);
|
229
|
+
|
230
|
+
long i, pos = identifier_len + 16;
|
231
|
+
for (i = 0; i < db->number_of_databases; i++) {
|
232
|
+
db->initial_databases[i] =
|
233
|
+
db->databases[i] = get_4bytes(&data[pos]);
|
234
|
+
pos += 4;
|
235
|
+
}
|
236
|
+
cleanup:
|
237
|
+
return retval;
|
238
|
+
}
|
239
|
+
|
240
|
+
static int rl_ensure_pages(rlite *db)
|
241
|
+
{
|
242
|
+
int retval = RL_OK;
|
243
|
+
void *tmp;
|
244
|
+
if (rl_has_flag(db, RLITE_OPEN_READWRITE)) {
|
245
|
+
if (db->write_pages_len == db->write_pages_alloc) {
|
246
|
+
RL_REALLOC(db->write_pages, sizeof(rl_page *) * db->write_pages_alloc * 2)
|
247
|
+
db->write_pages_alloc *= 2;
|
248
|
+
}
|
249
|
+
}
|
250
|
+
if (db->read_pages_len == db->read_pages_alloc) {
|
251
|
+
RL_REALLOC(db->read_pages, sizeof(rl_page *) * db->read_pages_alloc * 2)
|
252
|
+
db->read_pages_alloc *= 2;
|
253
|
+
}
|
254
|
+
cleanup:
|
255
|
+
return retval;
|
256
|
+
}
|
257
|
+
|
258
|
+
int rl_open(const char *filename, rlite **_db, int flags)
|
259
|
+
{
|
260
|
+
rl_btree_init();
|
261
|
+
rl_list_init();
|
262
|
+
int retval = RL_OK;
|
263
|
+
rlite *db;
|
264
|
+
RL_MALLOC(db, sizeof(*db));
|
265
|
+
|
266
|
+
db->selected_database = 0;
|
267
|
+
db->page_size = DEFAULT_PAGE_SIZE;
|
268
|
+
db->read_pages = db->write_pages = NULL;
|
269
|
+
db->read_pages_alloc = db->read_pages_len = db->write_pages_len = db->write_pages_alloc = 0;
|
270
|
+
|
271
|
+
RL_MALLOC(db->read_pages, sizeof(rl_page *) * DEFAULT_READ_PAGES_LEN)
|
272
|
+
db->read_pages_len = 0;
|
273
|
+
db->read_pages_alloc = DEFAULT_READ_PAGES_LEN;
|
274
|
+
db->write_pages_len = 0;
|
275
|
+
if ((flags & RLITE_OPEN_READWRITE) > 0) {
|
276
|
+
db->write_pages_alloc = DEFAULT_WRITE_PAGES_LEN;
|
277
|
+
RL_MALLOC(db->write_pages, sizeof(rl_page *) * DEFAULT_WRITE_PAGES_LEN);
|
278
|
+
}
|
279
|
+
else {
|
280
|
+
db->write_pages_alloc = 0;
|
281
|
+
}
|
282
|
+
|
283
|
+
if (memcmp(filename, ":memory:", 9) == 0) {
|
284
|
+
rl_memory_driver *driver;
|
285
|
+
RL_MALLOC(driver, sizeof(*driver));
|
286
|
+
db->driver_type = RL_MEMORY_DRIVER;
|
287
|
+
db->driver = driver;
|
288
|
+
driver->data = NULL;
|
289
|
+
driver->datalen = 0;
|
290
|
+
}
|
291
|
+
else {
|
292
|
+
if ((flags & RLITE_OPEN_CREATE) == 0) {
|
293
|
+
int _access_flags;
|
294
|
+
if ((flags & RLITE_OPEN_READWRITE) != 0) {
|
295
|
+
_access_flags = W_OK;
|
296
|
+
}
|
297
|
+
else {
|
298
|
+
_access_flags = R_OK | F_OK;
|
299
|
+
}
|
300
|
+
if (access(filename, _access_flags) != 0) {
|
301
|
+
retval = RL_INVALID_PARAMETERS;
|
302
|
+
goto cleanup;
|
303
|
+
}
|
304
|
+
}
|
305
|
+
|
306
|
+
rl_file_driver *driver;
|
307
|
+
RL_MALLOC(driver, sizeof(*driver));
|
308
|
+
driver->fp = NULL;
|
309
|
+
driver->filename = rl_malloc(sizeof(char) * (strlen(filename) + 1));
|
310
|
+
if (!driver->filename) {
|
311
|
+
rl_free(driver);
|
312
|
+
retval = RL_OUT_OF_MEMORY;
|
313
|
+
goto cleanup;
|
314
|
+
}
|
315
|
+
strcpy(driver->filename, filename);
|
316
|
+
driver->mode = flags;
|
317
|
+
db->driver = driver;
|
318
|
+
db->driver_type = RL_FILE_DRIVER;
|
319
|
+
}
|
320
|
+
|
321
|
+
RL_CALL(rl_read_header, RL_OK, db);
|
322
|
+
|
323
|
+
*_db = db;
|
324
|
+
cleanup:
|
325
|
+
if (retval != RL_OK && db != NULL) {
|
326
|
+
rl_close(db);
|
327
|
+
}
|
328
|
+
return retval;
|
329
|
+
}
|
330
|
+
|
331
|
+
int rl_close(rlite *db)
|
332
|
+
{
|
333
|
+
if (!db) {
|
334
|
+
return RL_OK;
|
335
|
+
}
|
336
|
+
if (db->driver_type == RL_FILE_DRIVER) {
|
337
|
+
rl_file_driver *driver = db->driver;
|
338
|
+
if (driver->fp) {
|
339
|
+
fclose(driver->fp);
|
340
|
+
}
|
341
|
+
rl_free(driver->filename);
|
342
|
+
rl_free(db->driver);
|
343
|
+
}
|
344
|
+
else if (db->driver_type == RL_MEMORY_DRIVER) {
|
345
|
+
rl_memory_driver* driver = db->driver;
|
346
|
+
rl_free(driver->data);
|
347
|
+
rl_free(driver);
|
348
|
+
}
|
349
|
+
rl_discard(db);
|
350
|
+
rl_free(db->read_pages);
|
351
|
+
rl_free(db->write_pages);
|
352
|
+
rl_free(db->databases);
|
353
|
+
rl_free(db->initial_databases);
|
354
|
+
rl_free(db);
|
355
|
+
return RL_OK;
|
356
|
+
}
|
357
|
+
|
358
|
+
int rl_has_flag(rlite *db, int flag)
|
359
|
+
{
|
360
|
+
if (db->driver_type == RL_FILE_DRIVER) {
|
361
|
+
return (((rl_file_driver *)db->driver)->mode & flag) > 0;
|
362
|
+
}
|
363
|
+
else if (db->driver_type == RL_MEMORY_DRIVER) {
|
364
|
+
return 1;
|
365
|
+
}
|
366
|
+
fprintf(stderr, "Unknown driver_type %d\n", db->driver_type);
|
367
|
+
return RL_UNEXPECTED;
|
368
|
+
}
|
369
|
+
|
370
|
+
int rl_create_db(rlite *db)
|
371
|
+
{
|
372
|
+
int retval, i;
|
373
|
+
RL_CALL(rl_ensure_pages, RL_OK, db);
|
374
|
+
db->next_empty_page = 1;
|
375
|
+
db->initial_number_of_pages =
|
376
|
+
db->number_of_pages = 1;
|
377
|
+
db->selected_database = 0;
|
378
|
+
db->initial_number_of_databases =
|
379
|
+
db->number_of_databases = 16;
|
380
|
+
RL_MALLOC(db->databases, sizeof(long) * db->number_of_databases);
|
381
|
+
RL_MALLOC(db->initial_databases, sizeof(long) * db->number_of_databases);
|
382
|
+
for (i = 0; i < db->number_of_databases; i++) {
|
383
|
+
db->initial_databases[i] =
|
384
|
+
db->databases[i] = 0;
|
385
|
+
}
|
386
|
+
cleanup:
|
387
|
+
return retval;
|
388
|
+
}
|
389
|
+
|
390
|
+
int rl_get_key_btree(rlite *db, rl_btree **retbtree, int create)
|
391
|
+
{
|
392
|
+
void *_btree;
|
393
|
+
int retval;
|
394
|
+
if (!db->databases[db->selected_database]) {
|
395
|
+
if (!create) {
|
396
|
+
return RL_NOT_FOUND;
|
397
|
+
}
|
398
|
+
rl_btree *btree;
|
399
|
+
RL_CALL(rl_btree_create, RL_OK, db, &btree, &rl_btree_type_hash_sha1_key);
|
400
|
+
db->databases[db->selected_database] = db->next_empty_page;
|
401
|
+
RL_CALL(rl_write, RL_OK, db, &rl_data_type_btree_hash_sha1_key, db->databases[db->selected_database], btree);
|
402
|
+
}
|
403
|
+
RL_CALL(rl_read, RL_FOUND, db, &rl_data_type_btree_hash_sha1_key, db->databases[db->selected_database], &rl_btree_type_hash_sha1_key, &_btree, 1);
|
404
|
+
*retbtree = _btree;
|
405
|
+
retval = RL_OK;
|
406
|
+
cleanup:
|
407
|
+
return retval;
|
408
|
+
}
|
409
|
+
|
410
|
+
int rl_read_header(rlite *db)
|
411
|
+
{
|
412
|
+
db->page_size = HEADER_SIZE;
|
413
|
+
int retval;
|
414
|
+
if (db->driver_type == RL_MEMORY_DRIVER) {
|
415
|
+
db->page_size = DEFAULT_PAGE_SIZE;
|
416
|
+
RL_CALL(rl_create_db, RL_OK, db);
|
417
|
+
}
|
418
|
+
else {
|
419
|
+
retval = rl_read(db, &rl_data_type_header, 0, NULL, NULL, 1);
|
420
|
+
if (retval == RL_NOT_FOUND && rl_has_flag(db, RLITE_OPEN_CREATE)) {
|
421
|
+
db->page_size = DEFAULT_PAGE_SIZE;
|
422
|
+
RL_CALL(rl_create_db, RL_OK, db);
|
423
|
+
RL_CALL(rl_write, RL_OK, db, &rl_data_type_header, 0, NULL);
|
424
|
+
}
|
425
|
+
else if (retval != RL_FOUND) {
|
426
|
+
goto cleanup;
|
427
|
+
}
|
428
|
+
}
|
429
|
+
retval = RL_OK;
|
430
|
+
cleanup:
|
431
|
+
return retval;
|
432
|
+
}
|
433
|
+
|
434
|
+
#ifdef DEBUG
|
435
|
+
void print_cache(rlite *db)
|
436
|
+
{
|
437
|
+
printf("Cache read pages:");
|
438
|
+
long i;
|
439
|
+
rl_page *page;
|
440
|
+
for (i = 0; i < db->read_pages_len; i++) {
|
441
|
+
page = db->read_pages[i];
|
442
|
+
printf("%ld, ", page->page_number);
|
443
|
+
}
|
444
|
+
printf("\nCache write pages:");
|
445
|
+
for (i = 0; i < db->write_pages_len; i++) {
|
446
|
+
page = db->write_pages[i];
|
447
|
+
printf("%ld, ", page->page_number);
|
448
|
+
}
|
449
|
+
printf("\n");
|
450
|
+
}
|
451
|
+
#endif
|
452
|
+
|
453
|
+
#ifdef DEBUG
|
454
|
+
static int rl_search_cache(rlite *db, rl_data_type *type, long page_number, void **obj, long *position, rl_page **pages, long page_len)
|
455
|
+
#else
|
456
|
+
static int rl_search_cache(rlite *UNUSED(db), rl_data_type *UNUSED(type), long page_number, void **obj, long *position, rl_page **pages, long page_len)
|
457
|
+
#endif
|
458
|
+
{
|
459
|
+
long pos, min = 0, max = page_len - 1;
|
460
|
+
rl_page *page;
|
461
|
+
if (max >= 0) {
|
462
|
+
do {
|
463
|
+
pos = min + (max - min) / 2;
|
464
|
+
page = pages[pos];
|
465
|
+
if (page->page_number == page_number) {
|
466
|
+
#ifdef DEBUG
|
467
|
+
if (page->type != &rl_data_type_long && type != &rl_data_type_long && type != NULL && page->type != type) {
|
468
|
+
fprintf(stderr, "Type of page in cache (%s) doesn't match the asked one (%s)\n", page->type->name, type->name);
|
469
|
+
return RL_UNEXPECTED;
|
470
|
+
}
|
471
|
+
#endif
|
472
|
+
if (obj) {
|
473
|
+
*obj = page->obj;
|
474
|
+
}
|
475
|
+
if (position) {
|
476
|
+
*position = pos;
|
477
|
+
}
|
478
|
+
return RL_FOUND;
|
479
|
+
}
|
480
|
+
else if (page->page_number > page_number) {
|
481
|
+
max = pos - 1;
|
482
|
+
}
|
483
|
+
else {
|
484
|
+
min = pos + 1;
|
485
|
+
}
|
486
|
+
}
|
487
|
+
while (max >= min);
|
488
|
+
if (position) {
|
489
|
+
if (pages[pos]->page_number > page_number && pos > 0) {
|
490
|
+
pos--;
|
491
|
+
}
|
492
|
+
if (pages[pos]->page_number < page_number && pos < page_len) {
|
493
|
+
pos++;
|
494
|
+
}
|
495
|
+
#ifdef DEBUG
|
496
|
+
long i, prev;
|
497
|
+
rl_page *p;
|
498
|
+
for (i = 0; i < db->read_pages_len; i++) {
|
499
|
+
p = db->read_pages[i];
|
500
|
+
if (i > 0) {
|
501
|
+
if (prev >= p->page_number) {
|
502
|
+
fprintf(stderr, "Reading cache is broken\n");
|
503
|
+
print_cache(db);
|
504
|
+
return RL_UNEXPECTED;
|
505
|
+
}
|
506
|
+
}
|
507
|
+
prev = p->page_number;
|
508
|
+
}
|
509
|
+
for (i = 0; i < db->write_pages_len; i++) {
|
510
|
+
p = db->write_pages[i];
|
511
|
+
if (i > 0) {
|
512
|
+
if (prev >= p->page_number) {
|
513
|
+
fprintf(stderr, "Writing cache is broken\n");
|
514
|
+
print_cache(db);
|
515
|
+
return RL_UNEXPECTED;
|
516
|
+
}
|
517
|
+
}
|
518
|
+
prev = p->page_number;
|
519
|
+
}
|
520
|
+
#endif
|
521
|
+
*position = pos;
|
522
|
+
}
|
523
|
+
}
|
524
|
+
else {
|
525
|
+
if (position) {
|
526
|
+
*position = 0;
|
527
|
+
}
|
528
|
+
}
|
529
|
+
return RL_NOT_FOUND;
|
530
|
+
}
|
531
|
+
|
532
|
+
int rl_read_from_cache(rlite *db, rl_data_type *type, long page_number, void **obj)
|
533
|
+
{
|
534
|
+
int retval = rl_search_cache(db, type, page_number, obj, NULL, db->write_pages, db->write_pages_len);
|
535
|
+
if (retval == RL_NOT_FOUND) {
|
536
|
+
retval = rl_search_cache(db, type, page_number, obj, NULL, db->read_pages, db->read_pages_len);
|
537
|
+
}
|
538
|
+
return retval;
|
539
|
+
}
|
540
|
+
|
541
|
+
int rl_read(rlite *db, rl_data_type *type, long page, void *context, void **obj, int cache)
|
542
|
+
{
|
543
|
+
// fprintf(stderr, "r %ld %s\n", page, type->name);
|
544
|
+
#ifdef DEBUG
|
545
|
+
int keep = 0;
|
546
|
+
long initial_page_size = db->page_size;
|
547
|
+
if (page == 0 && type != &rl_data_type_header) {
|
548
|
+
VALGRIND_PRINTF_BACKTRACE("Unexpected");
|
549
|
+
return RL_UNEXPECTED;
|
550
|
+
}
|
551
|
+
#endif
|
552
|
+
unsigned char *data = NULL;
|
553
|
+
int retval;
|
554
|
+
unsigned char *serialize_data;
|
555
|
+
retval = rl_read_from_cache(db, type, page, obj);
|
556
|
+
if (retval != RL_NOT_FOUND) {
|
557
|
+
if (!cache) {
|
558
|
+
RL_MALLOC(serialize_data, db->page_size * sizeof(unsigned char));
|
559
|
+
retval = type->serialize(db, *obj, serialize_data);
|
560
|
+
if (retval != RL_OK) {
|
561
|
+
rl_free(serialize_data);
|
562
|
+
return retval;
|
563
|
+
}
|
564
|
+
retval = type->deserialize(db, obj, context, serialize_data);
|
565
|
+
rl_free(serialize_data);
|
566
|
+
if (retval != RL_OK) {
|
567
|
+
return retval;
|
568
|
+
}
|
569
|
+
retval = RL_FOUND;
|
570
|
+
}
|
571
|
+
return retval;
|
572
|
+
}
|
573
|
+
RL_MALLOC(data, db->page_size * sizeof(unsigned char));
|
574
|
+
if (db->driver_type == RL_FILE_DRIVER) {
|
575
|
+
RL_CALL(file_driver_fp, RL_OK, db, 1);
|
576
|
+
rl_file_driver *driver = db->driver;
|
577
|
+
fseek(driver->fp, page * db->page_size, SEEK_SET);
|
578
|
+
size_t read = fread(data, sizeof(unsigned char), db->page_size, driver->fp);
|
579
|
+
if (read != (size_t)db->page_size) {
|
580
|
+
if (page > 0) {
|
581
|
+
#ifdef DEBUG
|
582
|
+
print_cache(db);
|
583
|
+
#endif
|
584
|
+
fprintf(stderr, "Unable to read page %ld on line %d\n", page, __LINE__);
|
585
|
+
perror(NULL);
|
586
|
+
}
|
587
|
+
retval = RL_NOT_FOUND;
|
588
|
+
goto cleanup;
|
589
|
+
}
|
590
|
+
}
|
591
|
+
else if (db->driver_type == RL_MEMORY_DRIVER) {
|
592
|
+
rl_memory_driver *driver = db->driver;
|
593
|
+
if ((page + 1) * db->page_size > driver->datalen) {
|
594
|
+
fprintf(stderr, "Unable to read page %ld on line %d\n", page, __LINE__);
|
595
|
+
retval = RL_NOT_FOUND;
|
596
|
+
goto cleanup;
|
597
|
+
}
|
598
|
+
memcpy(data, &driver->data[page * db->page_size], sizeof(unsigned char) * db->page_size);
|
599
|
+
}
|
600
|
+
else {
|
601
|
+
fprintf(stderr, "Unexpected driver %d when asking for page %ld\n", db->driver_type, page);
|
602
|
+
retval = RL_UNEXPECTED;
|
603
|
+
goto cleanup;
|
604
|
+
}
|
605
|
+
|
606
|
+
long pos;
|
607
|
+
retval = rl_search_cache(db, type, page, NULL, &pos, db->read_pages, db->read_pages_len);
|
608
|
+
if (retval != RL_NOT_FOUND) {
|
609
|
+
fprintf(stderr, "Unexpectedly found page in cache\n");
|
610
|
+
retval = RL_UNEXPECTED;
|
611
|
+
goto cleanup;
|
612
|
+
}
|
613
|
+
|
614
|
+
retval = type->deserialize(db, obj, context ? context : type, data);
|
615
|
+
if (retval != RL_OK) {
|
616
|
+
goto cleanup;
|
617
|
+
}
|
618
|
+
|
619
|
+
if (cache) {
|
620
|
+
rl_ensure_pages(db);
|
621
|
+
rl_page *page_obj;
|
622
|
+
page_obj = rl_malloc(sizeof(*page_obj));
|
623
|
+
if (!page_obj) {
|
624
|
+
if (obj) {
|
625
|
+
if (type->destroy && *obj) {
|
626
|
+
type->destroy(db, *obj);
|
627
|
+
}
|
628
|
+
*obj = NULL;
|
629
|
+
}
|
630
|
+
retval = RL_OUT_OF_MEMORY;
|
631
|
+
goto cleanup;
|
632
|
+
}
|
633
|
+
page_obj->page_number = page;
|
634
|
+
page_obj->type = type;
|
635
|
+
page_obj->obj = obj ? *obj : NULL;
|
636
|
+
#ifdef DEBUG
|
637
|
+
keep = 1;
|
638
|
+
if (initial_page_size != db->page_size) {
|
639
|
+
page_obj->serialized_data = realloc(data, db->page_size * sizeof(unsigned char));
|
640
|
+
if (page_obj->serialized_data == NULL) {
|
641
|
+
fprintf(stderr, "realloc failed\n");
|
642
|
+
retval = RL_OUT_OF_MEMORY;
|
643
|
+
goto cleanup;
|
644
|
+
}
|
645
|
+
data = page_obj->serialized_data;
|
646
|
+
if (db->page_size > initial_page_size) {
|
647
|
+
memset(&data[initial_page_size], 0, db->page_size - initial_page_size);
|
648
|
+
}
|
649
|
+
}
|
650
|
+
else {
|
651
|
+
page_obj->serialized_data = data;
|
652
|
+
}
|
653
|
+
|
654
|
+
serialize_data = calloc(db->page_size, sizeof(unsigned char));
|
655
|
+
retval = type->serialize(db, obj ? *obj : NULL, serialize_data);
|
656
|
+
if (retval != RL_OK) {
|
657
|
+
goto cleanup;
|
658
|
+
}
|
659
|
+
if (memcmp(data, serialize_data, db->page_size) != 0) {
|
660
|
+
fprintf(stderr, "serialize unserialized data mismatch\n");
|
661
|
+
long i;
|
662
|
+
for (i = 0; i < db->page_size; i++) {
|
663
|
+
if (serialize_data[i] != data[i]) {
|
664
|
+
fprintf(stderr, "at position %ld expected %d, got %d\n", i, serialize_data[i], data[i]);
|
665
|
+
}
|
666
|
+
}
|
667
|
+
}
|
668
|
+
rl_free(serialize_data);
|
669
|
+
#endif
|
670
|
+
if (pos < db->read_pages_len) {
|
671
|
+
memmove(&db->read_pages[pos + 1], &db->read_pages[pos], sizeof(rl_page *) * (db->read_pages_len - pos));
|
672
|
+
}
|
673
|
+
db->read_pages[pos] = page_obj;
|
674
|
+
db->read_pages_len++;
|
675
|
+
}
|
676
|
+
if (retval == RL_OK) {
|
677
|
+
retval = RL_FOUND;
|
678
|
+
}
|
679
|
+
cleanup:
|
680
|
+
#ifdef DEBUG
|
681
|
+
if (!keep) {
|
682
|
+
rl_free(data);
|
683
|
+
}
|
684
|
+
#endif
|
685
|
+
#ifndef DEBUG
|
686
|
+
rl_free(data);
|
687
|
+
#endif
|
688
|
+
return retval;
|
689
|
+
}
|
690
|
+
|
691
|
+
int rl_alloc_page_number(rlite *db, long *_page_number)
|
692
|
+
{
|
693
|
+
int retval = RL_OK;
|
694
|
+
long page_number = db->next_empty_page;
|
695
|
+
if (page_number == db->number_of_pages) {
|
696
|
+
db->next_empty_page++;
|
697
|
+
db->number_of_pages++;
|
698
|
+
}
|
699
|
+
else {
|
700
|
+
RL_CALL(rl_long_get, RL_OK, db, &db->next_empty_page, db->next_empty_page);
|
701
|
+
}
|
702
|
+
if (_page_number) {
|
703
|
+
*_page_number = page_number;
|
704
|
+
}
|
705
|
+
cleanup:
|
706
|
+
return retval;
|
707
|
+
}
|
708
|
+
|
709
|
+
int rl_write(struct rlite *db, rl_data_type *type, long page_number, void *obj)
|
710
|
+
{
|
711
|
+
// fprintf(stderr, "w %ld %s\n", page_number, type->name);
|
712
|
+
rl_page *page = NULL;
|
713
|
+
long pos;
|
714
|
+
int retval;
|
715
|
+
|
716
|
+
if (page_number == db->next_empty_page) {
|
717
|
+
RL_CALL(rl_alloc_page_number, RL_OK, db, NULL);
|
718
|
+
retval = rl_write(db, &rl_data_type_header, 0, NULL);
|
719
|
+
if (retval != RL_OK) {
|
720
|
+
goto cleanup;
|
721
|
+
}
|
722
|
+
}
|
723
|
+
|
724
|
+
retval = rl_search_cache(db, type, page_number, NULL, &pos, db->write_pages, db->write_pages_len);
|
725
|
+
if (retval == RL_FOUND) {
|
726
|
+
if (obj != db->write_pages[pos]->obj) {
|
727
|
+
if (db->write_pages[pos]->obj) {
|
728
|
+
db->write_pages[pos]->type->destroy(db, db->write_pages[pos]->obj);
|
729
|
+
}
|
730
|
+
db->write_pages[pos]->obj = obj;
|
731
|
+
db->write_pages[pos]->type = type;
|
732
|
+
}
|
733
|
+
retval = RL_OK;
|
734
|
+
}
|
735
|
+
else if (retval == RL_NOT_FOUND) {
|
736
|
+
rl_ensure_pages(db);
|
737
|
+
RL_MALLOC(page, sizeof(*page));
|
738
|
+
#ifdef DEBUG
|
739
|
+
page->serialized_data = NULL;
|
740
|
+
#endif
|
741
|
+
page->page_number = page_number;
|
742
|
+
page->type = type;
|
743
|
+
page->obj = obj;
|
744
|
+
if (pos < db->write_pages_len) {
|
745
|
+
memmove(&db->write_pages[pos + 1], &db->write_pages[pos], sizeof(rl_page *) * (db->write_pages_len - pos));
|
746
|
+
}
|
747
|
+
db->write_pages[pos] = page;
|
748
|
+
db->write_pages_len++;
|
749
|
+
|
750
|
+
retval = rl_search_cache(db, type, page_number, NULL, &pos, db->read_pages, db->read_pages_len);
|
751
|
+
if (retval == RL_FOUND) {
|
752
|
+
#ifdef DEBUG
|
753
|
+
rl_free(db->read_pages[pos]->serialized_data);
|
754
|
+
#endif
|
755
|
+
if (db->read_pages[pos]->obj != obj) {
|
756
|
+
db->read_pages[pos]->type->destroy(db, db->read_pages[pos]->obj);
|
757
|
+
}
|
758
|
+
rl_free(db->read_pages[pos]);
|
759
|
+
memmove(&db->read_pages[pos], &db->read_pages[pos + 1], sizeof(rl_page *) * (db->read_pages_len - pos));
|
760
|
+
db->read_pages_len--;
|
761
|
+
retval = RL_OK;
|
762
|
+
}
|
763
|
+
else if (retval != RL_NOT_FOUND) {
|
764
|
+
goto cleanup;
|
765
|
+
}
|
766
|
+
retval = RL_OK;
|
767
|
+
}
|
768
|
+
|
769
|
+
cleanup:
|
770
|
+
if (retval != RL_OK) {
|
771
|
+
if (obj) {
|
772
|
+
type->destroy(db, obj);
|
773
|
+
rl_purge_cache(db, page_number);
|
774
|
+
}
|
775
|
+
if (page_number != 0) {
|
776
|
+
rl_discard(db);
|
777
|
+
}
|
778
|
+
}
|
779
|
+
return retval;
|
780
|
+
}
|
781
|
+
|
782
|
+
int rl_purge_cache(struct rlite *db, long page_number)
|
783
|
+
{
|
784
|
+
long pos;
|
785
|
+
int retval;
|
786
|
+
retval = rl_search_cache(db, NULL, page_number, NULL, &pos, db->write_pages, db->write_pages_len);
|
787
|
+
if (retval == RL_FOUND) {
|
788
|
+
db->write_pages[pos]->obj = NULL;
|
789
|
+
}
|
790
|
+
else if (retval != RL_NOT_FOUND) {
|
791
|
+
goto cleanup;
|
792
|
+
}
|
793
|
+
retval = rl_search_cache(db, NULL, page_number, NULL, &pos, db->read_pages, db->read_pages_len);
|
794
|
+
if (retval == RL_FOUND) {
|
795
|
+
db->read_pages[pos]->obj = NULL;
|
796
|
+
}
|
797
|
+
else if (retval != RL_NOT_FOUND) {
|
798
|
+
goto cleanup;
|
799
|
+
}
|
800
|
+
retval = RL_OK;
|
801
|
+
cleanup:
|
802
|
+
return retval;
|
803
|
+
}
|
804
|
+
|
805
|
+
int rl_delete(struct rlite *db, long page_number)
|
806
|
+
{
|
807
|
+
int retval;
|
808
|
+
RL_CALL(rl_long_set, RL_OK, db, db->next_empty_page, page_number);
|
809
|
+
db->next_empty_page = page_number;
|
810
|
+
cleanup:
|
811
|
+
return retval;
|
812
|
+
}
|
813
|
+
|
814
|
+
int rl_dirty_hash(struct rlite *db, unsigned char **hash)
|
815
|
+
{
|
816
|
+
long i;
|
817
|
+
int retval = RL_OK;
|
818
|
+
rl_page *page;
|
819
|
+
SHA1_CTX sha;
|
820
|
+
unsigned char *data = NULL;
|
821
|
+
|
822
|
+
if (db->write_pages_len == 0) {
|
823
|
+
*hash = NULL;
|
824
|
+
goto cleanup;
|
825
|
+
}
|
826
|
+
|
827
|
+
RL_MALLOC(data, db->page_size * sizeof(unsigned char));
|
828
|
+
RL_MALLOC(*hash, sizeof(unsigned char) * 20);
|
829
|
+
SHA1Init(&sha);
|
830
|
+
for (i = 0; i < db->write_pages_len; i++) {
|
831
|
+
page = db->write_pages[i];
|
832
|
+
memset(data, 0, db->page_size);
|
833
|
+
if (page->type) {
|
834
|
+
retval = page->type->serialize(db, page->obj, data);
|
835
|
+
}
|
836
|
+
SHA1Update(&sha, data, db->page_size);
|
837
|
+
}
|
838
|
+
SHA1Final(*hash, &sha);
|
839
|
+
cleanup:
|
840
|
+
rl_free(data);
|
841
|
+
if (retval != RL_OK) {
|
842
|
+
rl_free(*hash);
|
843
|
+
*hash = NULL;
|
844
|
+
}
|
845
|
+
return retval;
|
846
|
+
}
|
847
|
+
|
848
|
+
int rl_commit(struct rlite *db)
|
849
|
+
{
|
850
|
+
int retval = RL_OK;
|
851
|
+
long i, page_number;
|
852
|
+
rl_page *page;
|
853
|
+
size_t written;
|
854
|
+
unsigned char *data;
|
855
|
+
RL_MALLOC(data, db->page_size * sizeof(unsigned char));
|
856
|
+
#ifdef DEBUG
|
857
|
+
for (i = 0; i < db->read_pages_len; i++) {
|
858
|
+
page = db->read_pages[i];
|
859
|
+
memset(data, 0, db->page_size);
|
860
|
+
retval = page->type->serialize(db, page->obj, data);
|
861
|
+
if (retval != RL_OK) {
|
862
|
+
goto cleanup;
|
863
|
+
}
|
864
|
+
if (memcmp(data, page->serialized_data, db->page_size) != 0) {
|
865
|
+
fprintf(stderr, "Read page %ld (%s) has changed\n", page->page_number, page->type->name);
|
866
|
+
for (i = 0; i < db->page_size; i++) {
|
867
|
+
if (page->serialized_data[i] != data[i]) {
|
868
|
+
fprintf(stderr, "Different data in position %ld (expected %d, got %d)\n", i, page->serialized_data[i], data[i]);
|
869
|
+
}
|
870
|
+
}
|
871
|
+
retval = rl_search_cache(db, page->type, page->page_number, NULL, NULL, db->write_pages, db->write_pages_len);
|
872
|
+
if (retval == RL_FOUND) {
|
873
|
+
fprintf(stderr, "Page found in write_pages\n");
|
874
|
+
}
|
875
|
+
else {
|
876
|
+
fprintf(stderr, "Page not found in write_pages\n");
|
877
|
+
}
|
878
|
+
exit(1);
|
879
|
+
}
|
880
|
+
}
|
881
|
+
#endif
|
882
|
+
if (db->driver_type == RL_FILE_DRIVER) {
|
883
|
+
rl_file_driver *driver = db->driver;
|
884
|
+
if (driver->fp) {
|
885
|
+
fclose(driver->fp);
|
886
|
+
driver->fp = NULL;
|
887
|
+
}
|
888
|
+
RL_CALL(file_driver_fp, RL_OK, db, 0);
|
889
|
+
for (i = 0; i < db->write_pages_len; i++) {
|
890
|
+
page = db->write_pages[i];
|
891
|
+
page_number = page->page_number;
|
892
|
+
memset(data, 0, db->page_size);
|
893
|
+
if (page->type) {
|
894
|
+
retval = page->type->serialize(db, page->obj, data);
|
895
|
+
}
|
896
|
+
fseek(driver->fp, page_number * db->page_size, SEEK_SET);
|
897
|
+
written = fwrite(data, sizeof(unsigned char), db->page_size, driver->fp);
|
898
|
+
if ((size_t)db->page_size != written) {
|
899
|
+
#ifdef DEBUG
|
900
|
+
print_cache(db);
|
901
|
+
#endif
|
902
|
+
retval = RL_UNEXPECTED;
|
903
|
+
goto cleanup;
|
904
|
+
}
|
905
|
+
}
|
906
|
+
}
|
907
|
+
else if (db->driver_type == RL_MEMORY_DRIVER) {
|
908
|
+
rl_memory_driver *driver = db->driver;
|
909
|
+
if (db->write_pages_len > 0) {
|
910
|
+
page = db->write_pages[db->write_pages_len - 1];
|
911
|
+
if ((page->page_number + 1) * db->page_size > driver->datalen) {
|
912
|
+
void *tmp = realloc(driver->data, (page->page_number + 1) * db->page_size * sizeof(unsigned char));
|
913
|
+
if (!tmp) {
|
914
|
+
retval = RL_OUT_OF_MEMORY;
|
915
|
+
goto cleanup;
|
916
|
+
}
|
917
|
+
driver->data = tmp;
|
918
|
+
driver->datalen = (page->page_number + 1) * db->page_size;
|
919
|
+
}
|
920
|
+
}
|
921
|
+
for (i = 0; i < db->write_pages_len; i++) {
|
922
|
+
page = db->write_pages[i];
|
923
|
+
page_number = page->page_number;
|
924
|
+
memset(data, 0, db->page_size);
|
925
|
+
if (page->type) {
|
926
|
+
retval = page->type->serialize(db, page->obj, data);
|
927
|
+
}
|
928
|
+
memcpy(&driver->data[page_number * db->page_size], data, db->page_size);
|
929
|
+
}
|
930
|
+
}
|
931
|
+
db->initial_number_of_pages = db->number_of_pages;
|
932
|
+
db->initial_number_of_databases = db->number_of_databases;
|
933
|
+
rl_free(db->initial_databases);
|
934
|
+
RL_MALLOC(db->initial_databases, sizeof(long) * db->number_of_databases);
|
935
|
+
memcpy(db->initial_databases, db->databases, sizeof(long) * db->number_of_databases);
|
936
|
+
rl_discard(db);
|
937
|
+
rl_free(data);
|
938
|
+
cleanup:
|
939
|
+
return retval;
|
940
|
+
}
|
941
|
+
|
942
|
+
int rl_discard(struct rlite *db)
|
943
|
+
{
|
944
|
+
long i;
|
945
|
+
void *tmp;
|
946
|
+
int retval = RL_OK;
|
947
|
+
|
948
|
+
db->number_of_pages = db->initial_number_of_pages;
|
949
|
+
db->number_of_databases = db->initial_number_of_databases;
|
950
|
+
rl_free(db->databases);
|
951
|
+
RL_MALLOC(db->databases, sizeof(long) * db->number_of_databases);
|
952
|
+
memcpy(db->databases, db->initial_databases, sizeof(long) * db->number_of_databases);
|
953
|
+
rl_page *page;
|
954
|
+
for (i = 0; i < db->read_pages_len; i++) {
|
955
|
+
page = db->read_pages[i];
|
956
|
+
if (page->type->destroy && page->obj) {
|
957
|
+
page->type->destroy(db, page->obj);
|
958
|
+
}
|
959
|
+
#ifdef DEBUG
|
960
|
+
rl_free(page->serialized_data);
|
961
|
+
#endif
|
962
|
+
rl_free(page);
|
963
|
+
}
|
964
|
+
for (i = 0; i < db->write_pages_len; i++) {
|
965
|
+
page = db->write_pages[i];
|
966
|
+
if (page->type && page->type->destroy && page->obj) {
|
967
|
+
page->type->destroy(db, page->obj);
|
968
|
+
}
|
969
|
+
#ifdef DEBUG
|
970
|
+
rl_free(page->serialized_data);
|
971
|
+
#endif
|
972
|
+
rl_free(page);
|
973
|
+
}
|
974
|
+
db->read_pages_len = 0;
|
975
|
+
db->write_pages_len = 0;
|
976
|
+
|
977
|
+
if (db->read_pages_alloc != DEFAULT_READ_PAGES_LEN) {
|
978
|
+
tmp = realloc(db->read_pages, sizeof(rl_page *) * DEFAULT_READ_PAGES_LEN);
|
979
|
+
if (!tmp) {
|
980
|
+
retval = RL_OUT_OF_MEMORY;
|
981
|
+
goto cleanup;
|
982
|
+
}
|
983
|
+
db->read_pages = tmp;
|
984
|
+
db->read_pages_alloc = DEFAULT_READ_PAGES_LEN;
|
985
|
+
}
|
986
|
+
if (db->write_pages_alloc > 0) {
|
987
|
+
if (db->write_pages_alloc != DEFAULT_WRITE_PAGES_LEN) {
|
988
|
+
db->write_pages_alloc = DEFAULT_WRITE_PAGES_LEN;
|
989
|
+
tmp = realloc(db->write_pages, sizeof(rl_page *) * DEFAULT_WRITE_PAGES_LEN);
|
990
|
+
if (!tmp) {
|
991
|
+
retval = RL_OUT_OF_MEMORY;
|
992
|
+
goto cleanup;
|
993
|
+
}
|
994
|
+
db->write_pages = tmp;
|
995
|
+
}
|
996
|
+
}
|
997
|
+
cleanup:
|
998
|
+
return retval;
|
999
|
+
}
|
1000
|
+
|
1001
|
+
int rl_database_is_balanced(rlite *db, short *pages)
|
1002
|
+
{
|
1003
|
+
int retval;
|
1004
|
+
void *tmp = NULL;
|
1005
|
+
rl_key *key;
|
1006
|
+
rl_btree *btree;
|
1007
|
+
rl_btree_iterator *iterator = NULL;
|
1008
|
+
retval = rl_get_key_btree(db, &btree, 0);
|
1009
|
+
if (retval == RL_NOT_FOUND) {
|
1010
|
+
retval = RL_OK;
|
1011
|
+
goto cleanup;
|
1012
|
+
}
|
1013
|
+
else if (retval != RL_OK) {
|
1014
|
+
goto cleanup;
|
1015
|
+
}
|
1016
|
+
|
1017
|
+
RL_CALL(rl_btree_pages, RL_OK, db, btree, pages);
|
1018
|
+
RL_CALL(rl_btree_iterator_create, RL_OK, db, btree, &iterator);
|
1019
|
+
|
1020
|
+
while ((retval = rl_btree_iterator_next(iterator, NULL, &tmp)) == RL_OK) {
|
1021
|
+
key = tmp;
|
1022
|
+
pages[key->string_page] = 1;
|
1023
|
+
pages[key->value_page] = 1;
|
1024
|
+
RL_CALL(rl_multi_string_pages, RL_OK, db, key->string_page, pages);
|
1025
|
+
if (key->type == RL_TYPE_ZSET) {
|
1026
|
+
retval = rl_zset_pages(db, key->value_page, pages);
|
1027
|
+
}
|
1028
|
+
else if (key->type == RL_TYPE_HASH) {
|
1029
|
+
retval = rl_hash_pages(db, key->value_page, pages);
|
1030
|
+
}
|
1031
|
+
else if (key->type == RL_TYPE_SET) {
|
1032
|
+
retval = rl_set_pages(db, key->value_page, pages);
|
1033
|
+
}
|
1034
|
+
else if (key->type == RL_TYPE_LIST) {
|
1035
|
+
retval = rl_llist_pages(db, key->value_page, pages);
|
1036
|
+
}
|
1037
|
+
else if (key->type == RL_TYPE_STRING) {
|
1038
|
+
retval = rl_string_pages(db, key->value_page, pages);
|
1039
|
+
}
|
1040
|
+
else {
|
1041
|
+
fprintf(stderr, "Unknown type %d\n", key->type);
|
1042
|
+
goto cleanup;
|
1043
|
+
}
|
1044
|
+
if (retval != RL_OK) {
|
1045
|
+
goto cleanup;
|
1046
|
+
}
|
1047
|
+
rl_free(tmp);
|
1048
|
+
}
|
1049
|
+
tmp = NULL;
|
1050
|
+
iterator = NULL;
|
1051
|
+
|
1052
|
+
if (retval != RL_END) {
|
1053
|
+
goto cleanup;
|
1054
|
+
}
|
1055
|
+
|
1056
|
+
retval = RL_OK;
|
1057
|
+
|
1058
|
+
cleanup:
|
1059
|
+
rl_free(tmp);
|
1060
|
+
if (iterator) {
|
1061
|
+
rl_btree_iterator_destroy(iterator);
|
1062
|
+
}
|
1063
|
+
return retval;
|
1064
|
+
}
|
1065
|
+
|
1066
|
+
int rl_is_balanced(rlite *db)
|
1067
|
+
{
|
1068
|
+
int retval;
|
1069
|
+
long i, selected_database = db->selected_database;
|
1070
|
+
short *pages = NULL;
|
1071
|
+
long missing_pages = 0;
|
1072
|
+
RL_MALLOC(pages, sizeof(short) * db->number_of_pages);
|
1073
|
+
|
1074
|
+
for (i = 1; i < db->number_of_pages; i++) {
|
1075
|
+
pages[i] = 0;
|
1076
|
+
}
|
1077
|
+
|
1078
|
+
for (i = 0; i < db->number_of_databases; i++) {
|
1079
|
+
if (db->databases[i] == 0) {
|
1080
|
+
continue;
|
1081
|
+
}
|
1082
|
+
pages[db->databases[i]] = 1;
|
1083
|
+
RL_CALL(rl_select, RL_OK, db, i);
|
1084
|
+
RL_CALL(rl_database_is_balanced, RL_OK, db, pages);
|
1085
|
+
}
|
1086
|
+
|
1087
|
+
RL_CALL(rl_select, RL_OK, db, selected_database);
|
1088
|
+
|
1089
|
+
long page_number = db->next_empty_page;
|
1090
|
+
while (page_number != db->number_of_pages) {
|
1091
|
+
pages[page_number] = 1;
|
1092
|
+
RL_CALL(rl_long_get, RL_OK, db, &page_number, page_number);
|
1093
|
+
}
|
1094
|
+
|
1095
|
+
for (i = 1; i < db->number_of_pages; i++) {
|
1096
|
+
if (pages[i] == 0) {
|
1097
|
+
fprintf(stderr, "Found orphan page %ld\n", i);
|
1098
|
+
missing_pages++;
|
1099
|
+
}
|
1100
|
+
}
|
1101
|
+
|
1102
|
+
if (missing_pages) {
|
1103
|
+
fprintf(stderr, "Missing %ld pages\n", missing_pages);
|
1104
|
+
retval = RL_UNEXPECTED;
|
1105
|
+
}
|
1106
|
+
cleanup:
|
1107
|
+
rl_free(pages);
|
1108
|
+
return retval;
|
1109
|
+
}
|
1110
|
+
|
1111
|
+
int rl_select(struct rlite *db, int selected_database)
|
1112
|
+
{
|
1113
|
+
if (selected_database < 0 || selected_database >= db->number_of_databases) {
|
1114
|
+
return RL_INVALID_PARAMETERS;
|
1115
|
+
}
|
1116
|
+
db->selected_database = selected_database;
|
1117
|
+
return RL_OK;
|
1118
|
+
}
|
1119
|
+
|
1120
|
+
int rl_move(struct rlite *db, unsigned char *key, long keylen, int database)
|
1121
|
+
{
|
1122
|
+
int retval;
|
1123
|
+
int olddb = db->selected_database;
|
1124
|
+
unsigned char type;
|
1125
|
+
unsigned long long expires;
|
1126
|
+
long value_page;
|
1127
|
+
// this could be more efficient, if we don't delete the value page
|
1128
|
+
RL_CALL(rl_key_get, RL_FOUND, db, key, keylen, &type, NULL, &value_page, &expires, NULL);
|
1129
|
+
RL_CALL(rl_select, RL_OK, db, database);
|
1130
|
+
RL_CALL(rl_key_get, RL_NOT_FOUND, db, key, keylen, NULL, NULL, NULL, NULL, NULL);
|
1131
|
+
RL_CALL(rl_select, RL_OK, db, olddb);
|
1132
|
+
RL_CALL(rl_key_delete, RL_OK, db, key, keylen);
|
1133
|
+
RL_CALL(rl_select, RL_OK, db, database);
|
1134
|
+
RL_CALL(rl_key_set, RL_OK, db, key, keylen, type, value_page, expires, 0);
|
1135
|
+
retval = RL_OK;
|
1136
|
+
cleanup:
|
1137
|
+
rl_select(db, olddb);
|
1138
|
+
return retval;
|
1139
|
+
}
|
1140
|
+
|
1141
|
+
int rl_rename(struct rlite *db, const unsigned char *src, long srclen, const unsigned char *target, long targetlen, int overwrite)
|
1142
|
+
{
|
1143
|
+
int retval;
|
1144
|
+
unsigned char type;
|
1145
|
+
unsigned long long expires;
|
1146
|
+
long value_page;
|
1147
|
+
long version = 0;
|
1148
|
+
if (overwrite) {
|
1149
|
+
RL_CALL2(rl_key_get, RL_FOUND, RL_NOT_FOUND, db, target, targetlen, NULL, NULL, NULL, NULL, &version);
|
1150
|
+
if (retval == RL_FOUND) {
|
1151
|
+
RL_CALL(rl_key_delete_with_value, RL_OK, db, target, targetlen);
|
1152
|
+
version++;
|
1153
|
+
}
|
1154
|
+
}
|
1155
|
+
else {
|
1156
|
+
RL_CALL(rl_key_get, RL_NOT_FOUND, db, target, targetlen, NULL, NULL, NULL, NULL, NULL);
|
1157
|
+
}
|
1158
|
+
// this could be more efficient, if we don't delete the value page
|
1159
|
+
RL_CALL(rl_key_get, RL_FOUND, db, src, srclen, &type, NULL, &value_page, &expires, NULL);
|
1160
|
+
RL_CALL(rl_key_delete, RL_OK, db, src, srclen);
|
1161
|
+
RL_CALL(rl_key_set, RL_OK, db, target, targetlen, type, value_page, expires, version);
|
1162
|
+
retval = RL_OK;
|
1163
|
+
cleanup:
|
1164
|
+
return retval;
|
1165
|
+
}
|
1166
|
+
|
1167
|
+
int rl_dbsize(struct rlite *db, long *size)
|
1168
|
+
{
|
1169
|
+
int retval;
|
1170
|
+
rl_btree *btree;
|
1171
|
+
retval = rl_get_key_btree(db, &btree, 0);
|
1172
|
+
if (retval == RL_NOT_FOUND) {
|
1173
|
+
*size = 0;
|
1174
|
+
retval = RL_OK;
|
1175
|
+
goto cleanup;
|
1176
|
+
}
|
1177
|
+
else if (retval != RL_OK) {
|
1178
|
+
goto cleanup;
|
1179
|
+
}
|
1180
|
+
*size = btree->number_of_elements;
|
1181
|
+
retval = RL_OK;
|
1182
|
+
cleanup:
|
1183
|
+
return retval;
|
1184
|
+
}
|
1185
|
+
|
1186
|
+
int rl_keys(struct rlite *db, unsigned char *pattern, long patternlen, long *_len, unsigned char ***_result, long **_resultlen)
|
1187
|
+
{
|
1188
|
+
int retval;
|
1189
|
+
rl_btree *btree;
|
1190
|
+
rl_btree_iterator *iterator;
|
1191
|
+
rl_key *key;
|
1192
|
+
void *tmp;
|
1193
|
+
long alloc, len;
|
1194
|
+
unsigned char **result = NULL, *keystr;
|
1195
|
+
long *resultlen = NULL, keystrlen;
|
1196
|
+
retval = rl_get_key_btree(db, &btree, 0);
|
1197
|
+
if (retval == RL_NOT_FOUND) {
|
1198
|
+
*_len = 0;
|
1199
|
+
*_result = NULL;
|
1200
|
+
*_resultlen = NULL;
|
1201
|
+
retval = RL_OK;
|
1202
|
+
goto cleanup;
|
1203
|
+
}
|
1204
|
+
else if (retval != RL_OK) {
|
1205
|
+
goto cleanup;
|
1206
|
+
}
|
1207
|
+
|
1208
|
+
RL_CALL(rl_btree_iterator_create, RL_OK, db, btree, &iterator);
|
1209
|
+
|
1210
|
+
len = 0;
|
1211
|
+
alloc = 16;
|
1212
|
+
RL_MALLOC(result, sizeof(unsigned char *) * alloc);
|
1213
|
+
RL_MALLOC(resultlen, sizeof(long) * alloc);
|
1214
|
+
int allkeys = patternlen == 1 && pattern[0] == '*';
|
1215
|
+
while ((retval = rl_btree_iterator_next(iterator, NULL, &tmp)) == RL_OK) {
|
1216
|
+
key = tmp;
|
1217
|
+
RL_CALL(rl_multi_string_get, RL_OK, db, key->string_page, &keystr, &keystrlen);
|
1218
|
+
if (allkeys || rl_stringmatchlen((char *)pattern, patternlen, (char *)keystr, keystrlen, 0)) {
|
1219
|
+
if (len + 1 == alloc) {
|
1220
|
+
RL_REALLOC(result, sizeof(unsigned char *) * alloc * 2)
|
1221
|
+
RL_REALLOC(resultlen, sizeof(long) * alloc * 2)
|
1222
|
+
alloc *= 2;
|
1223
|
+
}
|
1224
|
+
result[len] = keystr;
|
1225
|
+
resultlen[len] = keystrlen;
|
1226
|
+
len++;
|
1227
|
+
}
|
1228
|
+
else {
|
1229
|
+
rl_free(keystr);
|
1230
|
+
}
|
1231
|
+
rl_free(key);
|
1232
|
+
}
|
1233
|
+
|
1234
|
+
if (retval != RL_END) {
|
1235
|
+
goto cleanup;
|
1236
|
+
}
|
1237
|
+
|
1238
|
+
// TODO: should we realloc to shrink the result?
|
1239
|
+
*_len = len;
|
1240
|
+
*_result = result;
|
1241
|
+
*_resultlen = resultlen;
|
1242
|
+
|
1243
|
+
retval = RL_OK;
|
1244
|
+
cleanup:
|
1245
|
+
if (retval != RL_OK) {
|
1246
|
+
rl_free(result);
|
1247
|
+
rl_free(resultlen);
|
1248
|
+
}
|
1249
|
+
return retval;
|
1250
|
+
}
|
1251
|
+
|
1252
|
+
int rl_randomkey(struct rlite *db, unsigned char **key, long *keylen)
|
1253
|
+
{
|
1254
|
+
int retval;
|
1255
|
+
rl_btree *btree;
|
1256
|
+
rl_key *key_obj;
|
1257
|
+
RL_CALL(rl_get_key_btree, RL_OK, db, &btree, 0);
|
1258
|
+
RL_CALL(rl_btree_random_element, RL_OK, db, btree, NULL, (void **)&key_obj);
|
1259
|
+
RL_CALL(rl_multi_string_get, RL_OK, db, key_obj->string_page, key, keylen);
|
1260
|
+
cleanup:
|
1261
|
+
return retval;
|
1262
|
+
}
|
1263
|
+
|
1264
|
+
int rl_flushdb(struct rlite *db)
|
1265
|
+
{
|
1266
|
+
int retval;
|
1267
|
+
rl_btree *btree;
|
1268
|
+
rl_btree_iterator *iterator = NULL;
|
1269
|
+
rl_key *key;
|
1270
|
+
void *tmp;
|
1271
|
+
RL_CALL2(rl_get_key_btree, RL_OK, RL_NOT_FOUND, db, &btree, 0);
|
1272
|
+
if (retval == RL_NOT_FOUND) {
|
1273
|
+
retval = RL_OK;
|
1274
|
+
goto cleanup;
|
1275
|
+
}
|
1276
|
+
|
1277
|
+
RL_CALL(rl_btree_iterator_create, RL_OK, db, btree, &iterator);
|
1278
|
+
|
1279
|
+
while ((retval = rl_btree_iterator_next(iterator, NULL, &tmp)) == RL_OK) {
|
1280
|
+
key = tmp;
|
1281
|
+
RL_CALL(rl_key_delete_value, RL_OK, db, key->type, key->value_page);
|
1282
|
+
RL_CALL(rl_multi_string_delete, RL_OK, db, key->string_page);
|
1283
|
+
rl_free(key);
|
1284
|
+
}
|
1285
|
+
if (retval != RL_END) {
|
1286
|
+
goto cleanup;
|
1287
|
+
}
|
1288
|
+
RL_CALL(rl_btree_delete, RL_OK, db, btree);
|
1289
|
+
RL_CALL(rl_delete, RL_OK, db, db->databases[db->selected_database]);
|
1290
|
+
db->databases[db->selected_database] = 0;
|
1291
|
+
RL_CALL(rl_write, RL_OK, db, &rl_data_type_header, 0, NULL);
|
1292
|
+
retval = RL_OK;
|
1293
|
+
cleanup:
|
1294
|
+
return retval;
|
1295
|
+
}
|
1296
|
+
|
1297
|
+
int rl_flushall(struct rlite *db)
|
1298
|
+
{
|
1299
|
+
int retval, i;
|
1300
|
+
int selected_database = db->selected_database;
|
1301
|
+
|
1302
|
+
for (i = 0; i < db->number_of_databases; i++) {
|
1303
|
+
db->selected_database = i;
|
1304
|
+
RL_CALL(rl_flushdb, RL_OK, db);
|
1305
|
+
}
|
1306
|
+
db->selected_database = selected_database;
|
1307
|
+
cleanup:
|
1308
|
+
return retval;
|
1309
|
+
}
|