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,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
|