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