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.
Files changed (66) hide show
  1. checksums.yaml +15 -0
  2. data/LICENSE +28 -0
  3. data/Rakefile +51 -0
  4. data/ext/hirlite_ext/extconf.rb +33 -0
  5. data/ext/hirlite_ext/hirlite_ext.c +14 -0
  6. data/ext/hirlite_ext/hirlite_ext.h +38 -0
  7. data/ext/hirlite_ext/rlite.c +351 -0
  8. data/lib/hirlite/rlite.rb +1 -0
  9. data/lib/hirlite/version.rb +3 -0
  10. data/lib/hirlite.rb +2 -0
  11. data/vendor/rlite/Makefile +6 -0
  12. data/vendor/rlite/deps/crc64.c +191 -0
  13. data/vendor/rlite/deps/crc64.h +3 -0
  14. data/vendor/rlite/deps/endianconv.h +73 -0
  15. data/vendor/rlite/deps/hyperloglog.c +1547 -0
  16. data/vendor/rlite/deps/hyperloglog.h +14 -0
  17. data/vendor/rlite/deps/lzf.h +100 -0
  18. data/vendor/rlite/deps/lzfP.h +159 -0
  19. data/vendor/rlite/deps/lzf_c.c +295 -0
  20. data/vendor/rlite/deps/lzf_d.c +150 -0
  21. data/vendor/rlite/deps/sha1.c +227 -0
  22. data/vendor/rlite/deps/sha1.h +19 -0
  23. data/vendor/rlite/deps/utilfromredis.c +397 -0
  24. data/vendor/rlite/deps/utilfromredis.h +11 -0
  25. data/vendor/rlite/src/Makefile +79 -0
  26. data/vendor/rlite/src/constants.h +15 -0
  27. data/vendor/rlite/src/dump.c +191 -0
  28. data/vendor/rlite/src/dump.h +3 -0
  29. data/vendor/rlite/src/hirlite.c +3985 -0
  30. data/vendor/rlite/src/hirlite.h +186 -0
  31. data/vendor/rlite/src/page_btree.c +1556 -0
  32. data/vendor/rlite/src/page_btree.h +133 -0
  33. data/vendor/rlite/src/page_key.c +283 -0
  34. data/vendor/rlite/src/page_key.h +25 -0
  35. data/vendor/rlite/src/page_list.c +718 -0
  36. data/vendor/rlite/src/page_list.h +70 -0
  37. data/vendor/rlite/src/page_long.c +61 -0
  38. data/vendor/rlite/src/page_long.h +14 -0
  39. data/vendor/rlite/src/page_multi_string.c +538 -0
  40. data/vendor/rlite/src/page_multi_string.h +18 -0
  41. data/vendor/rlite/src/page_skiplist.c +689 -0
  42. data/vendor/rlite/src/page_skiplist.h +70 -0
  43. data/vendor/rlite/src/page_string.c +55 -0
  44. data/vendor/rlite/src/page_string.h +12 -0
  45. data/vendor/rlite/src/pqsort.c +185 -0
  46. data/vendor/rlite/src/pqsort.h +40 -0
  47. data/vendor/rlite/src/restore.c +401 -0
  48. data/vendor/rlite/src/restore.h +3 -0
  49. data/vendor/rlite/src/rlite.c +1309 -0
  50. data/vendor/rlite/src/rlite.h +159 -0
  51. data/vendor/rlite/src/sort.c +530 -0
  52. data/vendor/rlite/src/sort.h +18 -0
  53. data/vendor/rlite/src/status.h +19 -0
  54. data/vendor/rlite/src/type_hash.c +607 -0
  55. data/vendor/rlite/src/type_hash.h +29 -0
  56. data/vendor/rlite/src/type_list.c +477 -0
  57. data/vendor/rlite/src/type_list.h +23 -0
  58. data/vendor/rlite/src/type_set.c +796 -0
  59. data/vendor/rlite/src/type_set.h +34 -0
  60. data/vendor/rlite/src/type_string.c +613 -0
  61. data/vendor/rlite/src/type_string.h +34 -0
  62. data/vendor/rlite/src/type_zset.c +1147 -0
  63. data/vendor/rlite/src/type_zset.h +50 -0
  64. data/vendor/rlite/src/util.c +334 -0
  65. data/vendor/rlite/src/util.h +71 -0
  66. metadata +151 -0
