mirror of
https://github.com/dragonflydb/dragonfly.git
synced 2024-12-14 11:58:02 +00:00
Remove redundant redis files
This commit is contained in:
parent
7b9bad35e9
commit
f1c5e4d702
11 changed files with 8 additions and 5721 deletions
|
@ -8,8 +8,8 @@ else()
|
||||||
set(ZMALLOC_DEPS "")
|
set(ZMALLOC_DEPS "")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(redis_lib crc64.c crcspeed.c debug.c dict.c endianconv.c intset.c
|
add_library(redis_lib crc64.c crcspeed.c debug.c dict.c intset.c
|
||||||
listpack.c mt19937-64.c object.c lzf_c.c lzf_d.c sds.c sha256.c
|
listpack.c mt19937-64.c object.c lzf_c.c lzf_d.c sds.c
|
||||||
quicklist.c redis_aux.c siphash.c t_hash.c t_zset.c util.c ziplist.c ${ZMALLOC_SRC})
|
quicklist.c redis_aux.c siphash.c t_hash.c t_zset.c util.c ziplist.c ${ZMALLOC_SRC})
|
||||||
|
|
||||||
cxx_link(redis_lib ${ZMALLOC_DEPS})
|
cxx_link(redis_lib ${ZMALLOC_DEPS})
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
#ifndef __DICT_H
|
#ifndef __DICT_H
|
||||||
#define __DICT_H
|
#define __DICT_H
|
||||||
|
|
||||||
#include "mt19937-64.h"
|
#include "mt19937-64.h" // ROMAN: needed by dictGetFairRandomKey
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
|
@ -1,129 +0,0 @@
|
||||||
/* endinconv.c -- Endian conversions utilities.
|
|
||||||
*
|
|
||||||
* This functions are never called directly, but always using the macros
|
|
||||||
* defined into endianconv.h, this way we define everything is a non-operation
|
|
||||||
* if the arch is already little endian.
|
|
||||||
*
|
|
||||||
* Redis tries to encode everything as little endian (but a few things that need
|
|
||||||
* to be backward compatible are still in big endian) because most of the
|
|
||||||
* production environments are little endian, and we have a lot of conversions
|
|
||||||
* in a few places because ziplists, intsets, zipmaps, need to be endian-neutral
|
|
||||||
* even in memory, since they are serialized on RDB files directly with a single
|
|
||||||
* write(2) without other additional steps.
|
|
||||||
*
|
|
||||||
* ----------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* Copyright (c) 2011-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of Redis nor the names of its contributors may be used
|
|
||||||
* to endorse or promote products derived from this software without
|
|
||||||
* specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/* Toggle the 16 bit unsigned integer pointed by *p from little endian to
|
|
||||||
* big endian */
|
|
||||||
void memrev16(void *p) {
|
|
||||||
unsigned char *x = p, t;
|
|
||||||
|
|
||||||
t = x[0];
|
|
||||||
x[0] = x[1];
|
|
||||||
x[1] = t;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Toggle the 32 bit unsigned integer pointed by *p from little endian to
|
|
||||||
* big endian */
|
|
||||||
void memrev32(void *p) {
|
|
||||||
unsigned char *x = p, t;
|
|
||||||
|
|
||||||
t = x[0];
|
|
||||||
x[0] = x[3];
|
|
||||||
x[3] = t;
|
|
||||||
t = x[1];
|
|
||||||
x[1] = x[2];
|
|
||||||
x[2] = t;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Toggle the 64 bit unsigned integer pointed by *p from little endian to
|
|
||||||
* big endian */
|
|
||||||
void memrev64(void *p) {
|
|
||||||
unsigned char *x = p, t;
|
|
||||||
|
|
||||||
t = x[0];
|
|
||||||
x[0] = x[7];
|
|
||||||
x[7] = t;
|
|
||||||
t = x[1];
|
|
||||||
x[1] = x[6];
|
|
||||||
x[6] = t;
|
|
||||||
t = x[2];
|
|
||||||
x[2] = x[5];
|
|
||||||
x[5] = t;
|
|
||||||
t = x[3];
|
|
||||||
x[3] = x[4];
|
|
||||||
x[4] = t;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t intrev16(uint16_t v) {
|
|
||||||
memrev16(&v);
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t intrev32(uint32_t v) {
|
|
||||||
memrev32(&v);
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t intrev64(uint64_t v) {
|
|
||||||
memrev64(&v);
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef REDIS_TEST
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#define UNUSED(x) (void)(x)
|
|
||||||
int endianconvTest(int argc, char *argv[], int flags) {
|
|
||||||
char buf[32];
|
|
||||||
|
|
||||||
UNUSED(argc);
|
|
||||||
UNUSED(argv);
|
|
||||||
UNUSED(flags);
|
|
||||||
|
|
||||||
sprintf(buf,"ciaoroma");
|
|
||||||
memrev16(buf);
|
|
||||||
printf("%s\n", buf);
|
|
||||||
|
|
||||||
sprintf(buf,"ciaoroma");
|
|
||||||
memrev32(buf);
|
|
||||||
printf("%s\n", buf);
|
|
||||||
|
|
||||||
sprintf(buf,"ciaoroma");
|
|
||||||
memrev64(buf);
|
|
||||||
printf("%s\n", buf);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
|
@ -533,73 +533,6 @@ void decrRefCountVoid(void *o) {
|
||||||
decrRefCount(o);
|
decrRefCount(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ROMAN_CLIENT_DISABLE
|
|
||||||
|
|
||||||
/* See dismissObject() */
|
|
||||||
void dismissStreamObject(robj *o, size_t size_hint) {
|
|
||||||
stream *s = o->ptr;
|
|
||||||
rax *rax = s->rax;
|
|
||||||
if (raxSize(rax) == 0) return;
|
|
||||||
|
|
||||||
/* Iterate only on stream entries, although size_hint may include serialized
|
|
||||||
* consumer groups info, but usually, stream entries take up most of
|
|
||||||
* the space. */
|
|
||||||
if (size_hint / raxSize(rax) >= server.page_size) {
|
|
||||||
raxIterator ri;
|
|
||||||
raxStart(&ri,rax);
|
|
||||||
raxSeek(&ri,"^",NULL,0);
|
|
||||||
while (raxNext(&ri)) {
|
|
||||||
dismissMemory(ri.data, lpBytes(ri.data));
|
|
||||||
}
|
|
||||||
raxStop(&ri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* When creating a snapshot in a fork child process, the main process and child
|
|
||||||
* process share the same physical memory pages, and if / when the parent
|
|
||||||
* modifies any keys due to write traffic, it'll cause CoW which consume
|
|
||||||
* physical memory. In the child process, after serializing the key and value,
|
|
||||||
* the data is definitely not accessed again, so to avoid unnecessary CoW, we
|
|
||||||
* try to release their memory back to OS. see dismissMemory().
|
|
||||||
*
|
|
||||||
* Because of the cost of iterating all node/field/member/entry of complex data
|
|
||||||
* types, we iterate and dismiss them only when approximate average we estimate
|
|
||||||
* the size of an individual allocation is more than a page size of OS.
|
|
||||||
* 'size_hint' is the size of serialized value. This method is not accurate, but
|
|
||||||
* it can reduce unnecessary iteration for complex data types that are probably
|
|
||||||
* not going to release any memory. */
|
|
||||||
void dismissObject(robj *o, size_t size_hint) {
|
|
||||||
/* madvise(MADV_DONTNEED) may not work if Transparent Huge Pages is enabled. */
|
|
||||||
if (server.thp_enabled) return;
|
|
||||||
|
|
||||||
/* Currently we use zmadvise_dontneed only when we use jemalloc with Linux.
|
|
||||||
* so we avoid these pointless loops when they're not going to do anything. */
|
|
||||||
#if defined(USE_JEMALLOC) && defined(__linux__)
|
|
||||||
if (o->refcount != 1) return;
|
|
||||||
switch(o->type) {
|
|
||||||
case OBJ_STRING: dismissStringObject(o); break;
|
|
||||||
case OBJ_LIST: dismissListObject(o, size_hint); break;
|
|
||||||
case OBJ_SET: dismissSetObject(o, size_hint); break;
|
|
||||||
case OBJ_ZSET: dismissZsetObject(o, size_hint); break;
|
|
||||||
case OBJ_HASH: dismissHashObject(o, size_hint); break;
|
|
||||||
case OBJ_STREAM: dismissStreamObject(o, size_hint); break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
UNUSED(o); UNUSED(size_hint);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int checkType(client *c, robj *o, int type) {
|
|
||||||
/* A NULL is considered an empty key */
|
|
||||||
if (o && o->type != type) {
|
|
||||||
addReplyErrorObject(c,shared.wrongtypeerr);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int isSdsRepresentableAsLongLong(sds s, long long *llval) {
|
int isSdsRepresentableAsLongLong(sds s, long long *llval) {
|
||||||
return string2ll(s,sdslen(s),llval) ? C_OK : C_ERR;
|
return string2ll(s,sdslen(s),llval) ? C_OK : C_ERR;
|
||||||
}
|
}
|
||||||
|
@ -764,138 +697,6 @@ int getLongLongFromObject(robj *o, long long *target) {
|
||||||
return C_OK;
|
return C_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ROMAN_CLIENT_DISABLE
|
|
||||||
|
|
||||||
int getDoubleFromObjectOrReply(client *c, robj *o, double *target, const char *msg) {
|
|
||||||
double value;
|
|
||||||
if (getDoubleFromObject(o, &value) != C_OK) {
|
|
||||||
if (msg != NULL) {
|
|
||||||
addReplyError(c,(char*)msg);
|
|
||||||
} else {
|
|
||||||
addReplyError(c,"value is not a valid float");
|
|
||||||
}
|
|
||||||
return C_ERR;
|
|
||||||
}
|
|
||||||
*target = value;
|
|
||||||
return C_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getLongDoubleFromObject(robj *o, long double *target) {
|
|
||||||
long double value;
|
|
||||||
|
|
||||||
if (o == NULL) {
|
|
||||||
value = 0;
|
|
||||||
} else {
|
|
||||||
serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
|
|
||||||
if (sdsEncodedObject(o)) {
|
|
||||||
if (!string2ld(o->ptr, sdslen(o->ptr), &value))
|
|
||||||
return C_ERR;
|
|
||||||
} else if (o->encoding == OBJ_ENCODING_INT) {
|
|
||||||
value = (long)o->ptr;
|
|
||||||
} else {
|
|
||||||
serverPanic("Unknown string encoding");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*target = value;
|
|
||||||
return C_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getLongDoubleFromObjectOrReply(client *c, robj *o, long double *target, const char *msg) {
|
|
||||||
long double value;
|
|
||||||
if (getLongDoubleFromObject(o, &value) != C_OK) {
|
|
||||||
if (msg != NULL) {
|
|
||||||
addReplyError(c,(char*)msg);
|
|
||||||
} else {
|
|
||||||
addReplyError(c,"value is not a valid float");
|
|
||||||
}
|
|
||||||
return C_ERR;
|
|
||||||
}
|
|
||||||
*target = value;
|
|
||||||
return C_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getLongLongFromObject(robj *o, long long *target) {
|
|
||||||
long long value;
|
|
||||||
|
|
||||||
if (o == NULL) {
|
|
||||||
value = 0;
|
|
||||||
} else {
|
|
||||||
serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
|
|
||||||
if (sdsEncodedObject(o)) {
|
|
||||||
if (string2ll(o->ptr,sdslen(o->ptr),&value) == 0) return C_ERR;
|
|
||||||
} else if (o->encoding == OBJ_ENCODING_INT) {
|
|
||||||
value = (long)o->ptr;
|
|
||||||
} else {
|
|
||||||
serverPanic("Unknown string encoding");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (target) *target = value;
|
|
||||||
return C_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getLongLongFromObjectOrReply(client *c, robj *o, long long *target, const char *msg) {
|
|
||||||
long long value;
|
|
||||||
if (getLongLongFromObject(o, &value) != C_OK) {
|
|
||||||
if (msg != NULL) {
|
|
||||||
addReplyError(c,(char*)msg);
|
|
||||||
} else {
|
|
||||||
addReplyError(c,"value is not an integer or out of range");
|
|
||||||
}
|
|
||||||
return C_ERR;
|
|
||||||
}
|
|
||||||
*target = value;
|
|
||||||
return C_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getLongFromObjectOrReply(client *c, robj *o, long *target, const char *msg) {
|
|
||||||
long long value;
|
|
||||||
|
|
||||||
if (getLongLongFromObjectOrReply(c, o, &value, msg) != C_OK) return C_ERR;
|
|
||||||
if (value < LONG_MIN || value > LONG_MAX) {
|
|
||||||
if (msg != NULL) {
|
|
||||||
addReplyError(c,(char*)msg);
|
|
||||||
} else {
|
|
||||||
addReplyError(c,"value is out of range");
|
|
||||||
}
|
|
||||||
return C_ERR;
|
|
||||||
}
|
|
||||||
*target = value;
|
|
||||||
return C_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getRangeLongFromObjectOrReply(client *c, robj *o, long min, long max, long *target, const char *msg) {
|
|
||||||
if (getLongFromObjectOrReply(c, o, target, msg) != C_OK) return C_ERR;
|
|
||||||
if (*target < min || *target > max) {
|
|
||||||
if (msg != NULL) {
|
|
||||||
addReplyError(c,(char*)msg);
|
|
||||||
} else {
|
|
||||||
addReplyErrorFormat(c,"value is out of range, value must between %ld and %ld", min, max);
|
|
||||||
}
|
|
||||||
return C_ERR;
|
|
||||||
}
|
|
||||||
return C_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getPositiveLongFromObjectOrReply(client *c, robj *o, long *target, const char *msg) {
|
|
||||||
if (msg) {
|
|
||||||
return getRangeLongFromObjectOrReply(c, o, 0, LONG_MAX, target, msg);
|
|
||||||
} else {
|
|
||||||
return getRangeLongFromObjectOrReply(c, o, 0, LONG_MAX, target, "value is out of range, must be positive");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int getIntFromObjectOrReply(client *c, robj *o, int *target, const char *msg) {
|
|
||||||
long value;
|
|
||||||
|
|
||||||
if (getRangeLongFromObjectOrReply(c, o, INT_MIN, INT_MAX, &value, msg) != C_OK)
|
|
||||||
return C_ERR;
|
|
||||||
|
|
||||||
*target = value;
|
|
||||||
return C_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const char *strEncoding(int encoding) {
|
const char *strEncoding(int encoding) {
|
||||||
switch(encoding) {
|
switch(encoding) {
|
||||||
case OBJ_ENCODING_RAW: return "raw";
|
case OBJ_ENCODING_RAW: return "raw";
|
||||||
|
@ -1428,244 +1229,3 @@ int objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ROMAN_CLIENT_DISABLE
|
|
||||||
|
|
||||||
/* ======================= The OBJECT and MEMORY commands =================== */
|
|
||||||
|
|
||||||
/* This is a helper function for the OBJECT command. We need to lookup keys
|
|
||||||
* without any modification of LRU or other parameters. */
|
|
||||||
robj *objectCommandLookup(client *c, robj *key) {
|
|
||||||
return lookupKeyReadWithFlags(c->db,key,LOOKUP_NOTOUCH|LOOKUP_NONOTIFY);
|
|
||||||
}
|
|
||||||
|
|
||||||
robj *objectCommandLookupOrReply(client *c, robj *key, robj *reply) {
|
|
||||||
robj *o = objectCommandLookup(c,key);
|
|
||||||
if (!o) addReplyOrErrorObject(c, reply);
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Object command allows to inspect the internals of a Redis Object.
|
|
||||||
* Usage: OBJECT <refcount|encoding|idletime|freq> <key> */
|
|
||||||
void objectCommand(client *c) {
|
|
||||||
robj *o;
|
|
||||||
|
|
||||||
if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) {
|
|
||||||
const char *help[] = {
|
|
||||||
"ENCODING <key>",
|
|
||||||
" Return the kind of internal representation used in order to store the value",
|
|
||||||
" associated with a <key>.",
|
|
||||||
"FREQ <key>",
|
|
||||||
" Return the access frequency index of the <key>. The returned integer is",
|
|
||||||
" proportional to the logarithm of the recent access frequency of the key.",
|
|
||||||
"IDLETIME <key>",
|
|
||||||
" Return the idle time of the <key>, that is the approximated number of",
|
|
||||||
" seconds elapsed since the last access to the key.",
|
|
||||||
"REFCOUNT <key>",
|
|
||||||
" Return the number of references of the value associated with the specified",
|
|
||||||
" <key>.",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
addReplyHelp(c, help);
|
|
||||||
} else if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) {
|
|
||||||
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))
|
|
||||||
== NULL) return;
|
|
||||||
addReplyLongLong(c,o->refcount);
|
|
||||||
} else if (!strcasecmp(c->argv[1]->ptr,"encoding") && c->argc == 3) {
|
|
||||||
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))
|
|
||||||
== NULL) return;
|
|
||||||
addReplyBulkCString(c,strEncoding(o->encoding));
|
|
||||||
} else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) {
|
|
||||||
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))
|
|
||||||
== NULL) return;
|
|
||||||
if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
|
|
||||||
addReplyError(c,"An LFU maxmemory policy is selected, idle time not tracked. Please note that when switching between policies at runtime LRU and LFU data will take some time to adjust.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
addReplyLongLong(c,estimateObjectIdleTime(o)/1000);
|
|
||||||
} else if (!strcasecmp(c->argv[1]->ptr,"freq") && c->argc == 3) {
|
|
||||||
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.null[c->resp]))
|
|
||||||
== NULL) return;
|
|
||||||
if (!(server.maxmemory_policy & MAXMEMORY_FLAG_LFU)) {
|
|
||||||
addReplyError(c,"An LFU maxmemory policy is not selected, access frequency not tracked. Please note that when switching between policies at runtime LRU and LFU data will take some time to adjust.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* LFUDecrAndReturn should be called
|
|
||||||
* in case of the key has not been accessed for a long time,
|
|
||||||
* because we update the access time only
|
|
||||||
* when the key is read or overwritten. */
|
|
||||||
addReplyLongLong(c,LFUDecrAndReturn(o));
|
|
||||||
} else {
|
|
||||||
addReplySubcommandSyntaxError(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The memory command will eventually be a complete interface for the
|
|
||||||
* memory introspection capabilities of Redis.
|
|
||||||
*
|
|
||||||
* Usage: MEMORY usage <key> */
|
|
||||||
void memoryCommand(client *c) {
|
|
||||||
if (!strcasecmp(c->argv[1]->ptr,"help") && c->argc == 2) {
|
|
||||||
const char *help[] = {
|
|
||||||
"DOCTOR",
|
|
||||||
" Return memory problems reports.",
|
|
||||||
"MALLOC-STATS",
|
|
||||||
" Return internal statistics report from the memory allocator.",
|
|
||||||
"PURGE",
|
|
||||||
" Attempt to purge dirty pages for reclamation by the allocator.",
|
|
||||||
"STATS",
|
|
||||||
" Return information about the memory usage of the server.",
|
|
||||||
"USAGE <key> [SAMPLES <count>]",
|
|
||||||
" Return memory in bytes used by <key> and its value. Nested values are",
|
|
||||||
" sampled up to <count> times (default: 5, 0 means sample all).",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
addReplyHelp(c, help);
|
|
||||||
} else if (!strcasecmp(c->argv[1]->ptr,"usage") && c->argc >= 3) {
|
|
||||||
dictEntry *de;
|
|
||||||
long long samples = OBJ_COMPUTE_SIZE_DEF_SAMPLES;
|
|
||||||
for (int j = 3; j < c->argc; j++) {
|
|
||||||
if (!strcasecmp(c->argv[j]->ptr,"samples") &&
|
|
||||||
j+1 < c->argc)
|
|
||||||
{
|
|
||||||
if (getLongLongFromObjectOrReply(c,c->argv[j+1],&samples,NULL)
|
|
||||||
== C_ERR) return;
|
|
||||||
if (samples < 0) {
|
|
||||||
addReplyErrorObject(c,shared.syntaxerr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (samples == 0) samples = LLONG_MAX;
|
|
||||||
j++; /* skip option argument. */
|
|
||||||
} else {
|
|
||||||
addReplyErrorObject(c,shared.syntaxerr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((de = dictFind(c->db->dict,c->argv[2]->ptr)) == NULL) {
|
|
||||||
addReplyNull(c);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
size_t usage = objectComputeSize(c->argv[2],dictGetVal(de),samples,c->db->id);
|
|
||||||
usage += sdsZmallocSize(dictGetKey(de));
|
|
||||||
usage += sizeof(dictEntry);
|
|
||||||
addReplyLongLong(c,usage);
|
|
||||||
} else if (!strcasecmp(c->argv[1]->ptr,"stats") && c->argc == 2) {
|
|
||||||
struct redisMemOverhead *mh = getMemoryOverheadData();
|
|
||||||
|
|
||||||
addReplyMapLen(c,26+mh->num_dbs);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"peak.allocated");
|
|
||||||
addReplyLongLong(c,mh->peak_allocated);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"total.allocated");
|
|
||||||
addReplyLongLong(c,mh->total_allocated);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"startup.allocated");
|
|
||||||
addReplyLongLong(c,mh->startup_allocated);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"replication.backlog");
|
|
||||||
addReplyLongLong(c,mh->repl_backlog);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"clients.slaves");
|
|
||||||
addReplyLongLong(c,mh->clients_slaves);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"clients.normal");
|
|
||||||
addReplyLongLong(c,mh->clients_normal);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"aof.buffer");
|
|
||||||
addReplyLongLong(c,mh->aof_buffer);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"lua.caches");
|
|
||||||
addReplyLongLong(c,mh->lua_caches);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"functions.caches");
|
|
||||||
addReplyLongLong(c,mh->functions_caches);
|
|
||||||
|
|
||||||
for (size_t j = 0; j < mh->num_dbs; j++) {
|
|
||||||
char dbname[32];
|
|
||||||
snprintf(dbname,sizeof(dbname),"db.%zd",mh->db[j].dbid);
|
|
||||||
addReplyBulkCString(c,dbname);
|
|
||||||
addReplyMapLen(c,2);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"overhead.hashtable.main");
|
|
||||||
addReplyLongLong(c,mh->db[j].overhead_ht_main);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"overhead.hashtable.expires");
|
|
||||||
addReplyLongLong(c,mh->db[j].overhead_ht_expires);
|
|
||||||
}
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"overhead.total");
|
|
||||||
addReplyLongLong(c,mh->overhead_total);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"keys.count");
|
|
||||||
addReplyLongLong(c,mh->total_keys);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"keys.bytes-per-key");
|
|
||||||
addReplyLongLong(c,mh->bytes_per_key);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"dataset.bytes");
|
|
||||||
addReplyLongLong(c,mh->dataset);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"dataset.percentage");
|
|
||||||
addReplyDouble(c,mh->dataset_perc);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"peak.percentage");
|
|
||||||
addReplyDouble(c,mh->peak_perc);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"allocator.allocated");
|
|
||||||
addReplyLongLong(c,server.cron_malloc_stats.allocator_allocated);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"allocator.active");
|
|
||||||
addReplyLongLong(c,server.cron_malloc_stats.allocator_active);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"allocator.resident");
|
|
||||||
addReplyLongLong(c,server.cron_malloc_stats.allocator_resident);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"allocator-fragmentation.ratio");
|
|
||||||
addReplyDouble(c,mh->allocator_frag);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"allocator-fragmentation.bytes");
|
|
||||||
addReplyLongLong(c,mh->allocator_frag_bytes);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"allocator-rss.ratio");
|
|
||||||
addReplyDouble(c,mh->allocator_rss);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"allocator-rss.bytes");
|
|
||||||
addReplyLongLong(c,mh->allocator_rss_bytes);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"rss-overhead.ratio");
|
|
||||||
addReplyDouble(c,mh->rss_extra);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"rss-overhead.bytes");
|
|
||||||
addReplyLongLong(c,mh->rss_extra_bytes);
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"fragmentation"); /* this is the total RSS overhead, including fragmentation */
|
|
||||||
addReplyDouble(c,mh->total_frag); /* it is kept here for backwards compatibility */
|
|
||||||
|
|
||||||
addReplyBulkCString(c,"fragmentation.bytes");
|
|
||||||
addReplyLongLong(c,mh->total_frag_bytes);
|
|
||||||
|
|
||||||
freeMemoryOverheadData(mh);
|
|
||||||
} else if (!strcasecmp(c->argv[1]->ptr,"malloc-stats") && c->argc == 2) {
|
|
||||||
#if defined(USE_JEMALLOC)
|
|
||||||
sds info = sdsempty();
|
|
||||||
je_malloc_stats_print(inputCatSds, &info, NULL);
|
|
||||||
addReplyVerbatim(c,info,sdslen(info),"txt");
|
|
||||||
sdsfree(info);
|
|
||||||
#else
|
|
||||||
addReplyBulkCString(c,"Stats not supported for the current allocator");
|
|
||||||
#endif
|
|
||||||
} else if (!strcasecmp(c->argv[1]->ptr,"doctor") && c->argc == 2) {
|
|
||||||
sds report = getMemoryDoctorReport();
|
|
||||||
addReplyVerbatim(c,report,sdslen(report),"txt");
|
|
||||||
sdsfree(report);
|
|
||||||
} else if (!strcasecmp(c->argv[1]->ptr,"purge") && c->argc == 2) {
|
|
||||||
if (jemalloc_purge() == 0)
|
|
||||||
addReply(c, shared.ok);
|
|
||||||
else
|
|
||||||
addReplyError(c, "Error purging dirty pages");
|
|
||||||
} else {
|
|
||||||
addReplySubcommandSyntaxError(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,163 +0,0 @@
|
||||||
/*********************************************************************
|
|
||||||
* Filename: sha256.c
|
|
||||||
* Author: Brad Conte (brad AT bradconte.com)
|
|
||||||
* Copyright:
|
|
||||||
* Disclaimer: This code is presented "as is" without any guarantees.
|
|
||||||
* Details: Implementation of the SHA-256 hashing algorithm.
|
|
||||||
SHA-256 is one of the three algorithms in the SHA2
|
|
||||||
specification. The others, SHA-384 and SHA-512, are not
|
|
||||||
offered in this implementation.
|
|
||||||
Algorithm specification can be found here:
|
|
||||||
* http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf
|
|
||||||
This implementation uses little endian byte order.
|
|
||||||
*********************************************************************/
|
|
||||||
|
|
||||||
/*************************** HEADER FILES ***************************/
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "sha256.h"
|
|
||||||
|
|
||||||
/****************************** MACROS ******************************/
|
|
||||||
#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b))))
|
|
||||||
#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b))))
|
|
||||||
|
|
||||||
#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
|
|
||||||
#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
|
|
||||||
#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))
|
|
||||||
#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))
|
|
||||||
#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))
|
|
||||||
#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))
|
|
||||||
|
|
||||||
/**************************** VARIABLES *****************************/
|
|
||||||
static const WORD k[64] = {
|
|
||||||
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
|
|
||||||
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
|
|
||||||
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
|
|
||||||
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
|
|
||||||
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
|
|
||||||
0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
|
|
||||||
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
|
|
||||||
0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
|
|
||||||
};
|
|
||||||
|
|
||||||
/*********************** FUNCTION DEFINITIONS ***********************/
|
|
||||||
void sha256_transform(SHA256_CTX *ctx, const BYTE data[])
|
|
||||||
{
|
|
||||||
WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
|
|
||||||
|
|
||||||
for (i = 0, j = 0; i < 16; ++i, j += 4) {
|
|
||||||
m[i] = ((WORD) data[j + 0] << 24) |
|
|
||||||
((WORD) data[j + 1] << 16) |
|
|
||||||
((WORD) data[j + 2] << 8) |
|
|
||||||
((WORD) data[j + 3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( ; i < 64; ++i)
|
|
||||||
m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
|
|
||||||
|
|
||||||
a = ctx->state[0];
|
|
||||||
b = ctx->state[1];
|
|
||||||
c = ctx->state[2];
|
|
||||||
d = ctx->state[3];
|
|
||||||
e = ctx->state[4];
|
|
||||||
f = ctx->state[5];
|
|
||||||
g = ctx->state[6];
|
|
||||||
h = ctx->state[7];
|
|
||||||
|
|
||||||
for (i = 0; i < 64; ++i) {
|
|
||||||
t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i];
|
|
||||||
t2 = EP0(a) + MAJ(a,b,c);
|
|
||||||
h = g;
|
|
||||||
g = f;
|
|
||||||
f = e;
|
|
||||||
e = d + t1;
|
|
||||||
d = c;
|
|
||||||
c = b;
|
|
||||||
b = a;
|
|
||||||
a = t1 + t2;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->state[0] += a;
|
|
||||||
ctx->state[1] += b;
|
|
||||||
ctx->state[2] += c;
|
|
||||||
ctx->state[3] += d;
|
|
||||||
ctx->state[4] += e;
|
|
||||||
ctx->state[5] += f;
|
|
||||||
ctx->state[6] += g;
|
|
||||||
ctx->state[7] += h;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sha256_init(SHA256_CTX *ctx)
|
|
||||||
{
|
|
||||||
ctx->datalen = 0;
|
|
||||||
ctx->bitlen = 0;
|
|
||||||
ctx->state[0] = 0x6a09e667;
|
|
||||||
ctx->state[1] = 0xbb67ae85;
|
|
||||||
ctx->state[2] = 0x3c6ef372;
|
|
||||||
ctx->state[3] = 0xa54ff53a;
|
|
||||||
ctx->state[4] = 0x510e527f;
|
|
||||||
ctx->state[5] = 0x9b05688c;
|
|
||||||
ctx->state[6] = 0x1f83d9ab;
|
|
||||||
ctx->state[7] = 0x5be0cd19;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len)
|
|
||||||
{
|
|
||||||
WORD i;
|
|
||||||
|
|
||||||
for (i = 0; i < len; ++i) {
|
|
||||||
ctx->data[ctx->datalen] = data[i];
|
|
||||||
ctx->datalen++;
|
|
||||||
if (ctx->datalen == 64) {
|
|
||||||
sha256_transform(ctx, ctx->data);
|
|
||||||
ctx->bitlen += 512;
|
|
||||||
ctx->datalen = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sha256_final(SHA256_CTX *ctx, BYTE hash[])
|
|
||||||
{
|
|
||||||
WORD i;
|
|
||||||
|
|
||||||
i = ctx->datalen;
|
|
||||||
|
|
||||||
// Pad whatever data is left in the buffer.
|
|
||||||
if (ctx->datalen < 56) {
|
|
||||||
ctx->data[i++] = 0x80;
|
|
||||||
while (i < 56)
|
|
||||||
ctx->data[i++] = 0x00;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ctx->data[i++] = 0x80;
|
|
||||||
while (i < 64)
|
|
||||||
ctx->data[i++] = 0x00;
|
|
||||||
sha256_transform(ctx, ctx->data);
|
|
||||||
memset(ctx->data, 0, 56);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append to the padding the total message's length in bits and transform.
|
|
||||||
ctx->bitlen += ctx->datalen * 8;
|
|
||||||
ctx->data[63] = ctx->bitlen;
|
|
||||||
ctx->data[62] = ctx->bitlen >> 8;
|
|
||||||
ctx->data[61] = ctx->bitlen >> 16;
|
|
||||||
ctx->data[60] = ctx->bitlen >> 24;
|
|
||||||
ctx->data[59] = ctx->bitlen >> 32;
|
|
||||||
ctx->data[58] = ctx->bitlen >> 40;
|
|
||||||
ctx->data[57] = ctx->bitlen >> 48;
|
|
||||||
ctx->data[56] = ctx->bitlen >> 56;
|
|
||||||
sha256_transform(ctx, ctx->data);
|
|
||||||
|
|
||||||
// Since this implementation uses little endian byte ordering and SHA uses big endian,
|
|
||||||
// reverse all the bytes when copying the final state to the output hash.
|
|
||||||
for (i = 0; i < 4; ++i) {
|
|
||||||
hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
|
|
||||||
hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
|
|
||||||
hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
|
|
||||||
hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
|
|
||||||
hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
|
|
||||||
hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;
|
|
||||||
hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;
|
|
||||||
hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
/*********************************************************************
|
|
||||||
* Filename: sha256.h
|
|
||||||
* Author: Brad Conte (brad AT bradconte.com)
|
|
||||||
* Copyright:
|
|
||||||
* Disclaimer: This code is presented "as is" without any guarantees.
|
|
||||||
* Details: Defines the API for the corresponding SHA256 implementation.
|
|
||||||
*********************************************************************/
|
|
||||||
|
|
||||||
#ifndef SHA256_H
|
|
||||||
#define SHA256_H
|
|
||||||
|
|
||||||
/*************************** HEADER FILES ***************************/
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/****************************** MACROS ******************************/
|
|
||||||
#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest
|
|
||||||
|
|
||||||
/**************************** DATA TYPES ****************************/
|
|
||||||
typedef uint8_t BYTE; // 8-bit byte
|
|
||||||
typedef uint32_t WORD; // 32-bit word
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
BYTE data[64];
|
|
||||||
WORD datalen;
|
|
||||||
unsigned long long bitlen;
|
|
||||||
WORD state[8];
|
|
||||||
} SHA256_CTX;
|
|
||||||
|
|
||||||
/*********************** FUNCTION DECLARATIONS **********************/
|
|
||||||
void sha256_init(SHA256_CTX *ctx);
|
|
||||||
void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len);
|
|
||||||
void sha256_final(SHA256_CTX *ctx, BYTE hash[]);
|
|
||||||
|
|
||||||
#endif // SHA256_H
|
|
|
@ -448,19 +448,6 @@ sds hashTypeCurrentObjectNewSds(hashTypeIterator *hi, int what) {
|
||||||
return sdsfromlonglong(vll);
|
return sdsfromlonglong(vll);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ROMAN_CLIENT_DISABLE
|
|
||||||
robj *hashTypeLookupWriteOrCreate(client *c, robj *key) {
|
|
||||||
robj *o = lookupKeyWrite(c->db,key);
|
|
||||||
if (checkType(c,o,OBJ_HASH)) return NULL;
|
|
||||||
|
|
||||||
if (o == NULL) {
|
|
||||||
o = createHashObject();
|
|
||||||
dbAdd(c->db,key,o);
|
|
||||||
}
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void hashTypeConvertListpack(robj *o, int enc) {
|
void hashTypeConvertListpack(robj *o, int enc) {
|
||||||
serverAssert(o->encoding == OBJ_ENCODING_LISTPACK);
|
serverAssert(o->encoding == OBJ_ENCODING_LISTPACK);
|
||||||
|
|
||||||
|
@ -561,16 +548,6 @@ sds hashSdsFromListpackEntry(listpackEntry *e) {
|
||||||
return e->sval ? sdsnewlen(e->sval, e->slen) : sdsfromlonglong(e->lval);
|
return e->sval ? sdsnewlen(e->sval, e->slen) : sdsfromlonglong(e->lval);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ROMAN_CLIENT_DISABLE
|
|
||||||
/* Reply with bulk string from the listpack entry. */
|
|
||||||
void hashReplyFromListpackEntry(client *c, listpackEntry *e) {
|
|
||||||
if (e->sval)
|
|
||||||
addReplyBulkCBuffer(c, e->sval, e->slen);
|
|
||||||
else
|
|
||||||
addReplyBulkLongLong(c, e->lval);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Return random element from a non empty hash.
|
/* Return random element from a non empty hash.
|
||||||
* 'key' and 'val' will be set to hold the element.
|
* 'key' and 'val' will be set to hold the element.
|
||||||
|
@ -593,579 +570,3 @@ void hashTypeRandomElement(robj *hashobj, unsigned long hashsize, listpackEntry
|
||||||
serverPanic("Unknown hash encoding");
|
serverPanic("Unknown hash encoding");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ROMAN_CLIENT_DISABLE
|
|
||||||
/*-----------------------------------------------------------------------------
|
|
||||||
* Hash type commands
|
|
||||||
*----------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
void hsetnxCommand(client *c) {
|
|
||||||
robj *o;
|
|
||||||
if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
|
|
||||||
|
|
||||||
if (hashTypeExists(o, c->argv[2]->ptr)) {
|
|
||||||
addReply(c, shared.czero);
|
|
||||||
} else {
|
|
||||||
hashTypeTryConversion(o,c->argv,2,3);
|
|
||||||
hashTypeSet(o,c->argv[2]->ptr,c->argv[3]->ptr,HASH_SET_COPY);
|
|
||||||
addReply(c, shared.cone);
|
|
||||||
signalModifiedKey(c,c->db,c->argv[1]);
|
|
||||||
notifyKeyspaceEvent(NOTIFY_HASH,"hset",c->argv[1],c->db->id);
|
|
||||||
server.dirty++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void hsetCommand(client *c) {
|
|
||||||
int i, created = 0;
|
|
||||||
robj *o;
|
|
||||||
|
|
||||||
if ((c->argc % 2) == 1) {
|
|
||||||
addReplyErrorArity(c);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
|
|
||||||
hashTypeTryConversion(o,c->argv,2,c->argc-1);
|
|
||||||
|
|
||||||
for (i = 2; i < c->argc; i += 2)
|
|
||||||
created += !hashTypeSet(o,c->argv[i]->ptr,c->argv[i+1]->ptr,HASH_SET_COPY);
|
|
||||||
|
|
||||||
/* HMSET (deprecated) and HSET return value is different. */
|
|
||||||
char *cmdname = c->argv[0]->ptr;
|
|
||||||
if (cmdname[1] == 's' || cmdname[1] == 'S') {
|
|
||||||
/* HSET */
|
|
||||||
addReplyLongLong(c, created);
|
|
||||||
} else {
|
|
||||||
/* HMSET */
|
|
||||||
addReply(c, shared.ok);
|
|
||||||
}
|
|
||||||
signalModifiedKey(c,c->db,c->argv[1]);
|
|
||||||
notifyKeyspaceEvent(NOTIFY_HASH,"hset",c->argv[1],c->db->id);
|
|
||||||
server.dirty += (c->argc - 2)/2;
|
|
||||||
}
|
|
||||||
|
|
||||||
void hincrbyCommand(client *c) {
|
|
||||||
long long value, incr, oldvalue;
|
|
||||||
robj *o;
|
|
||||||
sds new;
|
|
||||||
unsigned char *vstr;
|
|
||||||
unsigned int vlen;
|
|
||||||
|
|
||||||
if (getLongLongFromObjectOrReply(c,c->argv[3],&incr,NULL) != C_OK) return;
|
|
||||||
if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
|
|
||||||
if (hashTypeGetValue(o,c->argv[2]->ptr,&vstr,&vlen,&value) == C_OK) {
|
|
||||||
if (vstr) {
|
|
||||||
if (string2ll((char*)vstr,vlen,&value) == 0) {
|
|
||||||
addReplyError(c,"hash value is not an integer");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} /* Else hashTypeGetValue() already stored it into &value */
|
|
||||||
} else {
|
|
||||||
value = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
oldvalue = value;
|
|
||||||
if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) ||
|
|
||||||
(incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) {
|
|
||||||
addReplyError(c,"increment or decrement would overflow");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
value += incr;
|
|
||||||
new = sdsfromlonglong(value);
|
|
||||||
hashTypeSet(o,c->argv[2]->ptr,new,HASH_SET_TAKE_VALUE);
|
|
||||||
addReplyLongLong(c,value);
|
|
||||||
signalModifiedKey(c,c->db,c->argv[1]);
|
|
||||||
notifyKeyspaceEvent(NOTIFY_HASH,"hincrby",c->argv[1],c->db->id);
|
|
||||||
server.dirty++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void hincrbyfloatCommand(client *c) {
|
|
||||||
long double value, incr;
|
|
||||||
long long ll;
|
|
||||||
robj *o;
|
|
||||||
sds new;
|
|
||||||
unsigned char *vstr;
|
|
||||||
unsigned int vlen;
|
|
||||||
|
|
||||||
if (getLongDoubleFromObjectOrReply(c,c->argv[3],&incr,NULL) != C_OK) return;
|
|
||||||
if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
|
|
||||||
if (hashTypeGetValue(o,c->argv[2]->ptr,&vstr,&vlen,&ll) == C_OK) {
|
|
||||||
if (vstr) {
|
|
||||||
if (string2ld((char*)vstr,vlen,&value) == 0) {
|
|
||||||
addReplyError(c,"hash value is not a float");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
value = (long double)ll;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
value = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
value += incr;
|
|
||||||
if (isnan(value) || isinf(value)) {
|
|
||||||
addReplyError(c,"increment would produce NaN or Infinity");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char buf[MAX_LONG_DOUBLE_CHARS];
|
|
||||||
int len = ld2string(buf,sizeof(buf),value,LD_STR_HUMAN);
|
|
||||||
new = sdsnewlen(buf,len);
|
|
||||||
hashTypeSet(o,c->argv[2]->ptr,new,HASH_SET_TAKE_VALUE);
|
|
||||||
addReplyBulkCBuffer(c,buf,len);
|
|
||||||
signalModifiedKey(c,c->db,c->argv[1]);
|
|
||||||
notifyKeyspaceEvent(NOTIFY_HASH,"hincrbyfloat",c->argv[1],c->db->id);
|
|
||||||
server.dirty++;
|
|
||||||
|
|
||||||
/* Always replicate HINCRBYFLOAT as an HSET command with the final value
|
|
||||||
* in order to make sure that differences in float precision or formatting
|
|
||||||
* will not create differences in replicas or after an AOF restart. */
|
|
||||||
robj *newobj;
|
|
||||||
newobj = createRawStringObject(buf,len);
|
|
||||||
rewriteClientCommandArgument(c,0,shared.hset);
|
|
||||||
rewriteClientCommandArgument(c,3,newobj);
|
|
||||||
decrRefCount(newobj);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void addHashFieldToReply(client *c, robj *o, sds field) {
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (o == NULL) {
|
|
||||||
addReplyNull(c);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (o->encoding == OBJ_ENCODING_LISTPACK) {
|
|
||||||
unsigned char *vstr = NULL;
|
|
||||||
unsigned int vlen = UINT_MAX;
|
|
||||||
long long vll = LLONG_MAX;
|
|
||||||
|
|
||||||
ret = hashTypeGetFromListpack(o, field, &vstr, &vlen, &vll);
|
|
||||||
if (ret < 0) {
|
|
||||||
addReplyNull(c);
|
|
||||||
} else {
|
|
||||||
if (vstr) {
|
|
||||||
addReplyBulkCBuffer(c, vstr, vlen);
|
|
||||||
} else {
|
|
||||||
addReplyBulkLongLong(c, vll);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (o->encoding == OBJ_ENCODING_HT) {
|
|
||||||
sds value = hashTypeGetFromHashTable(o, field);
|
|
||||||
if (value == NULL)
|
|
||||||
addReplyNull(c);
|
|
||||||
else
|
|
||||||
addReplyBulkCBuffer(c, value, sdslen(value));
|
|
||||||
} else {
|
|
||||||
serverPanic("Unknown hash encoding");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void hgetCommand(client *c) {
|
|
||||||
robj *o;
|
|
||||||
|
|
||||||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp])) == NULL ||
|
|
||||||
checkType(c,o,OBJ_HASH)) return;
|
|
||||||
|
|
||||||
addHashFieldToReply(c, o, c->argv[2]->ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void hmgetCommand(client *c) {
|
|
||||||
robj *o;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* Don't abort when the key cannot be found. Non-existing keys are empty
|
|
||||||
* hashes, where HMGET should respond with a series of null bulks. */
|
|
||||||
o = lookupKeyRead(c->db, c->argv[1]);
|
|
||||||
if (checkType(c,o,OBJ_HASH)) return;
|
|
||||||
|
|
||||||
addReplyArrayLen(c, c->argc-2);
|
|
||||||
for (i = 2; i < c->argc; i++) {
|
|
||||||
addHashFieldToReply(c, o, c->argv[i]->ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void hdelCommand(client *c) {
|
|
||||||
robj *o;
|
|
||||||
int j, deleted = 0, keyremoved = 0;
|
|
||||||
|
|
||||||
if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
|
||||||
checkType(c,o,OBJ_HASH)) return;
|
|
||||||
|
|
||||||
for (j = 2; j < c->argc; j++) {
|
|
||||||
if (hashTypeDelete(o,c->argv[j]->ptr)) {
|
|
||||||
deleted++;
|
|
||||||
if (hashTypeLength(o) == 0) {
|
|
||||||
dbDelete(c->db,c->argv[1]);
|
|
||||||
keyremoved = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (deleted) {
|
|
||||||
signalModifiedKey(c,c->db,c->argv[1]);
|
|
||||||
notifyKeyspaceEvent(NOTIFY_HASH,"hdel",c->argv[1],c->db->id);
|
|
||||||
if (keyremoved)
|
|
||||||
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[1],
|
|
||||||
c->db->id);
|
|
||||||
server.dirty += deleted;
|
|
||||||
}
|
|
||||||
addReplyLongLong(c,deleted);
|
|
||||||
}
|
|
||||||
|
|
||||||
void hlenCommand(client *c) {
|
|
||||||
robj *o;
|
|
||||||
|
|
||||||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
|
||||||
checkType(c,o,OBJ_HASH)) return;
|
|
||||||
|
|
||||||
addReplyLongLong(c,hashTypeLength(o));
|
|
||||||
}
|
|
||||||
|
|
||||||
void hstrlenCommand(client *c) {
|
|
||||||
robj *o;
|
|
||||||
|
|
||||||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
|
||||||
checkType(c,o,OBJ_HASH)) return;
|
|
||||||
addReplyLongLong(c,hashTypeGetValueLength(o,c->argv[2]->ptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void addHashIteratorCursorToReply(client *c, hashTypeIterator *hi, int what) {
|
|
||||||
if (hi->encoding == OBJ_ENCODING_LISTPACK) {
|
|
||||||
unsigned char *vstr = NULL;
|
|
||||||
unsigned int vlen = UINT_MAX;
|
|
||||||
long long vll = LLONG_MAX;
|
|
||||||
|
|
||||||
hashTypeCurrentFromListpack(hi, what, &vstr, &vlen, &vll);
|
|
||||||
if (vstr)
|
|
||||||
addReplyBulkCBuffer(c, vstr, vlen);
|
|
||||||
else
|
|
||||||
addReplyBulkLongLong(c, vll);
|
|
||||||
} else if (hi->encoding == OBJ_ENCODING_HT) {
|
|
||||||
sds value = hashTypeCurrentFromHashTable(hi, what);
|
|
||||||
addReplyBulkCBuffer(c, value, sdslen(value));
|
|
||||||
} else {
|
|
||||||
serverPanic("Unknown hash encoding");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void genericHgetallCommand(client *c, int flags) {
|
|
||||||
robj *o;
|
|
||||||
hashTypeIterator *hi;
|
|
||||||
int length, count = 0;
|
|
||||||
|
|
||||||
robj *emptyResp = (flags & OBJ_HASH_KEY && flags & OBJ_HASH_VALUE) ?
|
|
||||||
shared.emptymap[c->resp] : shared.emptyarray;
|
|
||||||
if ((o = lookupKeyReadOrReply(c,c->argv[1],emptyResp))
|
|
||||||
== NULL || checkType(c,o,OBJ_HASH)) return;
|
|
||||||
|
|
||||||
/* We return a map if the user requested keys and values, like in the
|
|
||||||
* HGETALL case. Otherwise to use a flat array makes more sense. */
|
|
||||||
length = hashTypeLength(o);
|
|
||||||
if (flags & OBJ_HASH_KEY && flags & OBJ_HASH_VALUE) {
|
|
||||||
addReplyMapLen(c, length);
|
|
||||||
} else {
|
|
||||||
addReplyArrayLen(c, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
hi = hashTypeInitIterator(o);
|
|
||||||
while (hashTypeNext(hi) != C_ERR) {
|
|
||||||
if (flags & OBJ_HASH_KEY) {
|
|
||||||
addHashIteratorCursorToReply(c, hi, OBJ_HASH_KEY);
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
if (flags & OBJ_HASH_VALUE) {
|
|
||||||
addHashIteratorCursorToReply(c, hi, OBJ_HASH_VALUE);
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hashTypeReleaseIterator(hi);
|
|
||||||
|
|
||||||
/* Make sure we returned the right number of elements. */
|
|
||||||
if (flags & OBJ_HASH_KEY && flags & OBJ_HASH_VALUE) count /= 2;
|
|
||||||
serverAssert(count == length);
|
|
||||||
}
|
|
||||||
|
|
||||||
void hkeysCommand(client *c) {
|
|
||||||
genericHgetallCommand(c,OBJ_HASH_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
void hvalsCommand(client *c) {
|
|
||||||
genericHgetallCommand(c,OBJ_HASH_VALUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void hgetallCommand(client *c) {
|
|
||||||
genericHgetallCommand(c,OBJ_HASH_KEY|OBJ_HASH_VALUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void hexistsCommand(client *c) {
|
|
||||||
robj *o;
|
|
||||||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
|
||||||
checkType(c,o,OBJ_HASH)) return;
|
|
||||||
|
|
||||||
addReply(c, hashTypeExists(o,c->argv[2]->ptr) ? shared.cone : shared.czero);
|
|
||||||
}
|
|
||||||
|
|
||||||
void hscanCommand(client *c) {
|
|
||||||
robj *o;
|
|
||||||
unsigned long cursor;
|
|
||||||
|
|
||||||
if (parseScanCursorOrReply(c,c->argv[2],&cursor) == C_ERR) return;
|
|
||||||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == NULL ||
|
|
||||||
checkType(c,o,OBJ_HASH)) return;
|
|
||||||
scanGenericCommand(c,o,cursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void harndfieldReplyWithListpack(client *c, unsigned int count, listpackEntry *keys, listpackEntry *vals) {
|
|
||||||
for (unsigned long i = 0; i < count; i++) {
|
|
||||||
if (vals && c->resp > 2)
|
|
||||||
addReplyArrayLen(c,2);
|
|
||||||
if (keys[i].sval)
|
|
||||||
addReplyBulkCBuffer(c, keys[i].sval, keys[i].slen);
|
|
||||||
else
|
|
||||||
addReplyBulkLongLong(c, keys[i].lval);
|
|
||||||
if (vals) {
|
|
||||||
if (vals[i].sval)
|
|
||||||
addReplyBulkCBuffer(c, vals[i].sval, vals[i].slen);
|
|
||||||
else
|
|
||||||
addReplyBulkLongLong(c, vals[i].lval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* How many times bigger should be the hash compared to the requested size
|
|
||||||
* for us to not use the "remove elements" strategy? Read later in the
|
|
||||||
* implementation for more info. */
|
|
||||||
#define HRANDFIELD_SUB_STRATEGY_MUL 3
|
|
||||||
|
|
||||||
/* If client is trying to ask for a very large number of random elements,
|
|
||||||
* queuing may consume an unlimited amount of memory, so we want to limit
|
|
||||||
* the number of randoms per time. */
|
|
||||||
#define HRANDFIELD_RANDOM_SAMPLE_LIMIT 1000
|
|
||||||
|
|
||||||
void hrandfieldWithCountCommand(client *c, long l, int withvalues) {
|
|
||||||
unsigned long count, size;
|
|
||||||
int uniq = 1;
|
|
||||||
robj *hash;
|
|
||||||
|
|
||||||
if ((hash = lookupKeyReadOrReply(c,c->argv[1],shared.emptyarray))
|
|
||||||
== NULL || checkType(c,hash,OBJ_HASH)) return;
|
|
||||||
size = hashTypeLength(hash);
|
|
||||||
|
|
||||||
if(l >= 0) {
|
|
||||||
count = (unsigned long) l;
|
|
||||||
} else {
|
|
||||||
count = -l;
|
|
||||||
uniq = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If count is zero, serve it ASAP to avoid special cases later. */
|
|
||||||
if (count == 0) {
|
|
||||||
addReply(c,shared.emptyarray);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* CASE 1: The count was negative, so the extraction method is just:
|
|
||||||
* "return N random elements" sampling the whole set every time.
|
|
||||||
* This case is trivial and can be served without auxiliary data
|
|
||||||
* structures. This case is the only one that also needs to return the
|
|
||||||
* elements in random order. */
|
|
||||||
if (!uniq || count == 1) {
|
|
||||||
if (withvalues && c->resp == 2)
|
|
||||||
addReplyArrayLen(c, count*2);
|
|
||||||
else
|
|
||||||
addReplyArrayLen(c, count);
|
|
||||||
if (hash->encoding == OBJ_ENCODING_HT) {
|
|
||||||
sds key, value;
|
|
||||||
while (count--) {
|
|
||||||
dictEntry *de = dictGetFairRandomKey(hash->ptr);
|
|
||||||
key = dictGetKey(de);
|
|
||||||
value = dictGetVal(de);
|
|
||||||
if (withvalues && c->resp > 2)
|
|
||||||
addReplyArrayLen(c,2);
|
|
||||||
addReplyBulkCBuffer(c, key, sdslen(key));
|
|
||||||
if (withvalues)
|
|
||||||
addReplyBulkCBuffer(c, value, sdslen(value));
|
|
||||||
}
|
|
||||||
} else if (hash->encoding == OBJ_ENCODING_LISTPACK) {
|
|
||||||
listpackEntry *keys, *vals = NULL;
|
|
||||||
unsigned long limit, sample_count;
|
|
||||||
|
|
||||||
limit = count > HRANDFIELD_RANDOM_SAMPLE_LIMIT ? HRANDFIELD_RANDOM_SAMPLE_LIMIT : count;
|
|
||||||
keys = zmalloc(sizeof(listpackEntry)*limit);
|
|
||||||
if (withvalues)
|
|
||||||
vals = zmalloc(sizeof(listpackEntry)*limit);
|
|
||||||
while (count) {
|
|
||||||
sample_count = count > limit ? limit : count;
|
|
||||||
count -= sample_count;
|
|
||||||
lpRandomPairs(hash->ptr, sample_count, keys, vals);
|
|
||||||
harndfieldReplyWithListpack(c, sample_count, keys, vals);
|
|
||||||
}
|
|
||||||
zfree(keys);
|
|
||||||
zfree(vals);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initiate reply count, RESP3 responds with nested array, RESP2 with flat one. */
|
|
||||||
long reply_size = count < size ? count : size;
|
|
||||||
if (withvalues && c->resp == 2)
|
|
||||||
addReplyArrayLen(c, reply_size*2);
|
|
||||||
else
|
|
||||||
addReplyArrayLen(c, reply_size);
|
|
||||||
|
|
||||||
/* CASE 2:
|
|
||||||
* The number of requested elements is greater than the number of
|
|
||||||
* elements inside the hash: simply return the whole hash. */
|
|
||||||
if(count >= size) {
|
|
||||||
hashTypeIterator *hi = hashTypeInitIterator(hash);
|
|
||||||
while (hashTypeNext(hi) != C_ERR) {
|
|
||||||
if (withvalues && c->resp > 2)
|
|
||||||
addReplyArrayLen(c,2);
|
|
||||||
addHashIteratorCursorToReply(c, hi, OBJ_HASH_KEY);
|
|
||||||
if (withvalues)
|
|
||||||
addHashIteratorCursorToReply(c, hi, OBJ_HASH_VALUE);
|
|
||||||
}
|
|
||||||
hashTypeReleaseIterator(hi);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* CASE 3:
|
|
||||||
* The number of elements inside the hash is not greater than
|
|
||||||
* HRANDFIELD_SUB_STRATEGY_MUL times the number of requested elements.
|
|
||||||
* In this case we create a hash from scratch with all the elements, and
|
|
||||||
* subtract random elements to reach the requested number of elements.
|
|
||||||
*
|
|
||||||
* This is done because if the number of requested elements is just
|
|
||||||
* a bit less than the number of elements in the hash, the natural approach
|
|
||||||
* used into CASE 4 is highly inefficient. */
|
|
||||||
if (count*HRANDFIELD_SUB_STRATEGY_MUL > size) {
|
|
||||||
dict *d = dictCreate(&sdsReplyDictType);
|
|
||||||
dictExpand(d, size);
|
|
||||||
hashTypeIterator *hi = hashTypeInitIterator(hash);
|
|
||||||
|
|
||||||
/* Add all the elements into the temporary dictionary. */
|
|
||||||
while ((hashTypeNext(hi)) != C_ERR) {
|
|
||||||
int ret = DICT_ERR;
|
|
||||||
sds key, value = NULL;
|
|
||||||
|
|
||||||
key = hashTypeCurrentObjectNewSds(hi,OBJ_HASH_KEY);
|
|
||||||
if (withvalues)
|
|
||||||
value = hashTypeCurrentObjectNewSds(hi,OBJ_HASH_VALUE);
|
|
||||||
ret = dictAdd(d, key, value);
|
|
||||||
|
|
||||||
serverAssert(ret == DICT_OK);
|
|
||||||
}
|
|
||||||
serverAssert(dictSize(d) == size);
|
|
||||||
hashTypeReleaseIterator(hi);
|
|
||||||
|
|
||||||
/* Remove random elements to reach the right count. */
|
|
||||||
while (size > count) {
|
|
||||||
dictEntry *de;
|
|
||||||
de = dictGetFairRandomKey(d);
|
|
||||||
dictUnlink(d,dictGetKey(de));
|
|
||||||
sdsfree(dictGetKey(de));
|
|
||||||
sdsfree(dictGetVal(de));
|
|
||||||
dictFreeUnlinkedEntry(d,de);
|
|
||||||
size--;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reply with what's in the dict and release memory */
|
|
||||||
dictIterator *di;
|
|
||||||
dictEntry *de;
|
|
||||||
di = dictGetIterator(d);
|
|
||||||
while ((de = dictNext(di)) != NULL) {
|
|
||||||
sds key = dictGetKey(de);
|
|
||||||
sds value = dictGetVal(de);
|
|
||||||
if (withvalues && c->resp > 2)
|
|
||||||
addReplyArrayLen(c,2);
|
|
||||||
addReplyBulkSds(c, key);
|
|
||||||
if (withvalues)
|
|
||||||
addReplyBulkSds(c, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
dictReleaseIterator(di);
|
|
||||||
dictRelease(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* CASE 4: We have a big hash compared to the requested number of elements.
|
|
||||||
* In this case we can simply get random elements from the hash and add
|
|
||||||
* to the temporary hash, trying to eventually get enough unique elements
|
|
||||||
* to reach the specified count. */
|
|
||||||
else {
|
|
||||||
if (hash->encoding == OBJ_ENCODING_LISTPACK) {
|
|
||||||
/* it is inefficient to repeatedly pick one random element from a
|
|
||||||
* listpack. so we use this instead: */
|
|
||||||
listpackEntry *keys, *vals = NULL;
|
|
||||||
keys = zmalloc(sizeof(listpackEntry)*count);
|
|
||||||
if (withvalues)
|
|
||||||
vals = zmalloc(sizeof(listpackEntry)*count);
|
|
||||||
serverAssert(lpRandomPairsUnique(hash->ptr, count, keys, vals) == count);
|
|
||||||
harndfieldReplyWithListpack(c, count, keys, vals);
|
|
||||||
zfree(keys);
|
|
||||||
zfree(vals);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hashtable encoding (generic implementation) */
|
|
||||||
unsigned long added = 0;
|
|
||||||
listpackEntry key, value;
|
|
||||||
dict *d = dictCreate(&hashDictType);
|
|
||||||
dictExpand(d, count);
|
|
||||||
while(added < count) {
|
|
||||||
hashTypeRandomElement(hash, size, &key, withvalues? &value : NULL);
|
|
||||||
|
|
||||||
/* Try to add the object to the dictionary. If it already exists
|
|
||||||
* free it, otherwise increment the number of objects we have
|
|
||||||
* in the result dictionary. */
|
|
||||||
sds skey = hashSdsFromListpackEntry(&key);
|
|
||||||
if (dictAdd(d,skey,NULL) != DICT_OK) {
|
|
||||||
sdsfree(skey);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
added++;
|
|
||||||
|
|
||||||
/* We can reply right away, so that we don't need to store the value in the dict. */
|
|
||||||
if (withvalues && c->resp > 2)
|
|
||||||
addReplyArrayLen(c,2);
|
|
||||||
hashReplyFromListpackEntry(c, &key);
|
|
||||||
if (withvalues)
|
|
||||||
hashReplyFromListpackEntry(c, &value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Release memory */
|
|
||||||
dictRelease(d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* HRANDFIELD key [<count> [WITHVALUES]] */
|
|
||||||
void hrandfieldCommand(client *c) {
|
|
||||||
long l;
|
|
||||||
int withvalues = 0;
|
|
||||||
robj *hash;
|
|
||||||
listpackEntry ele;
|
|
||||||
|
|
||||||
if (c->argc >= 3) {
|
|
||||||
if (getLongFromObjectOrReply(c,c->argv[2],&l,NULL) != C_OK) return;
|
|
||||||
if (c->argc > 4 || (c->argc == 4 && strcasecmp(c->argv[3]->ptr,"withvalues"))) {
|
|
||||||
addReplyErrorObject(c,shared.syntaxerr);
|
|
||||||
return;
|
|
||||||
} else if (c->argc == 4)
|
|
||||||
withvalues = 1;
|
|
||||||
hrandfieldWithCountCommand(c, l, withvalues);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handle variant without <count> argument. Reply with simple bulk string */
|
|
||||||
if ((hash = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp]))== NULL ||
|
|
||||||
checkType(c,hash,OBJ_HASH)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
hashTypeRandomElement(hash,hashTypeLength(hash),&ele,NULL);
|
|
||||||
hashReplyFromListpackEntry(c, &ele);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
2774
src/redis/t_zset.c
2774
src/redis/t_zset.c
File diff suppressed because it is too large
Load diff
|
@ -42,7 +42,6 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "sha256.h"
|
|
||||||
|
|
||||||
|
|
||||||
/* Glob-style pattern matching. */
|
/* Glob-style pattern matching. */
|
||||||
|
@ -639,6 +638,7 @@ int ld2string(char *buf, size_t len, long double value, ld2string_mode mode) {
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ROMAN_DISABLE_CODE
|
||||||
/* Get random bytes, attempts to get an initial seed from /dev/urandom and
|
/* Get random bytes, attempts to get an initial seed from /dev/urandom and
|
||||||
* the uses a one way hash function in counter mode to generate a random
|
* the uses a one way hash function in counter mode to generate a random
|
||||||
* stream. However if /dev/urandom is not available, a weaker seed is used.
|
* stream. However if /dev/urandom is not available, a weaker seed is used.
|
||||||
|
@ -721,7 +721,7 @@ void getRandomHexChars(char *p, size_t len) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef ROMAN_DISABLE_CODE
|
|
||||||
/* Given the filename, return the absolute path as an SDS string, or NULL
|
/* Given the filename, return the absolute path as an SDS string, or NULL
|
||||||
* if it fails for some reason. Note that "filename" may be an absolute path
|
* if it fails for some reason. Note that "filename" may be an absolute path
|
||||||
* already, this will be detected and handled correctly.
|
* already, this will be detected and handled correctly.
|
||||||
|
@ -774,8 +774,6 @@ sds getAbsolutePath(char *filename) {
|
||||||
return abspath;
|
return abspath;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Gets the proper timezone in a more portable fashion
|
* Gets the proper timezone in a more portable fashion
|
||||||
* i.e timezone variables are linux specific.
|
* i.e timezone variables are linux specific.
|
||||||
|
@ -802,6 +800,7 @@ int pathIsBaseName(char *path) {
|
||||||
return strchr(path,'/') == NULL && strchr(path,'\\') == NULL;
|
return strchr(path,'/') == NULL && strchr(path,'\\') == NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Return the UNIX time in microseconds */
|
/* Return the UNIX time in microseconds */
|
||||||
long long ustime(void) {
|
long long ustime(void) {
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#include <absl/strings/str_format.h>
|
#include <absl/strings/str_format.h>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "redis/endianconv.h"
|
|
||||||
#include "redis/intset.h"
|
#include "redis/intset.h"
|
||||||
#include "redis/listpack.h"
|
#include "redis/listpack.h"
|
||||||
#include "redis/rdb.h"
|
#include "redis/rdb.h"
|
||||||
|
@ -378,10 +377,9 @@ error_code RdbSerializer::SaveLongLongAsString(int64_t value) {
|
||||||
* Return -1 on error, the size of the serialized value on success. */
|
* Return -1 on error, the size of the serialized value on success. */
|
||||||
error_code RdbSerializer::SaveBinaryDouble(double val) {
|
error_code RdbSerializer::SaveBinaryDouble(double val) {
|
||||||
static_assert(sizeof(val) == 8);
|
static_assert(sizeof(val) == 8);
|
||||||
|
const uint64_t* src = reinterpret_cast<const uint64_t*>(&val);
|
||||||
uint8_t buf[8];
|
uint8_t buf[8];
|
||||||
|
absl::little_endian::Store64(buf, *src);
|
||||||
memcpy(buf, &val, sizeof(buf));
|
|
||||||
memrev64ifbe(buf);
|
|
||||||
|
|
||||||
return WriteRaw(Bytes{buf, sizeof(buf)});
|
return WriteRaw(Bytes{buf, sizeof(buf)});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue