mirror of
https://github.com/dragonflydb/dragonfly.git
synced 2025-05-10 18:05:44 +02:00
Implement basic list operations
Fix redis linker dependencies. Bring more redis (BSD-3) code. Fix bugs related to multi-database redis. Introduce multiple types for redis values.
This commit is contained in:
parent
b1b0213cd2
commit
d64b4e01ea
26 changed files with 5734 additions and 73 deletions
|
@ -12,6 +12,7 @@ enum class OpStatus : uint16_t {
|
|||
OK,
|
||||
KEY_NOTFOUND,
|
||||
SKIPPED,
|
||||
WRONG_TYPE,
|
||||
};
|
||||
|
||||
class OpResultBase {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
add_library(redis_lib endianconv.c intset.c listpack.c object.c
|
||||
lzf_c.c lzf_d.c sds.c sha256.c
|
||||
quicklist.c util.c zmalloc.c)
|
||||
add_library(redis_lib crc64.c crcspeed.c debug.c dict.c endianconv.c intset.c
|
||||
listpack.c mt19937-64.c object.c lzf_c.c lzf_d.c sds.c sha256.c
|
||||
quicklist.c redis_aux.c siphash.c t_zset.c util.c zmalloc.c)
|
||||
|
||||
cxx_link(redis_lib)
|
||||
|
|
161
redis/crc64.c
Normal file
161
redis/crc64.c
Normal file
|
@ -0,0 +1,161 @@
|
|||
/* Copyright (c) 2014, Matt Stancliff <matt@genges.com>
|
||||
* Copyright (c) 2020, Amazon Web Services
|
||||
* 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 "crc64.h"
|
||||
#include "crcspeed.h"
|
||||
static uint64_t crc64_table[8][256] = {{0}};
|
||||
|
||||
#define POLY UINT64_C(0xad93d23594c935a9)
|
||||
/******************** BEGIN GENERATED PYCRC FUNCTIONS ********************/
|
||||
/**
|
||||
* Generated on Sun Dec 21 14:14:07 2014,
|
||||
* by pycrc v0.8.2, https://www.tty1.net/pycrc/
|
||||
*
|
||||
* LICENSE ON GENERATED CODE:
|
||||
* ==========================
|
||||
* As of version 0.6, pycrc is released under the terms of the MIT licence.
|
||||
* The code generated by pycrc is not considered a substantial portion of the
|
||||
* software, therefore the author of pycrc will not claim any copyright on
|
||||
* the generated code.
|
||||
* ==========================
|
||||
*
|
||||
* CRC configuration:
|
||||
* Width = 64
|
||||
* Poly = 0xad93d23594c935a9
|
||||
* XorIn = 0xffffffffffffffff
|
||||
* ReflectIn = True
|
||||
* XorOut = 0x0000000000000000
|
||||
* ReflectOut = True
|
||||
* Algorithm = bit-by-bit-fast
|
||||
*
|
||||
* Modifications after generation (by matt):
|
||||
* - included finalize step in-line with update for single-call generation
|
||||
* - re-worked some inner variable architectures
|
||||
* - adjusted function parameters to match expected prototypes.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* Reflect all bits of a \a data word of \a data_len bytes.
|
||||
*
|
||||
* \param data The data word to be reflected.
|
||||
* \param data_len The width of \a data expressed in number of bits.
|
||||
* \return The reflected data.
|
||||
*****************************************************************************/
|
||||
static inline uint_fast64_t crc_reflect(uint_fast64_t data, size_t data_len) {
|
||||
uint_fast64_t ret = data & 0x01;
|
||||
|
||||
for (size_t i = 1; i < data_len; i++) {
|
||||
data >>= 1;
|
||||
ret = (ret << 1) | (data & 0x01);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the crc value with new data.
|
||||
*
|
||||
* \param crc The current crc value.
|
||||
* \param data Pointer to a buffer of \a data_len bytes.
|
||||
* \param data_len Number of bytes in the \a data buffer.
|
||||
* \return The updated crc value.
|
||||
******************************************************************************/
|
||||
uint64_t _crc64(uint_fast64_t crc, const void *in_data, const uint64_t len) {
|
||||
const uint8_t *data = in_data;
|
||||
unsigned long long bit;
|
||||
|
||||
for (uint64_t offset = 0; offset < len; offset++) {
|
||||
uint8_t c = data[offset];
|
||||
for (uint_fast8_t i = 0x01; i & 0xff; i <<= 1) {
|
||||
bit = crc & 0x8000000000000000;
|
||||
if (c & i) {
|
||||
bit = !bit;
|
||||
}
|
||||
|
||||
crc <<= 1;
|
||||
if (bit) {
|
||||
crc ^= POLY;
|
||||
}
|
||||
}
|
||||
|
||||
crc &= 0xffffffffffffffff;
|
||||
}
|
||||
|
||||
crc = crc & 0xffffffffffffffff;
|
||||
return crc_reflect(crc, 64) ^ 0x0000000000000000;
|
||||
}
|
||||
|
||||
/******************** END GENERATED PYCRC FUNCTIONS ********************/
|
||||
|
||||
/* Initializes the 16KB lookup tables. */
|
||||
void crc64_init(void) {
|
||||
crcspeed64native_init(_crc64, crc64_table);
|
||||
}
|
||||
|
||||
/* Compute crc64 */
|
||||
uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l) {
|
||||
return crcspeed64native(crc64_table, crc, (void *) s, l);
|
||||
}
|
||||
|
||||
/* Test main */
|
||||
#ifdef REDIS_TEST
|
||||
#include <stdio.h>
|
||||
|
||||
#define UNUSED(x) (void)(x)
|
||||
int crc64Test(int argc, char *argv[], int flags) {
|
||||
UNUSED(argc);
|
||||
UNUSED(argv);
|
||||
UNUSED(flags);
|
||||
crc64_init();
|
||||
printf("[calcula]: e9c6d914c4b8d9ca == %016" PRIx64 "\n",
|
||||
(uint64_t)_crc64(0, "123456789", 9));
|
||||
printf("[64speed]: e9c6d914c4b8d9ca == %016" PRIx64 "\n",
|
||||
(uint64_t)crc64(0, (unsigned char*)"123456789", 9));
|
||||
char li[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed "
|
||||
"do eiusmod tempor incididunt ut labore et dolore magna "
|
||||
"aliqua. Ut enim ad minim veniam, quis nostrud exercitation "
|
||||
"ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis "
|
||||
"aute irure dolor in reprehenderit in voluptate velit esse "
|
||||
"cillum dolore eu fugiat nulla pariatur. Excepteur sint "
|
||||
"occaecat cupidatat non proident, sunt in culpa qui officia "
|
||||
"deserunt mollit anim id est laborum.";
|
||||
printf("[calcula]: c7794709e69683b3 == %016" PRIx64 "\n",
|
||||
(uint64_t)_crc64(0, li, sizeof(li)));
|
||||
printf("[64speed]: c7794709e69683b3 == %016" PRIx64 "\n",
|
||||
(uint64_t)crc64(0, (unsigned char*)li, sizeof(li)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef REDIS_TEST_MAIN
|
||||
int main(int argc, char *argv[]) {
|
||||
return crc64Test(argc, argv);
|
||||
}
|
||||
|
||||
#endif
|
13
redis/crc64.h
Normal file
13
redis/crc64.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef CRC64_H
|
||||
#define CRC64_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void crc64_init(void);
|
||||
uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);
|
||||
|
||||
#ifdef REDIS_TEST
|
||||
int crc64Test(int argc, char *argv[], int flags);
|
||||
#endif
|
||||
|
||||
#endif
|
282
redis/crcspeed.c
Normal file
282
redis/crcspeed.c
Normal file
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Mark Adler
|
||||
* Originally by: crc64.c Version 1.4 16 Dec 2013 Mark Adler
|
||||
* Modifications by Matt Stancliff <matt@genges.com>:
|
||||
* - removed CRC64-specific behavior
|
||||
* - added generation of lookup tables by parameters
|
||||
* - removed inversion of CRC input/result
|
||||
* - removed automatic initialization in favor of explicit initialization
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Mark Adler
|
||||
madler@alumni.caltech.edu
|
||||
*/
|
||||
|
||||
#include "crcspeed.h"
|
||||
|
||||
/* Fill in a CRC constants table. */
|
||||
void crcspeed64little_init(crcfn64 crcfn, uint64_t table[8][256]) {
|
||||
uint64_t crc;
|
||||
|
||||
/* generate CRCs for all single byte sequences */
|
||||
for (int n = 0; n < 256; n++) {
|
||||
unsigned char v = n;
|
||||
table[0][n] = crcfn(0, &v, 1);
|
||||
}
|
||||
|
||||
/* generate nested CRC table for future slice-by-8 lookup */
|
||||
for (int n = 0; n < 256; n++) {
|
||||
crc = table[0][n];
|
||||
for (int k = 1; k < 8; k++) {
|
||||
crc = table[0][crc & 0xff] ^ (crc >> 8);
|
||||
table[k][n] = crc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void crcspeed16little_init(crcfn16 crcfn, uint16_t table[8][256]) {
|
||||
uint16_t crc;
|
||||
|
||||
/* generate CRCs for all single byte sequences */
|
||||
for (int n = 0; n < 256; n++) {
|
||||
table[0][n] = crcfn(0, &n, 1);
|
||||
}
|
||||
|
||||
/* generate nested CRC table for future slice-by-8 lookup */
|
||||
for (int n = 0; n < 256; n++) {
|
||||
crc = table[0][n];
|
||||
for (int k = 1; k < 8; k++) {
|
||||
crc = table[0][(crc >> 8) & 0xff] ^ (crc << 8);
|
||||
table[k][n] = crc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Reverse the bytes in a 64-bit word. */
|
||||
static inline uint64_t rev8(uint64_t a) {
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __builtin_bswap64(a);
|
||||
#else
|
||||
uint64_t m;
|
||||
|
||||
m = UINT64_C(0xff00ff00ff00ff);
|
||||
a = ((a >> 8) & m) | (a & m) << 8;
|
||||
m = UINT64_C(0xffff0000ffff);
|
||||
a = ((a >> 16) & m) | (a & m) << 16;
|
||||
return a >> 32 | a << 32;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* This function is called once to initialize the CRC table for use on a
|
||||
big-endian architecture. */
|
||||
void crcspeed64big_init(crcfn64 fn, uint64_t big_table[8][256]) {
|
||||
/* Create the little endian table then reverse all the entries. */
|
||||
crcspeed64little_init(fn, big_table);
|
||||
for (int k = 0; k < 8; k++) {
|
||||
for (int n = 0; n < 256; n++) {
|
||||
big_table[k][n] = rev8(big_table[k][n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void crcspeed16big_init(crcfn16 fn, uint16_t big_table[8][256]) {
|
||||
/* Create the little endian table then reverse all the entries. */
|
||||
crcspeed16little_init(fn, big_table);
|
||||
for (int k = 0; k < 8; k++) {
|
||||
for (int n = 0; n < 256; n++) {
|
||||
big_table[k][n] = rev8(big_table[k][n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate a non-inverted CRC multiple bytes at a time on a little-endian
|
||||
* architecture. If you need inverted CRC, invert *before* calling and invert
|
||||
* *after* calling.
|
||||
* 64 bit crc = process 8 bytes at once;
|
||||
*/
|
||||
uint64_t crcspeed64little(uint64_t little_table[8][256], uint64_t crc,
|
||||
void *buf, size_t len) {
|
||||
unsigned char *next = buf;
|
||||
|
||||
/* process individual bytes until we reach an 8-byte aligned pointer */
|
||||
while (len && ((uintptr_t)next & 7) != 0) {
|
||||
crc = little_table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
|
||||
len--;
|
||||
}
|
||||
|
||||
/* fast middle processing, 8 bytes (aligned!) per loop */
|
||||
while (len >= 8) {
|
||||
crc ^= *(uint64_t *)next;
|
||||
crc = little_table[7][crc & 0xff] ^
|
||||
little_table[6][(crc >> 8) & 0xff] ^
|
||||
little_table[5][(crc >> 16) & 0xff] ^
|
||||
little_table[4][(crc >> 24) & 0xff] ^
|
||||
little_table[3][(crc >> 32) & 0xff] ^
|
||||
little_table[2][(crc >> 40) & 0xff] ^
|
||||
little_table[1][(crc >> 48) & 0xff] ^
|
||||
little_table[0][crc >> 56];
|
||||
next += 8;
|
||||
len -= 8;
|
||||
}
|
||||
|
||||
/* process remaining bytes (can't be larger than 8) */
|
||||
while (len) {
|
||||
crc = little_table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
|
||||
len--;
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
uint16_t crcspeed16little(uint16_t little_table[8][256], uint16_t crc,
|
||||
void *buf, size_t len) {
|
||||
unsigned char *next = buf;
|
||||
|
||||
/* process individual bytes until we reach an 8-byte aligned pointer */
|
||||
while (len && ((uintptr_t)next & 7) != 0) {
|
||||
crc = little_table[0][((crc >> 8) ^ *next++) & 0xff] ^ (crc << 8);
|
||||
len--;
|
||||
}
|
||||
|
||||
/* fast middle processing, 8 bytes (aligned!) per loop */
|
||||
while (len >= 8) {
|
||||
uint64_t n = *(uint64_t *)next;
|
||||
crc = little_table[7][(n & 0xff) ^ ((crc >> 8) & 0xff)] ^
|
||||
little_table[6][((n >> 8) & 0xff) ^ (crc & 0xff)] ^
|
||||
little_table[5][(n >> 16) & 0xff] ^
|
||||
little_table[4][(n >> 24) & 0xff] ^
|
||||
little_table[3][(n >> 32) & 0xff] ^
|
||||
little_table[2][(n >> 40) & 0xff] ^
|
||||
little_table[1][(n >> 48) & 0xff] ^
|
||||
little_table[0][n >> 56];
|
||||
next += 8;
|
||||
len -= 8;
|
||||
}
|
||||
|
||||
/* process remaining bytes (can't be larger than 8) */
|
||||
while (len) {
|
||||
crc = little_table[0][((crc >> 8) ^ *next++) & 0xff] ^ (crc << 8);
|
||||
len--;
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
/* Calculate a non-inverted CRC eight bytes at a time on a big-endian
|
||||
* architecture.
|
||||
*/
|
||||
uint64_t crcspeed64big(uint64_t big_table[8][256], uint64_t crc, void *buf,
|
||||
size_t len) {
|
||||
unsigned char *next = buf;
|
||||
|
||||
crc = rev8(crc);
|
||||
while (len && ((uintptr_t)next & 7) != 0) {
|
||||
crc = big_table[0][(crc >> 56) ^ *next++] ^ (crc << 8);
|
||||
len--;
|
||||
}
|
||||
|
||||
while (len >= 8) {
|
||||
crc ^= *(uint64_t *)next;
|
||||
crc = big_table[0][crc & 0xff] ^
|
||||
big_table[1][(crc >> 8) & 0xff] ^
|
||||
big_table[2][(crc >> 16) & 0xff] ^
|
||||
big_table[3][(crc >> 24) & 0xff] ^
|
||||
big_table[4][(crc >> 32) & 0xff] ^
|
||||
big_table[5][(crc >> 40) & 0xff] ^
|
||||
big_table[6][(crc >> 48) & 0xff] ^
|
||||
big_table[7][crc >> 56];
|
||||
next += 8;
|
||||
len -= 8;
|
||||
}
|
||||
|
||||
while (len) {
|
||||
crc = big_table[0][(crc >> 56) ^ *next++] ^ (crc << 8);
|
||||
len--;
|
||||
}
|
||||
|
||||
return rev8(crc);
|
||||
}
|
||||
|
||||
/* WARNING: Completely untested on big endian architecture. Possibly broken. */
|
||||
uint16_t crcspeed16big(uint16_t big_table[8][256], uint16_t crc_in, void *buf,
|
||||
size_t len) {
|
||||
unsigned char *next = buf;
|
||||
uint64_t crc = crc_in;
|
||||
|
||||
crc = rev8(crc);
|
||||
while (len && ((uintptr_t)next & 7) != 0) {
|
||||
crc = big_table[0][((crc >> (56 - 8)) ^ *next++) & 0xff] ^ (crc >> 8);
|
||||
len--;
|
||||
}
|
||||
|
||||
while (len >= 8) {
|
||||
uint64_t n = *(uint64_t *)next;
|
||||
crc = big_table[0][(n & 0xff) ^ ((crc >> (56 - 8)) & 0xff)] ^
|
||||
big_table[1][((n >> 8) & 0xff) ^ (crc & 0xff)] ^
|
||||
big_table[2][(n >> 16) & 0xff] ^
|
||||
big_table[3][(n >> 24) & 0xff] ^
|
||||
big_table[4][(n >> 32) & 0xff] ^
|
||||
big_table[5][(n >> 40) & 0xff] ^
|
||||
big_table[6][(n >> 48) & 0xff] ^
|
||||
big_table[7][n >> 56];
|
||||
next += 8;
|
||||
len -= 8;
|
||||
}
|
||||
|
||||
while (len) {
|
||||
crc = big_table[0][((crc >> (56 - 8)) ^ *next++) & 0xff] ^ (crc >> 8);
|
||||
len--;
|
||||
}
|
||||
|
||||
return rev8(crc);
|
||||
}
|
||||
|
||||
/* Return the CRC of buf[0..len-1] with initial crc, processing eight bytes
|
||||
at a time using passed-in lookup table.
|
||||
This selects one of two routines depending on the endianess of
|
||||
the architecture. */
|
||||
uint64_t crcspeed64native(uint64_t table[8][256], uint64_t crc, void *buf,
|
||||
size_t len) {
|
||||
uint64_t n = 1;
|
||||
|
||||
return *(char *)&n ? crcspeed64little(table, crc, buf, len)
|
||||
: crcspeed64big(table, crc, buf, len);
|
||||
}
|
||||
|
||||
uint16_t crcspeed16native(uint16_t table[8][256], uint16_t crc, void *buf,
|
||||
size_t len) {
|
||||
uint64_t n = 1;
|
||||
|
||||
return *(char *)&n ? crcspeed16little(table, crc, buf, len)
|
||||
: crcspeed16big(table, crc, buf, len);
|
||||
}
|
||||
|
||||
/* Initialize CRC lookup table in architecture-dependent manner. */
|
||||
void crcspeed64native_init(crcfn64 fn, uint64_t table[8][256]) {
|
||||
uint64_t n = 1;
|
||||
|
||||
*(char *)&n ? crcspeed64little_init(fn, table)
|
||||
: crcspeed64big_init(fn, table);
|
||||
}
|
||||
|
||||
void crcspeed16native_init(crcfn16 fn, uint16_t table[8][256]) {
|
||||
uint64_t n = 1;
|
||||
|
||||
*(char *)&n ? crcspeed16little_init(fn, table)
|
||||
: crcspeed16big_init(fn, table);
|
||||
}
|
60
redis/crcspeed.h
Normal file
60
redis/crcspeed.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/* Copyright (c) 2014, Matt Stancliff <matt@genges.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. */
|
||||
|
||||
#ifndef CRCSPEED_H
|
||||
#define CRCSPEED_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef uint64_t (*crcfn64)(uint64_t, const void *, const uint64_t);
|
||||
typedef uint16_t (*crcfn16)(uint16_t, const void *, const uint64_t);
|
||||
|
||||
/* CRC-64 */
|
||||
void crcspeed64little_init(crcfn64 fn, uint64_t table[8][256]);
|
||||
void crcspeed64big_init(crcfn64 fn, uint64_t table[8][256]);
|
||||
void crcspeed64native_init(crcfn64 fn, uint64_t table[8][256]);
|
||||
|
||||
uint64_t crcspeed64little(uint64_t table[8][256], uint64_t crc, void *buf,
|
||||
size_t len);
|
||||
uint64_t crcspeed64big(uint64_t table[8][256], uint64_t crc, void *buf,
|
||||
size_t len);
|
||||
uint64_t crcspeed64native(uint64_t table[8][256], uint64_t crc, void *buf,
|
||||
size_t len);
|
||||
|
||||
/* CRC-16 */
|
||||
void crcspeed16little_init(crcfn16 fn, uint16_t table[8][256]);
|
||||
void crcspeed16big_init(crcfn16 fn, uint16_t table[8][256]);
|
||||
void crcspeed16native_init(crcfn16 fn, uint16_t table[8][256]);
|
||||
|
||||
uint16_t crcspeed16little(uint16_t table[8][256], uint16_t crc, void *buf,
|
||||
size_t len);
|
||||
uint16_t crcspeed16big(uint16_t table[8][256], uint16_t crc, void *buf,
|
||||
size_t len);
|
||||
uint16_t crcspeed16native(uint16_t table[8][256], uint16_t crc, void *buf,
|
||||
size_t len);
|
||||
#endif
|
113
redis/debug.c
Normal file
113
redis/debug.c
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2020, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2020, Redis Labs, Inc
|
||||
* 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 <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <syslog.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
int verbosity = LL_NOTICE;
|
||||
|
||||
void serverLog(int level, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
char msg[LOG_MAX_LEN];
|
||||
|
||||
if ((level&0xff) < verbosity) return;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(msg, sizeof(msg), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fprintf(stdout, "%s\n",msg);
|
||||
}
|
||||
|
||||
void _serverPanic(const char *file, int line, const char *msg, ...) {
|
||||
va_list ap;
|
||||
va_start(ap,msg);
|
||||
char fmtmsg[256];
|
||||
vsnprintf(fmtmsg,sizeof(fmtmsg),msg,ap);
|
||||
va_end(ap);
|
||||
|
||||
serverLog(LL_WARNING, "------------------------------------------------");
|
||||
serverLog(LL_WARNING, "!!! Software Failure. Press left mouse button to continue");
|
||||
serverLog(LL_WARNING, "Guru Meditation: %s #%s:%d", fmtmsg,file,line);
|
||||
#ifndef NDEBUG
|
||||
__assert_fail(msg, file, line, "");
|
||||
#endif
|
||||
}
|
||||
|
||||
void _serverAssert(const char *estr, const char *file, int line) {
|
||||
serverLog(LL_WARNING,"=== ASSERTION FAILED ===");
|
||||
serverLog(LL_WARNING,"==> %s:%d '%s' is not true",file,line,estr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Low level logging. To use only for very big messages, otherwise
|
||||
* serverLog() is to prefer. */
|
||||
void serverLogRaw(int level, const char *msg) {
|
||||
FILE *fp;
|
||||
int log_to_stdout = 1;
|
||||
|
||||
level &= 0xff; /* clear flags */
|
||||
if (level < verbosity) return;
|
||||
|
||||
fp = stdout;
|
||||
|
||||
fprintf(fp,"%s",msg);
|
||||
fflush(fp);
|
||||
|
||||
if (!log_to_stdout) fclose(fp);
|
||||
}
|
||||
|
||||
void serverLogHexDump(int level, char *descr, void *value, size_t len) {
|
||||
char buf[65], *b;
|
||||
unsigned char *v = value;
|
||||
char charset[] = "0123456789abcdef";
|
||||
|
||||
serverLog(level,"%s (hexdump of %zu bytes):", descr, len);
|
||||
b = buf;
|
||||
while(len) {
|
||||
b[0] = charset[(*v)>>4];
|
||||
b[1] = charset[(*v)&0xf];
|
||||
b[2] = '\0';
|
||||
b += 2;
|
||||
len--;
|
||||
v++;
|
||||
if (b-buf == 64 || len == 0) {
|
||||
serverLogRaw(level|LL_RAW,buf);
|
||||
b = buf;
|
||||
}
|
||||
}
|
||||
serverLogRaw(level|LL_RAW,"\n");
|
||||
}
|
|
@ -33,8 +33,6 @@
|
|||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "fmacros.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
#include "dict.h"
|
||||
#include "sds.h"
|
||||
#include "quicklist.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
/* The actual Redis Object */
|
||||
#define OBJ_STRING 0U /* String object. */
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "redis_aux.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "crc64.h"
|
||||
#include "object.h"
|
||||
|
@ -78,20 +79,6 @@ size_t sdsZmallocSize(sds s) {
|
|||
return zmalloc_size(sh);
|
||||
}
|
||||
|
||||
/* Return 1 if currently we allow dict to expand. Dict may allocate huge
|
||||
* memory to contain hash buckets when dict expands, that may lead redis
|
||||
* rejects user's requests or evicts some keys, we can stop dict to expand
|
||||
* provisionally if used memory will be over maxmemory after dict expands,
|
||||
* but to guarantee the performance of redis, we still allow dict to expand
|
||||
* if dict load factor exceeds HASHTABLE_MAX_LOAD_FACTOR. */
|
||||
static int dictExpandAllowed(size_t moreMem, double usedRatio) {
|
||||
if (usedRatio <= HASHTABLE_MAX_LOAD_FACTOR) {
|
||||
return !overMaxmemoryAfterAlloc(moreMem);
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set dictionary type. Keys are SDS strings, values are not used. */
|
||||
dictType setDictType = {
|
||||
dictSdsHash, /* hash function */
|
||||
|
@ -113,38 +100,3 @@ dictType zsetDictType = {
|
|||
NULL, /* val destructor */
|
||||
NULL /* allow to expand */
|
||||
};
|
||||
|
||||
static void dictObjectDestructor(dict* privdata, void* val) {
|
||||
DICT_NOTUSED(privdata);
|
||||
|
||||
if (val == NULL)
|
||||
return; /* Lazy freeing will set value to NULL. */
|
||||
decrRefCount(val);
|
||||
}
|
||||
|
||||
/* Db->dict, keys are sds strings, vals are Redis objects. */
|
||||
dictType dbDictType = {
|
||||
dictSdsHash, /* hash function */
|
||||
NULL, /* key dup */
|
||||
NULL, /* val dup */
|
||||
dictSdsKeyCompare, /* key compare */
|
||||
dictSdsDestructor, /* key destructor */
|
||||
dictObjectDestructor, /* val destructor */
|
||||
dictExpandAllowed /* allow to expand */
|
||||
};
|
||||
|
||||
/* Db->expires */
|
||||
/* Db->expires stores keys that are contained in redis_dict_.
|
||||
Which means that uniqueness of the strings is guaranteed through uniqueness of the pointers
|
||||
Therefore, it's enough to compare and hash keys by their addresses without reading the contents
|
||||
of the key
|
||||
*/
|
||||
dictType keyPtrDictType = {
|
||||
dictPtrHash, /* hash function */
|
||||
NULL, /* key dup */
|
||||
NULL, /* val dup */
|
||||
dictPtrKeyCompare, /* key compare */
|
||||
NULL, /* key destructor */
|
||||
NULL, /* val destructor */
|
||||
NULL /* allow to expand */
|
||||
};
|
||||
|
|
|
@ -35,10 +35,8 @@
|
|||
int htNeedsResize(dict *dict); // moved from server.cc
|
||||
|
||||
/* Hash table types */
|
||||
extern dictType dbDictType;
|
||||
extern dictType zsetDictType;
|
||||
extern dictType setDictType;
|
||||
extern dictType keyPtrDictType;
|
||||
extern dictType hashDictType;
|
||||
|
||||
/* To improve the quality of the LRU approximation we take a set of keys
|
||||
|
@ -63,10 +61,6 @@ struct evictionPoolEntry {
|
|||
uint64_t dictSdsHash(const void *key);
|
||||
int dictSdsKeyCompare(dict *privdata, const void *key1, const void *key2);
|
||||
void dictSdsDestructor(dict *privdata, void *val);
|
||||
int getMaxmemoryState(size_t *total, size_t *logical, size_t *tofree, float *level);
|
||||
|
||||
void evictionPoolPopulate(int dbid, dict *sampledict, dict *keydict, struct evictionPoolEntry *pool);
|
||||
int overMaxmemoryAfterAlloc(size_t moremem);
|
||||
size_t sdsZmallocSize(sds s) ;
|
||||
|
||||
typedef struct ServerStub {
|
||||
|
|
373
redis/siphash.c
Normal file
373
redis/siphash.c
Normal file
|
@ -0,0 +1,373 @@
|
|||
/*
|
||||
SipHash reference C implementation
|
||||
|
||||
Copyright (c) 2012-2016 Jean-Philippe Aumasson
|
||||
<jeanphilippe.aumasson@gmail.com>
|
||||
Copyright (c) 2012-2014 Daniel J. Bernstein <djb@cr.yp.to>
|
||||
Copyright (c) 2017 Salvatore Sanfilippo <antirez@gmail.com>
|
||||
|
||||
To the extent possible under law, the author(s) have dedicated all copyright
|
||||
and related and neighboring rights to this software to the public domain
|
||||
worldwide. This software is distributed without any warranty.
|
||||
|
||||
You should have received a copy of the CC0 Public Domain Dedication along
|
||||
with this software. If not, see
|
||||
<http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
This version was modified by Salvatore Sanfilippo <antirez@gmail.com>
|
||||
in the following ways:
|
||||
|
||||
1. We use SipHash 1-2. This is not believed to be as strong as the
|
||||
suggested 2-4 variant, but AFAIK there are not trivial attacks
|
||||
against this reduced-rounds version, and it runs at the same speed
|
||||
as Murmurhash2 that we used previously, while the 2-4 variant slowed
|
||||
down Redis by a 4% figure more or less.
|
||||
2. Hard-code rounds in the hope the compiler can optimize it more
|
||||
in this raw from. Anyway we always want the standard 2-4 variant.
|
||||
3. Modify the prototype and implementation so that the function directly
|
||||
returns an uint64_t value, the hash itself, instead of receiving an
|
||||
output buffer. This also means that the output size is set to 8 bytes
|
||||
and the 16 bytes output code handling was removed.
|
||||
4. Provide a case insensitive variant to be used when hashing strings that
|
||||
must be considered identical by the hash table regardless of the case.
|
||||
If we don't have directly a case insensitive hash function, we need to
|
||||
perform a text transformation in some temporary buffer, which is costly.
|
||||
5. Remove debugging code.
|
||||
6. Modified the original test.c file to be a stand-alone function testing
|
||||
the function in the new form (returning an uint64_t) using just the
|
||||
relevant test vector.
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/* Fast tolower() alike function that does not care about locale
|
||||
* but just returns a-z instead of A-Z. */
|
||||
int siptlw(int c) {
|
||||
if (c >= 'A' && c <= 'Z') {
|
||||
return c+('a'-'A');
|
||||
} else {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__has_attribute)
|
||||
#if __has_attribute(no_sanitize)
|
||||
#define NO_SANITIZE(sanitizer) __attribute__((no_sanitize(sanitizer)))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(NO_SANITIZE)
|
||||
#define NO_SANITIZE(sanitizer)
|
||||
#endif
|
||||
|
||||
/* Test of the CPU is Little Endian and supports not aligned accesses.
|
||||
* Two interesting conditions to speedup the function that happen to be
|
||||
* in most of x86 servers. */
|
||||
#if defined(__X86_64__) || defined(__x86_64__) || defined (__i386__) \
|
||||
|| defined (__aarch64__) || defined (__arm64__)
|
||||
#define UNALIGNED_LE_CPU
|
||||
#endif
|
||||
|
||||
#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
|
||||
|
||||
#define U32TO8_LE(p, v) \
|
||||
(p)[0] = (uint8_t)((v)); \
|
||||
(p)[1] = (uint8_t)((v) >> 8); \
|
||||
(p)[2] = (uint8_t)((v) >> 16); \
|
||||
(p)[3] = (uint8_t)((v) >> 24);
|
||||
|
||||
#define U64TO8_LE(p, v) \
|
||||
U32TO8_LE((p), (uint32_t)((v))); \
|
||||
U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));
|
||||
|
||||
#ifdef UNALIGNED_LE_CPU
|
||||
#define U8TO64_LE(p) (*((uint64_t*)(p)))
|
||||
#else
|
||||
#define U8TO64_LE(p) \
|
||||
(((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | \
|
||||
((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \
|
||||
((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \
|
||||
((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))
|
||||
#endif
|
||||
|
||||
#define U8TO64_LE_NOCASE(p) \
|
||||
(((uint64_t)(siptlw((p)[0]))) | \
|
||||
((uint64_t)(siptlw((p)[1])) << 8) | \
|
||||
((uint64_t)(siptlw((p)[2])) << 16) | \
|
||||
((uint64_t)(siptlw((p)[3])) << 24) | \
|
||||
((uint64_t)(siptlw((p)[4])) << 32) | \
|
||||
((uint64_t)(siptlw((p)[5])) << 40) | \
|
||||
((uint64_t)(siptlw((p)[6])) << 48) | \
|
||||
((uint64_t)(siptlw((p)[7])) << 56))
|
||||
|
||||
#define SIPROUND \
|
||||
do { \
|
||||
v0 += v1; \
|
||||
v1 = ROTL(v1, 13); \
|
||||
v1 ^= v0; \
|
||||
v0 = ROTL(v0, 32); \
|
||||
v2 += v3; \
|
||||
v3 = ROTL(v3, 16); \
|
||||
v3 ^= v2; \
|
||||
v0 += v3; \
|
||||
v3 = ROTL(v3, 21); \
|
||||
v3 ^= v0; \
|
||||
v2 += v1; \
|
||||
v1 = ROTL(v1, 17); \
|
||||
v1 ^= v2; \
|
||||
v2 = ROTL(v2, 32); \
|
||||
} while (0)
|
||||
|
||||
NO_SANITIZE("alignment")
|
||||
uint64_t siphash(const uint8_t *in, const size_t inlen, const uint8_t *k) {
|
||||
#ifndef UNALIGNED_LE_CPU
|
||||
uint64_t hash;
|
||||
uint8_t *out = (uint8_t*) &hash;
|
||||
#endif
|
||||
uint64_t v0 = 0x736f6d6570736575ULL;
|
||||
uint64_t v1 = 0x646f72616e646f6dULL;
|
||||
uint64_t v2 = 0x6c7967656e657261ULL;
|
||||
uint64_t v3 = 0x7465646279746573ULL;
|
||||
uint64_t k0 = U8TO64_LE(k);
|
||||
uint64_t k1 = U8TO64_LE(k + 8);
|
||||
uint64_t m;
|
||||
const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t));
|
||||
const int left = inlen & 7;
|
||||
uint64_t b = ((uint64_t)inlen) << 56;
|
||||
v3 ^= k1;
|
||||
v2 ^= k0;
|
||||
v1 ^= k1;
|
||||
v0 ^= k0;
|
||||
|
||||
for (; in != end; in += 8) {
|
||||
m = U8TO64_LE(in);
|
||||
v3 ^= m;
|
||||
|
||||
SIPROUND;
|
||||
|
||||
v0 ^= m;
|
||||
}
|
||||
|
||||
switch (left) {
|
||||
case 7: b |= ((uint64_t)in[6]) << 48; /* fall-thru */
|
||||
case 6: b |= ((uint64_t)in[5]) << 40; /* fall-thru */
|
||||
case 5: b |= ((uint64_t)in[4]) << 32; /* fall-thru */
|
||||
case 4: b |= ((uint64_t)in[3]) << 24; /* fall-thru */
|
||||
case 3: b |= ((uint64_t)in[2]) << 16; /* fall-thru */
|
||||
case 2: b |= ((uint64_t)in[1]) << 8; /* fall-thru */
|
||||
case 1: b |= ((uint64_t)in[0]); break;
|
||||
case 0: break;
|
||||
}
|
||||
|
||||
v3 ^= b;
|
||||
|
||||
SIPROUND;
|
||||
|
||||
v0 ^= b;
|
||||
v2 ^= 0xff;
|
||||
|
||||
SIPROUND;
|
||||
SIPROUND;
|
||||
|
||||
b = v0 ^ v1 ^ v2 ^ v3;
|
||||
#ifndef UNALIGNED_LE_CPU
|
||||
U64TO8_LE(out, b);
|
||||
return hash;
|
||||
#else
|
||||
return b;
|
||||
#endif
|
||||
}
|
||||
|
||||
NO_SANITIZE("alignment")
|
||||
uint64_t siphash_nocase(const uint8_t *in, const size_t inlen, const uint8_t *k)
|
||||
{
|
||||
#ifndef UNALIGNED_LE_CPU
|
||||
uint64_t hash;
|
||||
uint8_t *out = (uint8_t*) &hash;
|
||||
#endif
|
||||
uint64_t v0 = 0x736f6d6570736575ULL;
|
||||
uint64_t v1 = 0x646f72616e646f6dULL;
|
||||
uint64_t v2 = 0x6c7967656e657261ULL;
|
||||
uint64_t v3 = 0x7465646279746573ULL;
|
||||
uint64_t k0 = U8TO64_LE(k);
|
||||
uint64_t k1 = U8TO64_LE(k + 8);
|
||||
uint64_t m;
|
||||
const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t));
|
||||
const int left = inlen & 7;
|
||||
uint64_t b = ((uint64_t)inlen) << 56;
|
||||
v3 ^= k1;
|
||||
v2 ^= k0;
|
||||
v1 ^= k1;
|
||||
v0 ^= k0;
|
||||
|
||||
for (; in != end; in += 8) {
|
||||
m = U8TO64_LE_NOCASE(in);
|
||||
v3 ^= m;
|
||||
|
||||
SIPROUND;
|
||||
|
||||
v0 ^= m;
|
||||
}
|
||||
|
||||
switch (left) {
|
||||
case 7: b |= ((uint64_t)siptlw(in[6])) << 48; /* fall-thru */
|
||||
case 6: b |= ((uint64_t)siptlw(in[5])) << 40; /* fall-thru */
|
||||
case 5: b |= ((uint64_t)siptlw(in[4])) << 32; /* fall-thru */
|
||||
case 4: b |= ((uint64_t)siptlw(in[3])) << 24; /* fall-thru */
|
||||
case 3: b |= ((uint64_t)siptlw(in[2])) << 16; /* fall-thru */
|
||||
case 2: b |= ((uint64_t)siptlw(in[1])) << 8; /* fall-thru */
|
||||
case 1: b |= ((uint64_t)siptlw(in[0])); break;
|
||||
case 0: break;
|
||||
}
|
||||
|
||||
v3 ^= b;
|
||||
|
||||
SIPROUND;
|
||||
|
||||
v0 ^= b;
|
||||
v2 ^= 0xff;
|
||||
|
||||
SIPROUND;
|
||||
SIPROUND;
|
||||
|
||||
b = v0 ^ v1 ^ v2 ^ v3;
|
||||
#ifndef UNALIGNED_LE_CPU
|
||||
U64TO8_LE(out, b);
|
||||
return hash;
|
||||
#else
|
||||
return b;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* --------------------------------- TEST ------------------------------------ */
|
||||
|
||||
#ifdef SIPHASH_TEST
|
||||
|
||||
const uint8_t vectors_sip64[64][8] = {
|
||||
{ 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72, },
|
||||
{ 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74, },
|
||||
{ 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d, },
|
||||
{ 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85, },
|
||||
{ 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf, },
|
||||
{ 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18, },
|
||||
{ 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb, },
|
||||
{ 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab, },
|
||||
{ 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93, },
|
||||
{ 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e, },
|
||||
{ 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a, },
|
||||
{ 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4, },
|
||||
{ 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75, },
|
||||
{ 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14, },
|
||||
{ 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7, },
|
||||
{ 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1, },
|
||||
{ 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f, },
|
||||
{ 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69, },
|
||||
{ 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b, },
|
||||
{ 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb, },
|
||||
{ 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe, },
|
||||
{ 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0, },
|
||||
{ 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93, },
|
||||
{ 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8, },
|
||||
{ 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8, },
|
||||
{ 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc, },
|
||||
{ 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17, },
|
||||
{ 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f, },
|
||||
{ 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde, },
|
||||
{ 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6, },
|
||||
{ 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad, },
|
||||
{ 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32, },
|
||||
{ 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71, },
|
||||
{ 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7, },
|
||||
{ 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12, },
|
||||
{ 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15, },
|
||||
{ 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31, },
|
||||
{ 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02, },
|
||||
{ 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca, },
|
||||
{ 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a, },
|
||||
{ 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e, },
|
||||
{ 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad, },
|
||||
{ 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18, },
|
||||
{ 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4, },
|
||||
{ 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9, },
|
||||
{ 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9, },
|
||||
{ 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb, },
|
||||
{ 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0, },
|
||||
{ 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6, },
|
||||
{ 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7, },
|
||||
{ 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee, },
|
||||
{ 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1, },
|
||||
{ 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a, },
|
||||
{ 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81, },
|
||||
{ 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f, },
|
||||
{ 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24, },
|
||||
{ 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7, },
|
||||
{ 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea, },
|
||||
{ 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60, },
|
||||
{ 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66, },
|
||||
{ 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c, },
|
||||
{ 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f, },
|
||||
{ 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5, },
|
||||
{ 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95, },
|
||||
};
|
||||
|
||||
|
||||
/* Test siphash using a test vector. Returns 0 if the function passed
|
||||
* all the tests, otherwise 1 is returned.
|
||||
*
|
||||
* IMPORTANT: The test vector is for SipHash 2-4. Before running
|
||||
* the test revert back the siphash() function to 2-4 rounds since
|
||||
* now it uses 1-2 rounds. */
|
||||
int siphash_test(void) {
|
||||
uint8_t in[64], k[16];
|
||||
int i;
|
||||
int fails = 0;
|
||||
|
||||
for (i = 0; i < 16; ++i)
|
||||
k[i] = i;
|
||||
|
||||
for (i = 0; i < 64; ++i) {
|
||||
in[i] = i;
|
||||
uint64_t hash = siphash(in, i, k);
|
||||
const uint8_t *v = NULL;
|
||||
v = (uint8_t *)vectors_sip64;
|
||||
if (memcmp(&hash, v + (i * 8), 8)) {
|
||||
/* printf("fail for %d bytes\n", i); */
|
||||
fails++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Run a few basic tests with the case insensitive version. */
|
||||
uint64_t h1, h2;
|
||||
h1 = siphash((uint8_t*)"hello world",11,(uint8_t*)"1234567812345678");
|
||||
h2 = siphash_nocase((uint8_t*)"hello world",11,(uint8_t*)"1234567812345678");
|
||||
if (h1 != h2) fails++;
|
||||
|
||||
h1 = siphash((uint8_t*)"hello world",11,(uint8_t*)"1234567812345678");
|
||||
h2 = siphash_nocase((uint8_t*)"HELLO world",11,(uint8_t*)"1234567812345678");
|
||||
if (h1 != h2) fails++;
|
||||
|
||||
h1 = siphash((uint8_t*)"HELLO world",11,(uint8_t*)"1234567812345678");
|
||||
h2 = siphash_nocase((uint8_t*)"HELLO world",11,(uint8_t*)"1234567812345678");
|
||||
if (h1 == h2) fails++;
|
||||
|
||||
if (!fails) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
if (siphash_test() == 0) {
|
||||
printf("SipHash test: OK\n");
|
||||
return 0;
|
||||
} else {
|
||||
printf("SipHash test: FAILED\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
4358
redis/t_zset.c
Normal file
4358
redis/t_zset.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -4,10 +4,10 @@ cxx_link(dragonfly base dragonfly_lib)
|
|||
add_library(dragonfly_lib command_registry.cc common.cc config_flags.cc
|
||||
conn_context.cc db_slice.cc debugcmd.cc dragonfly_listener.cc
|
||||
dragonfly_connection.cc engine_shard_set.cc generic_family.cc
|
||||
main_service.cc memcache_parser.cc
|
||||
list_family.cc main_service.cc memcache_parser.cc
|
||||
redis_parser.cc reply_builder.cc string_family.cc transaction.cc)
|
||||
|
||||
cxx_link(dragonfly_lib dfly_core uring_fiber_lib
|
||||
cxx_link(dragonfly_lib dfly_core redis_lib uring_fiber_lib
|
||||
fibers_ext strings_lib http_server_lib tls_lib)
|
||||
|
||||
add_library(dfly_test_lib test_utils.cc)
|
||||
|
|
|
@ -19,6 +19,7 @@ string WrongNumArgsError(std::string_view cmd) {
|
|||
}
|
||||
|
||||
const char kSyntaxErr[] = "syntax error";
|
||||
const char kWrongTypeErr[] = "-WRONGTYPE Operation against a key holding the wrong kind of value";
|
||||
const char kInvalidIntErr[] = "value is not an integer or out of range";
|
||||
const char kUintErr[] = "value is out of range, must be positive";
|
||||
const char kDbIndOutOfRangeErr[] = "DB index is out of range";
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
namespace dfly {
|
||||
|
||||
enum class ListDir : uint8_t { LEFT, RIGHT };
|
||||
|
||||
enum class Protocol : uint8_t {
|
||||
MEMCACHE = 1,
|
||||
REDIS = 2
|
||||
|
|
|
@ -42,7 +42,7 @@ void DbSlice::Reserve(DbIndex db_ind, size_t key_size) {
|
|||
db->main_table.reserve(key_size);
|
||||
}
|
||||
|
||||
auto DbSlice::Find(DbIndex db_index, std::string_view key) const -> OpResult<MainIterator> {
|
||||
auto DbSlice::Find(DbIndex db_index, std::string_view key, unsigned obj_type) const -> OpResult<MainIterator> {
|
||||
auto [it, expire_it] = FindExt(db_index, key);
|
||||
|
||||
if (it == MainIterator{})
|
||||
|
|
|
@ -42,7 +42,7 @@ class DbSlice {
|
|||
return now_ms_;
|
||||
}
|
||||
|
||||
OpResult<MainIterator> Find(DbIndex db_index, std::string_view key) const;
|
||||
OpResult<MainIterator> Find(DbIndex db_index, std::string_view key, unsigned obj_type) const;
|
||||
|
||||
// Returns (value, expire) dict entries if key exists, null if it does not exist or has expired.
|
||||
std::pair<MainIterator, ExpireIterator> FindExt(DbIndex db_ind, std::string_view key) const;
|
||||
|
|
|
@ -32,11 +32,14 @@ EngineShard::EngineShard(util::ProactorBase* pb)
|
|||
// absl::GetCurrentTimeNanos() returns current time since the Unix Epoch.
|
||||
shard->db_slice().UpdateExpireClock(absl::GetCurrentTimeNanos() / 1000000);
|
||||
});
|
||||
|
||||
tmp_str = sdsempty();
|
||||
}
|
||||
|
||||
EngineShard::~EngineShard() {
|
||||
queue_.Shutdown();
|
||||
fiber_q_.join();
|
||||
sdsfree(tmp_str);
|
||||
if (periodic_task_) {
|
||||
ProactorBase::me()->CancelPeriodic(periodic_task_);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
extern "C" {
|
||||
#include "redis/sds.h"
|
||||
}
|
||||
|
||||
#include <xxhash.h>
|
||||
|
||||
#include "core/tx_queue.h"
|
||||
|
@ -58,6 +62,8 @@ class EngineShard {
|
|||
return txq_.Insert(trans);
|
||||
}
|
||||
|
||||
sds tmp_str;
|
||||
|
||||
private:
|
||||
EngineShard(util::ProactorBase* pb);
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace dfly {
|
|||
std::string WrongNumArgsError(std::string_view cmd);
|
||||
|
||||
extern const char kSyntaxErr[];
|
||||
extern const char kWrongTypeErr[];
|
||||
extern const char kInvalidIntErr[];
|
||||
extern const char kUintErr[];
|
||||
extern const char kDbIndOutOfRangeErr[];
|
||||
|
|
282
server/list_family.cc
Normal file
282
server/list_family.cc
Normal file
|
@ -0,0 +1,282 @@
|
|||
// Copyright 2021, Roman Gershman. All rights reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
//
|
||||
#include "server/list_family.h"
|
||||
|
||||
extern "C" {
|
||||
#include "redis/object.h"
|
||||
#include "redis/sds.h"
|
||||
}
|
||||
|
||||
#include <absl/strings/numbers.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "server/command_registry.h"
|
||||
#include "server/conn_context.h"
|
||||
#include "server/engine_shard_set.h"
|
||||
#include "server/error.h"
|
||||
#include "server/transaction.h"
|
||||
|
||||
/**
|
||||
* The number of entries allowed per internal list node can be specified
|
||||
* as a fixed maximum size or a maximum number of elements.
|
||||
* For a fixed maximum size, use -5 through -1, meaning:
|
||||
* -5: max size: 64 Kb <-- not recommended for normal workloads
|
||||
* -4: max size: 32 Kb <-- not recommended
|
||||
* -3: max size: 16 Kb <-- probably not recommended
|
||||
* -2: max size: 8 Kb <-- good
|
||||
* -1: max size: 4 Kb <-- good
|
||||
* Positive numbers mean store up to _exactly_ that number of elements
|
||||
* per list node.
|
||||
* The highest performing option is usually -2 (8 Kb size) or -1 (4 Kb size),
|
||||
* but if your use case is unique, adjust the settings as necessary.
|
||||
*
|
||||
*/
|
||||
DEFINE_int32(list_max_listpack_size, -2, "Maximum ziplist size, default is 8kb");
|
||||
|
||||
/**
|
||||
* Lists may also be compressed.
|
||||
* Compress depth is the number of quicklist ziplist nodes from *each* side of
|
||||
* the list to *exclude* from compression. The head and tail of the list
|
||||
* are always uncompressed for fast push/pop operations. Settings are:
|
||||
* 0: disable all list compression
|
||||
* 1: depth 1 means "don't start compressing until after 1 node into the list,
|
||||
* going from either the head or tail"
|
||||
* So: [head]->node->node->...->node->[tail]
|
||||
* [head], [tail] will always be uncompressed; inner nodes will compress.
|
||||
* 2: [head]->[next]->node->node->...->node->[prev]->[tail]
|
||||
* 2 here means: don't compress head or head->next or tail->prev or tail,
|
||||
* but compress all nodes between them.
|
||||
* 3: [head]->[next]->[next]->node->node->...->node->[prev]->[prev]->[tail]
|
||||
* etc.
|
||||
*
|
||||
*/
|
||||
|
||||
DEFINE_int32(list_compress_depth, 0, "Compress depth of the list. Default is no compression");
|
||||
|
||||
namespace dfly {
|
||||
|
||||
using namespace std;
|
||||
namespace {
|
||||
|
||||
quicklist* GetQL(const MainValue& mv) {
|
||||
DCHECK(mv.robj);
|
||||
robj* o = (robj*)mv.robj;
|
||||
return (quicklist*)o->ptr;
|
||||
}
|
||||
|
||||
void* listPopSaver(unsigned char* data, size_t sz) {
|
||||
return createStringObject((char*)data, sz);
|
||||
}
|
||||
|
||||
OpResult<string> ListPop(DbIndex db_ind, const MainValue& mv, ListDir dir) {
|
||||
VLOG(1) << "Pop db_indx " << db_ind;
|
||||
|
||||
if (mv.ObjType() != OBJ_LIST)
|
||||
return OpStatus::WRONG_TYPE;
|
||||
|
||||
long long vlong;
|
||||
robj* value = NULL;
|
||||
|
||||
int ql_where = (dir == ListDir::LEFT) ? QUICKLIST_HEAD : QUICKLIST_TAIL;
|
||||
quicklist* ql = GetQL(mv);
|
||||
|
||||
// Empty list automatically removes the key (see below).
|
||||
CHECK_EQ(1,
|
||||
quicklistPopCustom(ql, ql_where, (unsigned char**)&value, NULL, &vlong, listPopSaver));
|
||||
string res;
|
||||
if (value) {
|
||||
DCHECK(value->encoding == OBJ_ENCODING_EMBSTR || value->encoding == OBJ_ENCODING_RAW);
|
||||
sds s = (sds)(value->ptr);
|
||||
res = string{s, sdslen(s)};
|
||||
decrRefCount(value);
|
||||
} else {
|
||||
res = absl::StrCat(vlong);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ListFamily::LPush(CmdArgList args, ConnectionContext* cntx) {
|
||||
return PushGeneric(ListDir::LEFT, std::move(args), cntx);
|
||||
}
|
||||
|
||||
void ListFamily::LPop(CmdArgList args, ConnectionContext* cntx) {
|
||||
return PopGeneric(ListDir::LEFT, std::move(args), cntx);
|
||||
}
|
||||
|
||||
void ListFamily::RPush(CmdArgList args, ConnectionContext* cntx) {
|
||||
return PushGeneric(ListDir::RIGHT, std::move(args), cntx);
|
||||
}
|
||||
|
||||
void ListFamily::RPop(CmdArgList args, ConnectionContext* cntx) {
|
||||
return PopGeneric(ListDir::RIGHT, std::move(args), cntx);
|
||||
}
|
||||
|
||||
void ListFamily::LLen(CmdArgList args, ConnectionContext* cntx) {
|
||||
auto key = ArgS(args, 1);
|
||||
auto cb = [&](Transaction* t, EngineShard* shard) {
|
||||
return OpLen(OpArgs{shard, t->db_index()}, key);
|
||||
};
|
||||
OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
|
||||
if (result) {
|
||||
cntx->SendLong(result.value());
|
||||
} else if (result.status() == OpStatus::KEY_NOTFOUND) {
|
||||
cntx->SendLong(0);
|
||||
} else {
|
||||
cntx->SendError(result.status());
|
||||
}
|
||||
}
|
||||
|
||||
void ListFamily::LIndex(CmdArgList args, ConnectionContext* cntx) {
|
||||
std::string_view key = ArgS(args, 1);
|
||||
std::string_view index_str = ArgS(args, 2);
|
||||
int32_t index;
|
||||
if (!absl::SimpleAtoi(index_str, &index)) {
|
||||
cntx->SendError(kInvalidIntErr);
|
||||
return;
|
||||
}
|
||||
|
||||
auto cb = [&](Transaction* t, EngineShard* shard) {
|
||||
return OpIndex(OpArgs{shard, t->db_index()}, key, index);
|
||||
};
|
||||
OpResult<string> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
|
||||
if (result) {
|
||||
cntx->SendBulkString(result.value());
|
||||
} else {
|
||||
cntx->SendNull();
|
||||
}
|
||||
}
|
||||
|
||||
void ListFamily::PushGeneric(ListDir dir, const CmdArgList& args, ConnectionContext* cntx) {
|
||||
std::string_view key = ArgS(args, 1);
|
||||
vector<std::string_view> vals(args.size() - 2);
|
||||
for (size_t i = 2; i < args.size(); ++i) {
|
||||
vals[i - 2] = ArgS(args, i);
|
||||
}
|
||||
absl::Span<std::string_view> span{vals.data(), vals.size()};
|
||||
auto cb = [&](Transaction* t, EngineShard* shard) {
|
||||
return OpPush(OpArgs{shard, t->db_index()}, key, dir, span);
|
||||
};
|
||||
|
||||
OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
|
||||
switch (result.status()) {
|
||||
case OpStatus::KEY_NOTFOUND:
|
||||
return cntx->SendNull();
|
||||
case OpStatus::WRONG_TYPE:
|
||||
return cntx->SendError(kWrongTypeErr);
|
||||
default:;
|
||||
}
|
||||
|
||||
return cntx->SendLong(result.value());
|
||||
}
|
||||
|
||||
void ListFamily::PopGeneric(ListDir dir, const CmdArgList& args, ConnectionContext* cntx) {
|
||||
std::string_view key = ArgS(args, 1);
|
||||
|
||||
auto cb = [&](Transaction* t, EngineShard* shard) {
|
||||
return OpPop(OpArgs{shard, t->db_index()}, key, dir);
|
||||
};
|
||||
|
||||
OpResult<string> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
|
||||
|
||||
switch (result.status()) {
|
||||
case OpStatus::KEY_NOTFOUND:
|
||||
return cntx->SendNull();
|
||||
case OpStatus::WRONG_TYPE:
|
||||
return cntx->SendError(kWrongTypeErr);
|
||||
default:;
|
||||
}
|
||||
|
||||
return cntx->SendBulkString(result.value());
|
||||
}
|
||||
|
||||
OpResult<uint32_t> ListFamily::OpPush(const OpArgs& op_args, std::string_view key, ListDir dir,
|
||||
const absl::Span<std::string_view>& vals) {
|
||||
EngineShard* es = op_args.shard;
|
||||
auto [it, new_key] = es->db_slice().AddOrFind(op_args.db_ind, key);
|
||||
quicklist* ql;
|
||||
|
||||
if (new_key) {
|
||||
robj* o = createQuicklistObject();
|
||||
ql = (quicklist*)o->ptr;
|
||||
quicklistSetOptions(ql, FLAGS_list_max_listpack_size, FLAGS_list_compress_depth);
|
||||
it->second.robj = o;
|
||||
it->second.obj_type = OBJ_LIST;
|
||||
} else {
|
||||
if (it->second.ObjType() != OBJ_LIST)
|
||||
return OpStatus::WRONG_TYPE;
|
||||
ql = GetQL(it->second);
|
||||
}
|
||||
|
||||
// Left push is LIST_HEAD.
|
||||
int pos = (dir == ListDir::LEFT) ? QUICKLIST_HEAD : QUICKLIST_TAIL;
|
||||
|
||||
for (auto v : vals) {
|
||||
es->tmp_str = sdscpylen(es->tmp_str, v.data(), v.size());
|
||||
quicklistPush(ql, es->tmp_str, sdslen(es->tmp_str), pos);
|
||||
}
|
||||
|
||||
return quicklistCount(ql);
|
||||
}
|
||||
|
||||
OpResult<string> ListFamily::OpPop(const OpArgs& op_args, string_view key, ListDir dir) {
|
||||
auto& db_slice = op_args.shard->db_slice();
|
||||
auto [it, expire] = db_slice.FindExt(op_args.db_ind, key);
|
||||
if (it == MainIterator{})
|
||||
return OpStatus::KEY_NOTFOUND;
|
||||
|
||||
OpResult<string> res = ListPop(op_args.db_ind, it->second, dir);
|
||||
if (res) {
|
||||
quicklist* ql = GetQL(it->second);
|
||||
if (quicklistCount(ql) == 0) {
|
||||
CHECK(db_slice.Del(op_args.db_ind, it));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
OpResult<uint32_t> ListFamily::OpLen(const OpArgs& op_args, std::string_view key) {
|
||||
auto res = op_args.shard->db_slice().Find(op_args.db_ind, key, OBJ_LIST);
|
||||
if (!res)
|
||||
return res.status();
|
||||
|
||||
quicklist* ql = GetQL(res.value()->second);
|
||||
|
||||
return quicklistCount(ql);
|
||||
}
|
||||
|
||||
OpResult<string> ListFamily::OpIndex(const OpArgs& op_args, std::string_view key, long index) {
|
||||
auto res = op_args.shard->db_slice().Find(op_args.db_ind, key, OBJ_LIST);
|
||||
if (!res)
|
||||
return res.status();
|
||||
quicklist* ql = GetQL(res.value()->second);
|
||||
quicklistEntry entry;
|
||||
quicklistIter* iter = quicklistGetIteratorEntryAtIdx(ql, index, &entry);
|
||||
|
||||
if (!iter)
|
||||
return OpStatus::KEY_NOTFOUND;
|
||||
|
||||
if (entry.value) {
|
||||
return string{reinterpret_cast<char*>(entry.value), entry.sz};
|
||||
} else {
|
||||
return absl::StrCat(entry.longval);
|
||||
}
|
||||
}
|
||||
|
||||
using CI = CommandId;
|
||||
|
||||
#define HFUNC(x) SetHandler(&ListFamily::x)
|
||||
|
||||
void ListFamily::Register(CommandRegistry* registry) {
|
||||
*registry << CI{"LPUSH", CO::WRITE | CO::FAST | CO::DENYOOM, -3, 1, 1, 1}.HFUNC(LPush)
|
||||
<< CI{"LPOP", CO::WRITE | CO::FAST | CO::DENYOOM, 2, 1, 1, 1}.HFUNC(LPop)
|
||||
<< CI{"RPUSH", CO::WRITE | CO::FAST | CO::DENYOOM, -3, 1, 1, 1}.HFUNC(RPush)
|
||||
<< CI{"RPOP", CO::WRITE | CO::FAST | CO::DENYOOM, 2, 1, 1, 1}.HFUNC(RPop)
|
||||
<< CI{"LLEN", CO::READONLY | CO::FAST, 2, 1, 1, 1}.HFUNC(LLen)
|
||||
<< CI{"LINDEX", CO::READONLY, 3, 1, 1, 1}.HFUNC(LIndex);
|
||||
}
|
||||
|
||||
} // namespace dfly
|
38
server/list_family.h
Normal file
38
server/list_family.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2021, Roman Gershman. All rights reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/op_status.h"
|
||||
#include "server/common_types.h"
|
||||
|
||||
namespace dfly {
|
||||
|
||||
class ConnectionContext;
|
||||
class CommandRegistry;
|
||||
class EngineShard;
|
||||
|
||||
class ListFamily {
|
||||
public:
|
||||
static void Register(CommandRegistry* registry);
|
||||
|
||||
private:
|
||||
static void LPush(CmdArgList args, ConnectionContext* cntx);
|
||||
static void RPush(CmdArgList args, ConnectionContext* cntx);
|
||||
static void LPop(CmdArgList args, ConnectionContext* cntx);
|
||||
static void RPop(CmdArgList args, ConnectionContext* cntx);
|
||||
static void LLen(CmdArgList args, ConnectionContext* cntx);
|
||||
static void LIndex(CmdArgList args, ConnectionContext* cntx);
|
||||
|
||||
static void PopGeneric(ListDir dir, const CmdArgList& args, ConnectionContext* cntx);
|
||||
static void PushGeneric(ListDir dir, const CmdArgList& args, ConnectionContext* cntx);
|
||||
|
||||
static OpResult<uint32_t> OpPush(const OpArgs& op_args, std::string_view key, ListDir dir,
|
||||
const absl::Span<std::string_view>& vals);
|
||||
static OpResult<std::string> OpPop(const OpArgs& op_args, std::string_view key, ListDir dir);
|
||||
static OpResult<uint32_t> OpLen(const OpArgs& op_args, std::string_view key);
|
||||
static OpResult<std::string> OpIndex(const OpArgs& op_args, std::string_view key, long index);
|
||||
};
|
||||
|
||||
} // namespace dfly
|
|
@ -15,6 +15,7 @@
|
|||
#include "server/debugcmd.h"
|
||||
#include "server/error.h"
|
||||
#include "server/generic_family.h"
|
||||
#include "server/list_family.h"
|
||||
#include "server/string_family.h"
|
||||
#include "server/transaction.h"
|
||||
#include "util/metrics/metrics.h"
|
||||
|
@ -207,6 +208,7 @@ void Service::RegisterCommands() {
|
|||
|
||||
StringFamily::Register(®istry_);
|
||||
GenericFamily::Register(®istry_);
|
||||
ListFamily::Register(®istry_);
|
||||
}
|
||||
|
||||
} // namespace dfly
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
|
||||
#include "server/string_family.h"
|
||||
|
||||
extern "C" {
|
||||
#include "redis/object.h"
|
||||
}
|
||||
|
||||
#include <absl/container/inlined_vector.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
|
@ -46,6 +50,8 @@ OpResult<void> SetCmd::Set(const SetParams& params, std::string_view key, std::s
|
|||
return OpStatus::SKIPPED;
|
||||
|
||||
if (params.prev_val) {
|
||||
if (it->second.ObjType() != OBJ_STRING)
|
||||
return OpStatus::WRONG_TYPE;
|
||||
params.prev_val->emplace(it->second.str);
|
||||
}
|
||||
|
||||
|
@ -68,7 +74,12 @@ OpResult<void> SetCmd::SetExisting(DbIndex db_ind, std::string_view value, uint6
|
|||
db_slice_->Expire(db_ind, dest, expire_at_ms);
|
||||
}
|
||||
|
||||
if (dest->second.robj) {
|
||||
decrRefCountVoid(dest->second.robj);
|
||||
dest->second.robj = nullptr;
|
||||
}
|
||||
dest->second = value;
|
||||
dest->second.obj_type = OBJ_STRING;
|
||||
|
||||
return OpStatus::OK;
|
||||
}
|
||||
|
@ -139,7 +150,7 @@ void StringFamily::Get(CmdArgList args, ConnectionContext* cntx) {
|
|||
std::string_view key = ArgS(args, 1);
|
||||
|
||||
auto cb = [&](Transaction* t, EngineShard* shard) -> OpResult<string> {
|
||||
OpResult<MainIterator> it_res = shard->db_slice().Find(cntx->db_index(), key);
|
||||
OpResult<MainIterator> it_res = shard->db_slice().Find(cntx->db_index(), key, OBJ_STRING);
|
||||
if (!it_res.ok())
|
||||
return it_res.status();
|
||||
|
||||
|
@ -156,8 +167,14 @@ void StringFamily::Get(CmdArgList args, ConnectionContext* cntx) {
|
|||
DVLOG(1) << "GET " << trans->DebugId() << ": " << key << " " << result.value();
|
||||
cntx->SendGetReply(key, 0, result.value());
|
||||
} else {
|
||||
DVLOG(1) << "GET " << key << " nil";
|
||||
cntx->SendGetNotFound();
|
||||
switch (result.status()) {
|
||||
case OpStatus::WRONG_TYPE:
|
||||
cntx->SendError(kWrongTypeErr);
|
||||
break;
|
||||
default:
|
||||
DVLOG(1) << "GET " << key << " nil";
|
||||
cntx->SendGetNotFound();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -253,9 +270,9 @@ auto StringFamily::OpMGet(const Transaction* t, EngineShard* shard) -> MGetRespo
|
|||
|
||||
auto& db_slice = shard->db_slice();
|
||||
for (size_t i = 0; i < args.size(); ++i) {
|
||||
OpResult<MainIterator> de_res = db_slice.Find(0, args[i]);
|
||||
if (de_res.ok()) {
|
||||
response[i] = de_res.value()->second.str;
|
||||
OpResult<MainIterator> it_res = db_slice.Find(t->db_index(), args[i], OBJ_STRING);
|
||||
if (it_res.ok()) {
|
||||
response[i] = it_res.value()->second.str;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,7 +283,7 @@ OpStatus StringFamily::OpMSet(const Transaction* t, EngineShard* es) {
|
|||
ArgSlice largs = t->ShardArgsInShard(es->shard_id());
|
||||
CHECK(!largs.empty() && largs.size() % 2 == 0);
|
||||
|
||||
SetCmd::SetParams params{0};
|
||||
SetCmd::SetParams params{t->db_index()};
|
||||
SetCmd sg(&es->db_slice());
|
||||
for (size_t i = 0; i < largs.size(); i += 2) {
|
||||
DVLOG(1) << "MSet " << largs[i] << ":" << largs[i + 1];
|
||||
|
|
|
@ -10,6 +10,8 @@ namespace dfly {
|
|||
|
||||
struct MainValue {
|
||||
std::string str;
|
||||
void* robj = nullptr;
|
||||
uint8_t obj_type = 0;
|
||||
|
||||
MainValue() = default;
|
||||
MainValue(std::string_view s) : str(s) {
|
||||
|
@ -23,6 +25,10 @@ struct MainValue {
|
|||
has_expire_ = b;
|
||||
}
|
||||
|
||||
unsigned ObjType() const {
|
||||
return obj_type;
|
||||
}
|
||||
|
||||
private:
|
||||
bool has_expire_ = false;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue