hirlite 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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