@@ -0,0 +1,159 @@
1
+ #ifndef _RLITE_H
2
+ #define _RLITE_H
3
+
4
+ #include <stdio.h>
5
+ #include "status.h"
6
+ #include "page_btree.h"
7
+ #include "page_key.h"
8
+ #include "type_hash.h"
9
+ #include "type_zset.h"
10
+ #include "type_set.h"
11
+ #include "type_list.h"
12
+ #include "type_string.h"
13
+ #include "sort.h"
14
+ #include "restore.h"
15
+ #include "dump.h"
16
+ #include "util.h"
17
+
18
+ #define REDIS_RDB_VERSION 6
19
+
20
+ #define REDIS_RDB_6BITLEN 0
21
+ #define REDIS_RDB_14BITLEN 1
22
+ #define REDIS_RDB_32BITLEN 2
23
+ #define REDIS_RDB_ENCVAL 3
24
+
25
+ #define REDIS_RDB_OPCODE_EXPIRETIME_MS 252
26
+ #define REDIS_RDB_OPCODE_EXPIRETIME 253
27
+ #define REDIS_RDB_OPCODE_SELECTDB 254
28
+ #define REDIS_RDB_OPCODE_EOF 255
29
+
30
+ #define REDIS_RDB_TYPE_STRING 0
31
+ #define REDIS_RDB_TYPE_LIST 1
32
+ #define REDIS_RDB_TYPE_SET 2
33
+ #define REDIS_RDB_TYPE_ZSET 3
34
+ #define REDIS_RDB_TYPE_HASH 4
35
+ #define REDIS_RDB_TYPE_HASH_ZIPMAP 9
36
+ #define REDIS_RDB_TYPE_LIST_ZIPLIST 10
37
+ #define REDIS_RDB_TYPE_SET_INTSET 11
38
+ #define REDIS_RDB_TYPE_ZSET_ZIPLIST 12
39
+ #define REDIS_RDB_TYPE_HASH_ZIPLIST 13
40
+
41
+ #define REDIS_RDB_ENC_INT8 0
42
+ #define REDIS_RDB_ENC_INT16 1
43
+ #define REDIS_RDB_ENC_INT32 2
44
+ #define REDIS_RDB_ENC_LZF 3
45
+
46
+ #define RL_MEMORY_DRIVER 0
47
+ #define RL_FILE_DRIVER 1
48
+
49
+ #define RLITE_OPEN_READONLY 0x00000001
50
+ #define RLITE_OPEN_READWRITE 0x00000002
51
+ #define RLITE_OPEN_CREATE 0x00000004
52
+
53
+ struct rlite;
54
+ struct rl_btree;
55
+
56
+ typedef struct rl_data_type {
57
+ const char *name;
58
+ int (*serialize)(struct rlite *db, void *obj, unsigned char *data);
59
+ int (*deserialize)(struct rlite *db, void **obj, void *context, unsigned char *data);
60
+ int (*destroy)(struct rlite *db, void *obj);
61
+ } rl_data_type;
62
+
63
+ typedef struct {
64
+ FILE *fp;
65
+ char *filename;
66
+ int mode;
67
+ } rl_file_driver;
68
+
69
+ typedef struct {
70
+ char *data;
71
+ long datalen;
72
+ } rl_memory_driver;
73
+
74
+ typedef struct {
75
+ long page_number;
76
+ rl_data_type *type;
77
+ void *obj;
78
+ #ifdef DEBUG
79
+ unsigned char *serialized_data;
80
+ #endif
81
+ } rl_page;
82
+
83
+ typedef struct rlite {
84
+ // these four properties can change during a transaction
85
+ // we need to record their original values to use when
86
+ // checking watched keys
87
+ long initial_number_of_pages;
88
+ int initial_number_of_databases;
89
+ long *initial_databases;
90
+
91
+ long number_of_pages;
92
+ long next_empty_page;
93
+ long page_size;
94
+ void *driver;
95
+ int driver_type;
96
+ int selected_database;
97
+ int number_of_databases;
98
+ long *databases;
99
+ long read_pages_alloc;
100
+ long read_pages_len;
101
+ rl_page **read_pages;
102
+ long write_pages_alloc;
103
+ long write_pages_len;
104
+ rl_page **write_pages;
105
+ } rlite;
106
+
107
+ typedef struct watched_key {
108
+ unsigned char digest[20];
109
+ long version;
110
+ int database;
111
+ } watched_key;
112
+
113
+ int rl_open(const char *filename, rlite **db, int flags);
114
+ int rl_close(rlite *db);
115
+
116
+ int rl_read_header(rlite *db);
117
+ int rl_read(struct rlite *db, rl_data_type *type, long page, void *context, void **obj, int cache);
118
+ int rl_get_key_btree(rlite *db, struct rl_btree **btree, int create);
119
+ int rl_alloc_page_number(rlite *db, long *page_number);
120
+ int rl_write(struct rlite *db, rl_data_type *type, long page, void *obj);
121
+ int rl_purge_cache(struct rlite *db, long page);
122
+ int rl_delete(struct rlite *db, long page);
123
+ int rl_dirty_hash(struct rlite *db, unsigned char **hash);
124
+ int rl_commit(struct rlite *db);
125
+ int rl_discard(struct rlite *db);
126
+ int rl_is_balanced(struct rlite *db);
127
+ int rl_select(struct rlite *db, int selected_database);
128
+ int rl_move(struct rlite *db, unsigned char *key, long keylen, int database);
129
+ int rl_rename(struct rlite *db, const unsigned char *src, long srclen, const unsigned char *target, long targetlen, int overwrite);
130
+ int rl_dbsize(struct rlite *db, long *size);
131
+ int rl_keys(struct rlite *db, unsigned char *pattern, long patternlen, long *size, unsigned char ***result, long **resultlen);
132
+ int rl_randomkey(struct rlite *db, unsigned char **key, long *keylen);
133
+ int rl_flushall(struct rlite *db);
134
+ int rl_flushdb(struct rlite *db);
135
+
136
+ extern rl_data_type rl_data_type_header;
137
+ extern rl_data_type rl_data_type_btree_hash_sha1_hashkey;
138
+ extern rl_data_type rl_data_type_btree_hash_sha1_key;
139
+ extern rl_data_type rl_data_type_btree_node_hash_sha1_key;
140
+ extern rl_data_type rl_data_type_btree_node_hash_sha1_hashkey;
141
+ extern rl_data_type rl_data_type_btree_set_long;
142
+ extern rl_data_type rl_data_type_btree_node_set_long;
143
+ extern rl_data_type rl_data_type_btree_hash_long_long;
144
+ extern rl_data_type rl_data_type_btree_node_hash_long_long;
145
+ extern rl_data_type rl_data_type_btree_hash_double_long;
146
+ extern rl_data_type rl_data_type_btree_node_hash_double_long;
147
+ extern rl_data_type rl_data_type_btree_hash_sha1_double;
148
+ extern rl_data_type rl_data_type_btree_node_hash_sha1_double;
149
+ extern rl_data_type rl_data_type_btree_hash_sha1_long;
150
+ extern rl_data_type rl_data_type_btree_node_hash_sha1_long;
151
+ extern rl_data_type rl_data_type_list_long;
152
+ extern rl_data_type rl_data_type_list_node_long;
153
+ extern rl_data_type rl_data_type_list_node_key;
154
+ extern rl_data_type rl_data_type_string;
155
+ extern rl_data_type rl_data_type_long;
156
+ extern rl_data_type rl_data_type_skiplist;
157
+ extern rl_data_type rl_data_type_skiplist_node;
158
+
159
+ #endif
@@ -0,0 +1,530 @@
1
+ /* SORT command and helper functions.
2
+ *
3
+ * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
4
+ * All rights reserved.
5
+ *
6
+ * Redistribution and use in source and binary forms, with or without
7
+ * modification, are permitted provided that the following conditions are met:
8
+ *
9
+ * * Redistributions of source code must retain the above copyright notice,
10
+ * this list of conditions and the following disclaimer.
11
+ * * Redistributions in binary form must reproduce the above copyright
12
+ * notice, this list of conditions and the following disclaimer in the
13
+ * documentation and/or other materials provided with the distribution.
14
+ * * Neither the name of Redis nor the names of its contributors may be used
15
+ * to endorse or promote products derived from this software without
16
+ * specific prior written permission.
17
+ *
18
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
+ * POSSIBILITY OF SUCH DAMAGE.
29
+ */
30
+
31
+
32
+ #include "rlite.h"
33
+ #include "sort.h"
34
+ #include "pqsort.h" /* Partial qsort for SORT+LIMIT */
35
+ #include <math.h> /* isnan() */
36
+ #include <errno.h>
37
+
38
+ static int sort_desc = 0;
39
+ static int sort_alpha = 0;
40
+ static int sort_bypattern = 0;
41
+ static int sort_store = 0;
42
+
43
+ static int compareString(unsigned char *obj1, long obj1len, unsigned char *obj2, long obj2len) {
44
+ int cmp;
45
+ long minlen = obj1len > obj2len ? obj2len : obj1len;
46
+ cmp = memcmp(obj1, obj2, minlen);
47
+ if (cmp == 0 && obj1len != obj2len) {
48
+ cmp = obj1len > obj2len ? 1 : -1;
49
+ }
50
+ return cmp;
51
+ }
52
+ static int compareObjectsAsString(const rliteSortObject *so1, const rliteSortObject *so2) {
53
+ return compareString(so1->obj, so1->objlen, so2->obj, so2->objlen);
54
+ }
55
+
56
+ static int compareObjectsAsLongLong(const rliteSortObject *so1, const rliteSortObject *so2) {
57
+ return strcoll((char *)so1->obj, (char *)so2->obj);
58
+ }
59
+
60
+ /* Return the value associated to the key with a name obtained using
61
+ * the following rules:
62
+ *
63
+ * 1) The first occurrence of '*' in 'pattern' is substituted with 'subst'.
64
+ *
65
+ * 2) If 'pattern' matches the "->" string, everything on the left of
66
+ * the arrow is treated as the name of a hash field, and the part on the
67
+ * left as the key name containing a hash. The value of the specified
68
+ * field is returned.
69
+ *
70
+ * 3) If 'pattern' equals "#", the function simply returns 'subst' itself so
71
+ * that the SORT command can be used like: SORT key GET # to retrieve
72
+ * the Set/List elements directly.
73
+ *
74
+ * The returned object will always have its refcount increased by 1
75
+ * when it is non-NULL. */
76
+ static int lookupKeyByPattern(rlite *db, unsigned char *pattern, long patternlen, unsigned char *subst, long sublen, unsigned char **retobj, long *retobjlen) {
77
+ int retval = RL_OK;
78
+ unsigned char type;
79
+ unsigned char *p, *f;
80
+ unsigned char *key = NULL;
81
+ long keylen;
82
+ unsigned char *field = NULL;
83
+ long fieldlen = 0;
84
+ int prefixlen, postfixlen;
85
+
86
+ /* If the pattern is "#" return the substitution object itself in order
87
+ * to implement the "SORT ... GET #" feature. */
88
+ if (pattern[0] == '#' && pattern[1] == '\0') {
89
+ *retobj = malloc(sizeof(unsigned char) * sublen);
90
+ if (!*retobj) {
91
+ return RL_OUT_OF_MEMORY;
92
+ }
93
+ memcpy(*retobj, subst, sublen);
94
+ *retobjlen = sublen;
95
+ return RL_OK;
96
+ }
97
+
98
+ /* If we can't find '*' in the pattern we return NULL as to GET a
99
+ * fixed key does not make sense. */
100
+ p = (unsigned char *)strchr((char *)pattern,'*');
101
+ if (!p) {
102
+ retval = RL_NOT_FOUND;
103
+ goto cleanup;
104
+ }
105
+
106
+ /* Find out if we're dealing with a hash dereference. */
107
+ if ((f = (unsigned char *)strstr((char *)p+1, "->")) != NULL && *(f+2) != '\0') {
108
+ fieldlen = patternlen-(f-pattern)-2;
109
+ field = malloc(sizeof(unsigned char) * fieldlen);
110
+ if (!field) {
111
+ return RL_OUT_OF_MEMORY;
112
+ }
113
+ memcpy(field, f + 2, fieldlen);
114
+ } else {
115
+ fieldlen = 0;
116
+ }
117
+
118
+ /* Perform the '*' substitution. */
119
+ prefixlen = p-pattern;
120
+ postfixlen = patternlen-(prefixlen+1)-(fieldlen ? fieldlen+2 : 0);
121
+ keylen = prefixlen+sublen+postfixlen;
122
+ key = malloc(sizeof(unsigned char) * keylen);
123
+ if (!key) {
124
+ retval = RL_OUT_OF_MEMORY;
125
+ goto cleanup;
126
+ }
127
+ memcpy(key,pattern,prefixlen);
128
+ memcpy(key+prefixlen,subst,sublen);
129
+ memcpy(key+prefixlen+sublen,p+1,postfixlen);
130
+
131
+ /* Lookup substituted key */
132
+ RL_CALL(rl_key_get, RL_FOUND, db, key, keylen, &type, NULL, NULL, NULL, NULL);
133
+
134
+ if (field) {
135
+ if (type != RL_TYPE_HASH) {
136
+ retval = RL_NOT_FOUND;
137
+ goto cleanup;
138
+ }
139
+
140
+ /* Retrieve value from hash by the field name. This operation
141
+ * already increases the refcount of the returned object. */
142
+ RL_CALL(rl_hget, RL_FOUND, db, key, keylen, field, fieldlen, retobj, retobjlen);
143
+ } else {
144
+ if (type != RL_TYPE_STRING) {
145
+ retval = RL_NOT_FOUND;
146
+ goto cleanup;
147
+ }
148
+
149
+ /* Every object that this function returns needs to have its refcount
150
+ * increased. sortCommand decreases it again. */
151
+ RL_CALL(rl_get, RL_OK, db, key, keylen, retobj, retobjlen);
152
+ }
153
+ retval = RL_OK;
154
+ cleanup:
155
+ free(key);
156
+ free(field);
157
+ if (retval != RL_OK) {
158
+ *retobj = NULL;
159
+ *retobjlen = 0;
160
+ }
161
+ return retval;
162
+ }
163
+
164
+ /* sortCompare() is used by qsort in sortCommand(). Given that qsort_r with
165
+ * the additional parameter is not standard but a BSD-specific we have to
166
+ * pass sorting parameters via the global 'server' structure */
167
+ static int sortCompare(const void *s1, const void *s2) {
168
+ const rliteSortObject *so1 = s1, *so2 = s2;
169
+ int cmp;
170
+
171
+ if (!sort_alpha) {
172
+ /* Numeric sorting. Here it's trivial as we precomputed scores */
173
+ if (so1->u.score > so2->u.score) {
174
+ cmp = 1;
175
+ } else if (so1->u.score < so2->u.score) {
176
+ cmp = -1;
177
+ } else {
178
+ /* Objects have the same score, but we don't want the comparison
179
+ * to be undefined, so we compare objects lexicographically.
180
+ * This way the result of SORT is deterministic. */
181
+ cmp = compareObjectsAsString(so1, so2);
182
+ }
183
+ } else {
184
+ /* Alphanumeric sorting */
185
+ if (sort_bypattern) {
186
+ if (!so1->u.cmpobj.obj || !so2->u.cmpobj.obj) {
187
+ /* At least one compare object is NULL */
188
+ if (so1->u.cmpobj.obj == so2->u.cmpobj.obj)
189
+ cmp = 0;
190
+ else if (so1->u.cmpobj.obj == NULL)
191
+ cmp = -1;
192
+ else
193
+ cmp = 1;
194
+ } else {
195
+ /* We have both the objects, compare them. */
196
+ if (sort_store) {
197
+ cmp = compareString(so1->u.cmpobj.obj, so1->u.cmpobj.objlen, so2->u.cmpobj.obj, so2->u.cmpobj.objlen);
198
+ } else {
199
+ /* Here we can use strcoll() directly as we are sure that
200
+ * the objects are decoded string objects. */
201
+ cmp = strcoll((char *)so1->u.cmpobj.obj, (char *)so2->u.cmpobj.obj);
202
+ }
203
+ }
204
+ } else {
205
+ /* Compare elements directly. */
206
+ if (sort_store) {
207
+ cmp = compareObjectsAsString(so1,so2);
208
+ } else {
209
+ cmp = compareObjectsAsLongLong(so1,so2);
210
+ }
211
+ }
212
+ }
213
+ return sort_desc ? -cmp : cmp;
214
+ }
215
+
216
+ int rl_sort(struct rlite *db, unsigned char *key, long keylen, unsigned char *sortby, long sortbylen, int dontsort, int alpha, int desc, long limit_start, long limit_count, int getc, unsigned char **getv, long *getvlen, unsigned char *storekey, long storekeylen, long *retobjc, unsigned char ***retobjv, long **retobjvlen) {
217
+ int i, j, k, retval;
218
+ long vectorlen, start, end;
219
+ long size;
220
+ unsigned char **values = NULL, *value;
221
+ long *valueslen = NULL, valuelen;
222
+ rliteSortObject *vector = NULL;
223
+ /* Lookup the key to sort. It must be of the right types */
224
+ unsigned char type;
225
+ long objc;
226
+ unsigned char **objv;
227
+ long *objvlen;
228
+
229
+ if (storekey) {
230
+ RL_CALL2(rl_key_delete_with_value, RL_OK, RL_NOT_FOUND, db, storekey, storekeylen);
231
+ }
232
+
233
+ RL_CALL(rl_key_get, RL_FOUND, db, key, keylen, &type, NULL, NULL, NULL, NULL);
234
+ if (type != RL_TYPE_SET &&
235
+ type != RL_TYPE_LIST &&
236
+ type != RL_TYPE_ZSET)
237
+ {
238
+ retval = RL_WRONG_TYPE;
239
+ goto cleanup;
240
+ }
241
+
242
+ /* When sorting a set with no sort specified, we must sort the output
243
+ * so the result is consistent across scripting and replication.
244
+ *
245
+ * The other types (list, sorted set) will retain their native order
246
+ * even if no sort order is requested, so they remain stable across
247
+ * scripting and replication. */
248
+ if (dontsort &&
249
+ type == RL_TYPE_SET &&
250
+ storekey)
251
+ {
252
+ /* Force ALPHA sorting */
253
+ dontsort = 0;
254
+ alpha = 1;
255
+ sortby = NULL;
256
+ }
257
+
258
+ /* Obtain the length of the object to sort. */
259
+ switch(type) {
260
+ case RL_TYPE_LIST:
261
+ RL_CALL(rl_llen, RL_OK, db, key, keylen, &vectorlen);
262
+ break;
263
+ case RL_TYPE_SET:
264
+ RL_CALL(rl_scard, RL_OK, db, key, keylen, &vectorlen);
265
+ break;
266
+ case RL_TYPE_ZSET:
267
+ RL_CALL(rl_zcard, RL_OK, db, key, keylen, &vectorlen);
268
+ break;
269
+ default:
270
+ vectorlen = 0;
271
+ retval = RL_UNEXPECTED;
272
+ goto cleanup;
273
+ }
274
+
275
+ /* Perform LIMIT start,count sanity checking. */
276
+ start = (limit_start < 0) ? 0 : limit_start;
277
+ end = (limit_count < 0) ? vectorlen-1 : start+limit_count-1;
278
+ if (start >= vectorlen) {
279
+ start = vectorlen-1;
280
+ end = vectorlen-2;
281
+ }
282
+ if (end >= vectorlen) end = vectorlen-1;
283
+
284
+ /* Whenever possible, we load elements into the output array in a more
285
+ * direct way. This is possible if:
286
+ *
287
+ * 1) The object to sort is a sorted set or a list (internally sorted).
288
+ * 2) There is nothing to sort as dontsort is true (BY <constant string>).
289
+ *
290
+ * In this special case, if we have a LIMIT option that actually reduces
291
+ * the number of elements to fetch, we also optimize to just load the
292
+ * range we are interested in and allocating a vector that is big enough
293
+ * for the selected range length. */
294
+ if ((type == RL_TYPE_ZSET || type == RL_TYPE_LIST) &&
295
+ dontsort &&
296
+ (start != 0 || end != vectorlen-1))
297
+ {
298
+ vectorlen = end-start+1;
299
+ }
300
+
301
+ /* Load the sorting vector with all the objects to sort */
302
+ RL_MALLOC(vector, sizeof(rliteSortObject) * vectorlen);
303
+ j = 0;
304
+
305
+ if (type == RL_TYPE_LIST && dontsort) {
306
+ /* Special handling for a list, if 'dontsort' is true.
307
+ * This makes sure we return elements in the list original
308
+ * ordering, accordingly to DESC / ASC options.
309
+ *
310
+ * Note that in this case we also handle LIMIT here in a direct
311
+ * way, just getting the required range, as an optimization. */
312
+ if (end >= start) {
313
+ RL_CALL(rl_lrange, RL_OK, db, key, keylen, start, end, &size, &values, &valueslen);
314
+
315
+ for (j = 0; j < size; j++) {
316
+ vector[j].obj = values[j];
317
+ vector[j].objlen = valueslen[j];
318
+ vector[j].u.score = 0;
319
+ vector[j].u.cmpobj.obj = NULL;
320
+ vector[j].u.cmpobj.objlen = 0;
321
+ }
322
+ /* Fix start/end: output code is not aware of this optimization. */
323
+ end -= start;
324
+ start = 0;
325
+ }
326
+ } else if (type == RL_TYPE_LIST) {
327
+ RL_CALL(rl_lrange, RL_OK, db, key, keylen, 0, -1, &size, &values, &valueslen);
328
+
329
+ for (j = 0; j < size; j++) {
330
+ vector[j].obj = values[j];
331
+ vector[j].objlen = valueslen[j];
332
+ vector[j].u.score = 0;
333
+ vector[j].u.cmpobj.obj = NULL;
334
+ vector[j].u.cmpobj.objlen = 0;
335
+ }
336
+ } else if (type == RL_TYPE_SET) {
337
+ rl_set_iterator *siterator;
338
+ RL_CALL(rl_smembers, RL_OK, db, &siterator, key, keylen);
339
+
340
+ while ((retval = rl_set_iterator_next(siterator, &value, &valuelen)) == RL_OK) {
341
+ vector[j].obj = value;
342
+ vector[j].objlen = valuelen;
343
+ vector[j].u.score = 0;
344
+ vector[j].u.cmpobj.obj = NULL;
345
+ vector[j].u.cmpobj.objlen = 0;
346
+ j++;
347
+ }
348
+ if (retval != RL_END) {
349
+ goto cleanup;
350
+ }
351
+ } else if (type == RL_TYPE_ZSET && dontsort) {
352
+ /* Special handling for a sorted set, if 'dontsort' is true.
353
+ * This makes sure we return elements in the sorted set original
354
+ * ordering, accordingly to DESC / ASC options.
355
+ *
356
+ * Note that in this case we also handle LIMIT here in a direct
357
+ * way, just getting the required range, as an optimization. */
358
+
359
+ rl_zset_iterator *ziterator;
360
+ if (desc) {
361
+ RL_CALL(rl_zrevrange, RL_OK, db, key, keylen, start, end, &ziterator);
362
+ } else {
363
+ RL_CALL(rl_zrange, RL_OK, db, key, keylen, start, end, &ziterator);
364
+ }
365
+ while ((retval = rl_zset_iterator_next(ziterator, NULL, &value, &valuelen)) == RL_OK) {
366
+ vector[j].obj = value;
367
+ vector[j].objlen = valuelen;
368
+ vector[j].u.score = 0;
369
+ vector[j].u.cmpobj.obj = NULL;
370
+ vector[j].u.cmpobj.objlen = 0;
371
+ j++;
372
+ }
373
+ if (retval != RL_END) {
374
+ goto cleanup;
375
+ }
376
+ /* Fix start/end: output code is not aware of this optimization. */
377
+ end -= start;
378
+ start = 0;
379
+ } else if (type == RL_TYPE_ZSET) {
380
+ rl_zset_iterator *ziterator;
381
+ RL_CALL(rl_zrange, RL_OK, db, key, keylen, 0, -1, &ziterator);
382
+ while ((retval = rl_zset_iterator_next(ziterator, NULL, &value, &valuelen)) == RL_OK) {
383
+ vector[j].obj = value;
384
+ vector[j].objlen = valuelen;
385
+ vector[j].u.score = 0;
386
+ vector[j].u.cmpobj.obj = NULL;
387
+ vector[j].u.cmpobj.objlen = 0;
388
+ j++;
389
+ }
390
+ if (retval != RL_END) {
391
+ goto cleanup;
392
+ }
393
+ } else {
394
+ retval = RL_UNEXPECTED;
395
+ goto cleanup;
396
+ }
397
+ if (j != vectorlen) {
398
+ retval = RL_UNEXPECTED;
399
+ goto cleanup;
400
+ }
401
+
402
+ /* Now it's time to load the right scores in the sorting vector */
403
+ if (dontsort == 0) {
404
+ for (j = 0; j < vectorlen; j++) {
405
+ unsigned char *byval;
406
+ long byvallen;
407
+ if (sortby) {
408
+ /* lookup value to sort by */
409
+ RL_CALL2(lookupKeyByPattern, RL_OK, RL_NOT_FOUND, db, sortby, sortbylen, vector[j].obj, vector[j].objlen, &byval, &byvallen);
410
+ if (!byval) continue;
411
+ } else {
412
+ /* use object itself to sort by */
413
+ byval = vector[j].obj;
414
+ byvallen = vector[j].objlen;
415
+ }
416
+
417
+ if (alpha) {
418
+ if (sortby) {
419
+ vector[j].u.cmpobj.obj = byval;
420
+ vector[j].u.cmpobj.objlen = byvallen;
421
+ }
422
+ } else {
423
+ unsigned char *eptr;
424
+ vector[j].u.score = rl_strtod(byval, byvallen, &eptr);
425
+ if (eptr[0] != '\0' || errno == ERANGE ||
426
+ isnan(vector[j].u.score))
427
+ {
428
+ if (sortby) {
429
+ rl_free(byval);
430
+ }
431
+ retval = RL_NAN;
432
+ goto cleanup;
433
+ }
434
+ if (sortby) {
435
+ rl_free(byval);
436
+ }
437
+ }
438
+ }
439
+
440
+ sort_desc = desc;
441
+ sort_alpha = alpha;
442
+ sort_bypattern = sortby ? 1 : 0;
443
+ sort_store = storekey ? 1 : 0;
444
+ if (sortby && (start != 0 || end != vectorlen-1)) {
445
+ pqsort(vector,vectorlen,sizeof(rliteSortObject),sortCompare, start,end);
446
+ }
447
+ else {
448
+ qsort(vector,vectorlen,sizeof(rliteSortObject),sortCompare);
449
+ }
450
+
451
+ if (sortby && alpha) {
452
+ for (j = 0; j < vectorlen; j++) {
453
+ rl_free(vector[j].u.cmpobj.obj);
454
+ vector[j].u.cmpobj.obj = NULL;
455
+ }
456
+ }
457
+ }
458
+
459
+ /* Send command output to the output buffer, performing the specified
460
+ * GET/DEL/INCR/DECR operations if any. */
461
+ objc = getc ? getc*(end-start+1) : end-start+1;
462
+ objv = malloc(sizeof(unsigned char *) * objc);
463
+ if (!objv) {
464
+ retval = RL_OUT_OF_MEMORY;
465
+ goto cleanup;
466
+ }
467
+ objvlen = malloc(sizeof(long) * objc);
468
+ if (!objvlen) {
469
+ free(objv);
470
+ retval = RL_OUT_OF_MEMORY;
471
+ goto cleanup;
472
+ }
473
+
474
+ for (i = 0, j = start; j <= end; j++) {
475
+ if (getc) {
476
+ for (k = 0; k < getc; k++) {
477
+ RL_CALL2(lookupKeyByPattern, RL_OK, RL_NOT_FOUND, db, getv[k], getvlen[k], vector[j].obj, vector[j].objlen, &objv[i], &objvlen[i]);
478
+ i++;
479
+ }
480
+ rl_free(vector[j].obj);
481
+ } else {
482
+ objv[i] = vector[j].obj;
483
+ objvlen[i] = vector[j].objlen;
484
+ i++;
485
+ }
486
+ }
487
+
488
+ *retobjc = objc;
489
+ if (storekey) {
490
+ if (objc) {
491
+ RL_CALL(rl_push, RL_OK, db, storekey, storekeylen, 1, 0, objc, objv, objvlen, NULL);
492
+ }
493
+ for (j = 0; j < vectorlen; j++) {
494
+ rl_free(vector[j].obj);
495
+ }
496
+ rl_free(objv);
497
+ rl_free(objvlen);
498
+ } else {
499
+ *retobjv = objv;
500
+ *retobjvlen = objvlen;
501
+ for (j = 0; j < start; j++) {
502
+ rl_free(vector[j].obj);
503
+ }
504
+
505
+ for (j = end + 1; j < vectorlen; j++) {
506
+ rl_free(vector[j].obj);
507
+ }
508
+ }
509
+
510
+ if (alpha) {
511
+ for (j = 0; j < vectorlen; j++) {
512
+ if (vector[j].u.cmpobj.obj) {
513
+ rl_free(vector[j].u.cmpobj.obj);
514
+ }
515
+ }
516
+ }
517
+
518
+ retval = RL_OK;
519
+ cleanup:
520
+ if (retval != RL_OK) {
521
+ for (j = 0; j < vectorlen; j++) {
522
+ rl_free(vector[j].obj);
523
+ }
524
+ *retobjc = 0;
525
+ }
526
+ rl_free(vector);
527
+ rl_free(values);
528
+ rl_free(valueslen);
529
+ return retval;
530
+ }
@@ -0,0 +1,18 @@
1
+ #ifndef _RL_SORT_H
2
+ #define _RL_SORT_H
3
+ #include "rlite.h"
4
+
5
+ typedef struct rliteSortObject {
6
+ unsigned char *obj;
7
+ long objlen;
8
+ union {
9
+ double score;
10
+ struct {
11
+ unsigned char *obj;
12
+ long objlen;
13
+ } cmpobj;
14
+ } u;
15
+ } rliteSortObject;
16
+
17
+ int rl_sort(struct rlite *db, unsigned char *key, long keylen, unsigned char *sortby, long sortbylen, int dontsort, int alpha, int desc, long limit_start, long limit_count, int getc, unsigned char **getv, long *getvlen, unsigned char *storekey, long storekeylen, long *retobjc, unsigned char ***retobjv, long **retobjvlen);
18
+ #endif