diff --git a/api/librdb-api.h b/api/librdb-api.h index 53a2880..9c59746 100644 --- a/api/librdb-api.h +++ b/api/librdb-api.h @@ -68,6 +68,8 @@ typedef enum RdbRes { RDB_ERR_LIST_ZL_INTEG_CHECK, RDB_ERR_SET_IS_INTEG_CHECK, RDB_ERR_SET_LP_INTEG_CHECK, + RDB_ERR_ZSET_ZL_INTEG_CHECK, + RDB_ERR_ZSET_LP_INTEG_CHECK, RDB_ERR_HASH_LP_INTEG_CHECK, RDB_ERR_HASH_ZM_INTEG_CHECK, RDB_ERR_SSTYPE_INTEG_CHECK, @@ -75,6 +77,7 @@ typedef enum RdbRes { RDB_ERR_PLAIN_HASH_INVALID_STATE, RDB_ERR_PLAIN_LIST_INVALID_STATE, RDB_ERR_PLAIN_SET_INVALID_STATE, + RDB_ERR_PLAIN_ZSET_INVALID_STATE, RDB_ERR_QUICK_LIST_INVALID_STATE, RDB_ERR_SSTYPE_INVALID_STATE, RDB_ERR_MODULE_INVALID_STATE, @@ -255,6 +258,14 @@ typedef struct RdbHandlersStructCallbacks { RdbRes (*handleSetIS)(RdbParser *p, void *userData, RdbBulk intset); /* Callback to handle a listpack-based set value */ RdbRes (*handleSetLP)(RdbParser *p, void *userData, RdbBulk listpack); + + /* Callback to handle an item from a plain sorted set */ + RdbRes (*handleZsetPlain)(RdbParser *p, void *userData, RdbBulk item, double score); + /* Callback to handle a ziplist-based sorted set value */ + RdbRes (*handleZsetZL)(RdbParser *p, void *userData, RdbBulk ziplist); + /* Callback to handle a listpack-based sorted set value */ + RdbRes (*handleZsetLP)(RdbParser *p, void *userData, RdbBulk listpack); + /* Callback to handle function code */ RdbRes (*handleFunction)(RdbParser *p, void *userData, RdbBulk func); /* Callback to handle module. Currently only reports about the name & size. */ @@ -262,11 +273,6 @@ typedef struct RdbHandlersStructCallbacks { /*** TODO: RdbHandlersStructCallbacks: ***/ - /* Callback to handle a ziplist-based sorted set value */ - RdbRes (*handleZsetZL)(RdbParser *p, void *userData, RdbBulk ziplist); - /* Callback to handle a listpack-based sorted set value */ - RdbRes (*handleZsetLP)(RdbParser *p, void *userData, RdbBulk listpack); - /*** TODO: RdbHandlersStructCallbacks: stream stuff ... ***/ /* Callback to handle a stream key with listpack value */ diff --git a/deps/redis/fpconv_dtoa.c b/deps/redis/fpconv_dtoa.c new file mode 100644 index 0000000..ad2f2de --- /dev/null +++ b/deps/redis/fpconv_dtoa.c @@ -0,0 +1,373 @@ +/* fpconv_dtoa.c -- floating point conversion utilities. + * + * Fast and accurate double to string conversion based on Florian Loitsch's + * Grisu-algorithm[1]. + * + * [1] https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf + * ---------------------------------------------------------------------------- + * + * Copyright (c) 2013-2019, night-shift + * Copyright (c) 2009, Florian Loitsch < florian.loitsch at inria dot fr > + * All rights reserved. + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "fpconv_dtoa.h" + +#include "fpconv_powers.h" + +#include +#include + +#define fracmask 0x000FFFFFFFFFFFFFU +#define expmask 0x7FF0000000000000U +#define hiddenbit 0x0010000000000000U +#define signmask 0x8000000000000000U +#define expbias (1023 + 52) + +#define absv(n) ((n) < 0 ? -(n) : (n)) +#define minv(a, b) ((a) < (b) ? (a) : (b)) + +static uint64_t tens[] = { 10000000000000000000U, + 1000000000000000000U, + 100000000000000000U, + 10000000000000000U, + 1000000000000000U, + 100000000000000U, + 10000000000000U, + 1000000000000U, + 100000000000U, + 10000000000U, + 1000000000U, + 100000000U, + 10000000U, + 1000000U, + 100000U, + 10000U, + 1000U, + 100U, + 10U, + 1U }; + +static inline uint64_t get_dbits(double d) { + union + { + double dbl; + uint64_t i; + } dbl_bits = { d }; + + return dbl_bits.i; +} + +static Fp build_fp(double d) { + uint64_t bits = get_dbits(d); + + Fp fp; + fp.frac = bits & fracmask; + fp.exp = (bits & expmask) >> 52; + + if (fp.exp) { + fp.frac += hiddenbit; + fp.exp -= expbias; + + } else { + fp.exp = -expbias + 1; + } + + return fp; +} + +static void normalize(Fp *fp) { + while ((fp->frac & hiddenbit) == 0) { + fp->frac <<= 1; + fp->exp--; + } + + int shift = 64 - 52 - 1; + fp->frac <<= shift; + fp->exp -= shift; +} + +static void get_normalized_boundaries(Fp *fp, Fp *lower, Fp *upper) { + upper->frac = (fp->frac << 1) + 1; + upper->exp = fp->exp - 1; + + while ((upper->frac & (hiddenbit << 1)) == 0) { + upper->frac <<= 1; + upper->exp--; + } + + int u_shift = 64 - 52 - 2; + + upper->frac <<= u_shift; + upper->exp = upper->exp - u_shift; + + int l_shift = fp->frac == hiddenbit ? 2 : 1; + + lower->frac = (fp->frac << l_shift) - 1; + lower->exp = fp->exp - l_shift; + + lower->frac <<= lower->exp - upper->exp; + lower->exp = upper->exp; +} + +static Fp multiply(Fp *a, Fp *b) { + const uint64_t lomask = 0x00000000FFFFFFFF; + + uint64_t ah_bl = (a->frac >> 32) * (b->frac & lomask); + uint64_t al_bh = (a->frac & lomask) * (b->frac >> 32); + uint64_t al_bl = (a->frac & lomask) * (b->frac & lomask); + uint64_t ah_bh = (a->frac >> 32) * (b->frac >> 32); + + uint64_t tmp = (ah_bl & lomask) + (al_bh & lomask) + (al_bl >> 32); + /* round up */ + tmp += 1U << 31; + + Fp fp = { ah_bh + (ah_bl >> 32) + (al_bh >> 32) + (tmp >> 32), a->exp + b->exp + 64 }; + + return fp; +} + +static void round_digit(char *digits, + int ndigits, + uint64_t delta, + uint64_t rem, + uint64_t kappa, + uint64_t frac) { + while (rem < frac && delta - rem >= kappa && + (rem + kappa < frac || frac - rem > rem + kappa - frac)) { + digits[ndigits - 1]--; + rem += kappa; + } +} + +static int generate_digits(Fp *fp, Fp *upper, Fp *lower, char *digits, int *K) { + uint64_t wfrac = upper->frac - fp->frac; + uint64_t delta = upper->frac - lower->frac; + + Fp one; + one.frac = 1ULL << -upper->exp; + one.exp = upper->exp; + + uint64_t part1 = upper->frac >> -one.exp; + uint64_t part2 = upper->frac & (one.frac - 1); + + int idx = 0, kappa = 10; + uint64_t *divp; + /* 1000000000 */ + for (divp = tens + 10; kappa > 0; divp++) { + uint64_t div = *divp; + unsigned digit = part1 / div; + + if (digit || idx) { + digits[idx++] = digit + '0'; + } + + part1 -= digit * div; + kappa--; + + uint64_t tmp = (part1 << -one.exp) + part2; + if (tmp <= delta) { + *K += kappa; + round_digit(digits, idx, delta, tmp, div << -one.exp, wfrac); + + return idx; + } + } + + /* 10 */ + uint64_t *unit = tens + 18; + + while (true) { + part2 *= 10; + delta *= 10; + kappa--; + + unsigned digit = part2 >> -one.exp; + if (digit || idx) { + digits[idx++] = digit + '0'; + } + + part2 &= one.frac - 1; + if (part2 < delta) { + *K += kappa; + round_digit(digits, idx, delta, part2, one.frac, wfrac * *unit); + + return idx; + } + + unit--; + } +} + +static int grisu2(double d, char *digits, int *K) { + Fp w = build_fp(d); + + Fp lower, upper; + get_normalized_boundaries(&w, &lower, &upper); + + normalize(&w); + + int k; + Fp cp = find_cachedpow10(upper.exp, &k); + + w = multiply(&w, &cp); + upper = multiply(&upper, &cp); + lower = multiply(&lower, &cp); + + lower.frac++; + upper.frac--; + + *K = -k; + + return generate_digits(&w, &upper, &lower, digits, K); +} + +static int emit_digits(char *digits, int ndigits, char *dest, int K, bool neg) { + int exp = absv(K + ndigits - 1); + + /* write plain integer */ + if (K >= 0 && (exp < (ndigits + 7))) { + memcpy(dest, digits, ndigits); + memset(dest + ndigits, '0', K); + + return ndigits + K; + } + + /* write decimal w/o scientific notation */ + if (K < 0 && (K > -7 || exp < 4)) { + int offset = ndigits - absv(K); + /* fp < 1.0 -> write leading zero */ + if (offset <= 0) { + offset = -offset; + dest[0] = '0'; + dest[1] = '.'; + memset(dest + 2, '0', offset); + memcpy(dest + offset + 2, digits, ndigits); + + return ndigits + 2 + offset; + + /* fp > 1.0 */ + } else { + memcpy(dest, digits, offset); + dest[offset] = '.'; + memcpy(dest + offset + 1, digits + offset, ndigits - offset); + + return ndigits + 1; + } + } + + /* write decimal w/ scientific notation */ + ndigits = minv(ndigits, 18 - neg); + + int idx = 0; + dest[idx++] = digits[0]; + + if (ndigits > 1) { + dest[idx++] = '.'; + memcpy(dest + idx, digits + 1, ndigits - 1); + idx += ndigits - 1; + } + + dest[idx++] = 'e'; + + char sign = K + ndigits - 1 < 0 ? '-' : '+'; + dest[idx++] = sign; + + int cent = 0; + + if (exp > 99) { + cent = exp / 100; + dest[idx++] = cent + '0'; + exp -= cent * 100; + } + if (exp > 9) { + int dec = exp / 10; + dest[idx++] = dec + '0'; + exp -= dec * 10; + + } else if (cent) { + dest[idx++] = '0'; + } + + dest[idx++] = exp % 10 + '0'; + + return idx; +} + +static int filter_special(double fp, char *dest) { + if (fp == 0.0) { + dest[0] = '0'; + return 1; + } + + uint64_t bits = get_dbits(fp); + + bool nan = (bits & expmask) == expmask; + + if (!nan) { + return 0; + } + + if (bits & fracmask) { + dest[0] = 'n'; + dest[1] = 'a'; + dest[2] = 'n'; + + } else { + dest[0] = 'i'; + dest[1] = 'n'; + dest[2] = 'f'; + } + + return 3; +} + +int fpconv_dtoa(double d, char dest[24]) { + char digits[18]; + + int str_len = 0; + bool neg = false; + + if (get_dbits(d) & signmask) { + dest[0] = '-'; + str_len++; + neg = true; + } + + int spec = filter_special(d, dest + str_len); + + if (spec) { + return str_len + spec; + } + + int K = 0; + int ndigits = grisu2(d, digits, &K); + + str_len += emit_digits(digits, ndigits, dest + str_len, K, neg); + + return str_len; +} diff --git a/deps/redis/fpconv_dtoa.h b/deps/redis/fpconv_dtoa.h new file mode 100644 index 0000000..328ed83 --- /dev/null +++ b/deps/redis/fpconv_dtoa.h @@ -0,0 +1,45 @@ +/* fpconv_dtoa.h -- floating point conversion utilities. + * + * Fast and accurate double to string conversion based on Florian Loitsch's + * Grisu-algorithm[1]. + * + * [1] https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf + * ---------------------------------------------------------------------------- + * + * Copyright (c) 2013-2019, night-shift + * Copyright (c) 2009, Florian Loitsch < florian.loitsch at inria dot fr > + * All rights reserved. + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef FPCONV_DTOA_H +#define FPCONV_DTOA_H + +int fpconv_dtoa(double fp, char dest[24]); + +#endif + +/* [1] http://florian.loitsch.com/publications/dtoa-pldi2010.pdf */ diff --git a/deps/redis/fpconv_powers.h b/deps/redis/fpconv_powers.h new file mode 100644 index 0000000..bc488f6 --- /dev/null +++ b/deps/redis/fpconv_powers.h @@ -0,0 +1,133 @@ +/* fpconv_powers.h -- floating point conversion utilities. + * + * Fast and accurate double to string conversion based on Florian Loitsch's + * Grisu-algorithm[1]. + * + * [1] https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf + * ---------------------------------------------------------------------------- + * + * Copyright (c) 2021, Redis Labs + * Copyright (c) 2013-2019, night-shift + * Copyright (c) 2009, Florian Loitsch < florian.loitsch at inria dot fr > + * All rights reserved. + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include + +#define npowers 87 +#define steppowers 8 +#define firstpower -348 /* 10 ^ -348 */ + +#define expmax -32 +#define expmin -60 + + +typedef struct Fp { + uint64_t frac; + int exp; +} Fp; + +static Fp powers_ten[] = { + { 18054884314459144840U, -1220 }, { 13451937075301367670U, -1193 }, + { 10022474136428063862U, -1166 }, { 14934650266808366570U, -1140 }, + { 11127181549972568877U, -1113 }, { 16580792590934885855U, -1087 }, + { 12353653155963782858U, -1060 }, { 18408377700990114895U, -1034 }, + { 13715310171984221708U, -1007 }, { 10218702384817765436U, -980 }, + { 15227053142812498563U, -954 }, { 11345038669416679861U, -927 }, + { 16905424996341287883U, -901 }, { 12595523146049147757U, -874 }, + { 9384396036005875287U, -847 }, { 13983839803942852151U, -821 }, + { 10418772551374772303U, -794 }, { 15525180923007089351U, -768 }, + { 11567161174868858868U, -741 }, { 17236413322193710309U, -715 }, + { 12842128665889583758U, -688 }, { 9568131466127621947U, -661 }, + { 14257626930069360058U, -635 }, { 10622759856335341974U, -608 }, + { 15829145694278690180U, -582 }, { 11793632577567316726U, -555 }, + { 17573882009934360870U, -529 }, { 13093562431584567480U, -502 }, + { 9755464219737475723U, -475 }, { 14536774485912137811U, -449 }, + { 10830740992659433045U, -422 }, { 16139061738043178685U, -396 }, + { 12024538023802026127U, -369 }, { 17917957937422433684U, -343 }, + { 13349918974505688015U, -316 }, { 9946464728195732843U, -289 }, + { 14821387422376473014U, -263 }, { 11042794154864902060U, -236 }, + { 16455045573212060422U, -210 }, { 12259964326927110867U, -183 }, + { 18268770466636286478U, -157 }, { 13611294676837538539U, -130 }, + { 10141204801825835212U, -103 }, { 15111572745182864684U, -77 }, + { 11258999068426240000U, -50 }, { 16777216000000000000U, -24 }, + { 12500000000000000000U, 3 }, { 9313225746154785156U, 30 }, + { 13877787807814456755U, 56 }, { 10339757656912845936U, 83 }, + { 15407439555097886824U, 109 }, { 11479437019748901445U, 136 }, + { 17105694144590052135U, 162 }, { 12744735289059618216U, 189 }, + { 9495567745759798747U, 216 }, { 14149498560666738074U, 242 }, + { 10542197943230523224U, 269 }, { 15709099088952724970U, 295 }, + { 11704190886730495818U, 322 }, { 17440603504673385349U, 348 }, + { 12994262207056124023U, 375 }, { 9681479787123295682U, 402 }, + { 14426529090290212157U, 428 }, { 10748601772107342003U, 455 }, + { 16016664761464807395U, 481 }, { 11933345169920330789U, 508 }, + { 17782069995880619868U, 534 }, { 13248674568444952270U, 561 }, + { 9871031767461413346U, 588 }, { 14708983551653345445U, 614 }, + { 10959046745042015199U, 641 }, { 16330252207878254650U, 667 }, + { 12166986024289022870U, 694 }, { 18130221999122236476U, 720 }, + { 13508068024458167312U, 747 }, { 10064294952495520794U, 774 }, + { 14996968138956309548U, 800 }, { 11173611982879273257U, 827 }, + { 16649979327439178909U, 853 }, { 12405201291620119593U, 880 }, + { 9242595204427927429U, 907 }, { 13772540099066387757U, 933 }, + { 10261342003245940623U, 960 }, { 15290591125556738113U, 986 }, + { 11392378155556871081U, 1013 }, { 16975966327722178521U, 1039 }, + { 12648080533535911531U, 1066 } +}; + +/** + * Grisu needs a cache of precomputed powers-of-ten. + * This function, given an exponent and an integer k + * return the normalized floating-point approximation of the power of 10. + * @param exp + * @param k + * @return + */ +static Fp find_cachedpow10(int exp, int* k) +{ + const double one_log_ten = 0.30102999566398114; + + const int approx = -(exp + npowers) * one_log_ten; + int idx = (approx - firstpower) / steppowers; + + while(1) { + int current = exp + powers_ten[idx].exp + 64; + + if(current < expmin) { + idx++; + continue; + } + + if(current > expmax) { + idx--; + continue; + } + + *k = (firstpower + idx * steppowers); + + return powers_ten[idx]; + } +} diff --git a/deps/redis/t_zset.c b/deps/redis/t_zset.c new file mode 100644 index 0000000..ff03d7c --- /dev/null +++ b/deps/redis/t_zset.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2009-2012, Salvatore Sanfilippo + * Copyright (c) 2009-2012, Pieter Noordhuis + * 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 +#include + +double zzlStrtod(unsigned char *vstr, unsigned int vlen) { + char buf[128]; + if (vlen > sizeof(buf) - 1) + vlen = sizeof(buf) - 1; + memcpy(buf,vstr,vlen); + buf[vlen] = '\0'; + return strtod(buf,NULL); +} diff --git a/deps/redis/t_zset.h b/deps/redis/t_zset.h new file mode 100644 index 0000000..679bc88 --- /dev/null +++ b/deps/redis/t_zset.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2009-2012, Salvatore Sanfilippo + * Copyright (c) 2009-2012, Pieter Noordhuis + * 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 LIBRDB_T_ZSET_H +#define LIBRDB_T_ZSET_H + +double zzlStrtod(unsigned char *vstr, unsigned int vlen); + +#endif diff --git a/deps/redis/util.c b/deps/redis/util.c index 735b130..3524577 100644 --- a/deps/redis/util.c +++ b/deps/redis/util.c @@ -3,7 +3,11 @@ #include #include #include +#include +#include +#include #include "util.h" +#include "fpconv_dtoa.h" /* Return the number of digits of 'v' when converted to string in radix 10. * See ll2string() for more information. */ @@ -202,3 +206,75 @@ int lpStringToInt64(const char *s, unsigned long slen, int64_t *value) { } return 1; } + +/* Returns 1 if the double value can safely be represented in long long without + * precision loss, in which case the corresponding long long is stored in the out variable. */ +static int double2ll(double d, long long *out) { + (void) out; +#if (DBL_MANT_DIG >= 52) && (DBL_MANT_DIG <= 63) && (LLONG_MAX == 0x7fffffffffffffffLL) + /* Check if the float is in a safe range to be casted into a + * long long. We are assuming that long long is 64 bit here. + * Also we are assuming that there are no implementations around where + * double has precision < 52 bit. + * + * Under this assumptions we test if a double is inside a range + * where casting to long long is safe. Then using two castings we + * make sure the decimal part is zero. If all this is true we can use + * integer without precision loss. + * + * Note that numbers above 2^52 and below 2^63 use all the fraction bits as real part, + * and the exponent bits are positive, which means the "decimal" part must be 0. + * i.e. all double values in that range are representable as a long without precision loss, + * but not all long values in that range can be represented as a double. + * we only care about the first part here. */ + if (d < (double)(-LLONG_MAX/2) || d > (double)(LLONG_MAX/2)) + return 0; + long long ll = d; + if (ll == d) { + *out = ll; + return 1; + } +#else + (void) out; + (void) d; +#endif + return 0; +} + +/* Convert a double to a string representation. Returns the number of bytes + * required. The representation should always be parsable by strtod(3). + * This function does not support human-friendly formatting like ld2string + * does. It is intended mainly to be used inside t_zset.c when writing scores + * into a listpack representing a sorted set. */ +int d2string(char *buf, size_t len, double value) { + if (isnan(value)) { + /* Libc in some systems will format nan in a different way, + * like nan, -nan, NAN, nan(char-sequence). + * So we normalize it and create a single nan form in an explicit way. */ + len = snprintf(buf,len,"nan"); + } else if (isinf(value)) { + /* Libc in odd systems (Hi Solaris!) will format infinite in a + * different way, so better to handle it in an explicit way. */ + if (value < 0) + len = snprintf(buf,len,"-inf"); + else + len = snprintf(buf,len,"inf"); + } else if (value == 0) { + /* See: http://en.wikipedia.org/wiki/Signed_zero, "Comparisons". */ + if (1.0/value < 0) + len = snprintf(buf,len,"-0"); + else + len = snprintf(buf,len,"0"); + } else { + long long lvalue; + /* Integer printing function is much faster, check if we can safely use it. */ + if (double2ll(value, &lvalue)) + len = ll2string(buf,len,lvalue); + else { + len = fpconv_dtoa(value, buf); + buf[len] = '\0'; + } + } + + return len; +} diff --git a/deps/redis/util.h b/deps/redis/util.h index c6d5b8f..39b8162 100644 --- a/deps/redis/util.h +++ b/deps/redis/util.h @@ -14,9 +14,20 @@ /* Bytes needed for long -> str + '\0' */ #define LONG_STR_SIZE 21 +/* The maximum number of characters needed to for d2string/fpconv_dtoa call. + * Since it uses %g and not %f, some 40 chars should be enough. */ +#define MAX_D2STRING_CHARS 128 + int ll2string(char *s, size_t len, long long value); int ull2string(char *s, size_t len, unsigned long long value); int lpStringToInt64(const char *s, unsigned long slen, int64_t *value); unsigned int getEnvVar(const char* varName, unsigned int defaultVal); +/* This is the exact function that is used in redis zset implementation for + * double <-> string conversions. Using some other function may result in + * incompatibilities as you can also convert double to string that results in + * loss of precision, or it might not represent inf, -inf or nan values + * similar to this function output. */ +int d2string(char *buf, size_t len, double value); + #endif /*LIBRDB_UTIL_H*/ diff --git a/src/ext/handlersToJson.c b/src/ext/handlersToJson.c index e8e1450..01d910f 100644 --- a/src/ext/handlersToJson.c +++ b/src/ext/handlersToJson.c @@ -4,6 +4,8 @@ #include #include "common.h" +#include "../../deps/redis/util.h" + struct RdbxToJson; #define _RDB_TYPE_MODULE_2 7 @@ -343,6 +345,38 @@ static RdbRes toJsonSet(RdbParser *p, void *userData, RdbBulk member) { return RDB_OK; } +static RdbRes toJsonZset(RdbParser *p, void *userData, RdbBulk member, double score) { + RdbxToJson *ctx = userData; + + char score_str[MAX_D2STRING_CHARS]; + int len = d2string(score_str, sizeof(score_str), score); + + if (ctx->state == R2J_IN_KEY) { + /* output json part */ + fprintf(ctx->outfile, "{"); + outputQuotedEscaping(ctx, member, RDB_bulkLen(p, member)); + fprintf(ctx->outfile, ":\"%.*s\"", len, score_str); + + /* update new state */ + ctx->state = R2J_IN_ZSET; + + } else if (ctx->state == R2J_IN_ZSET) { + /* output json part */ + fprintf(ctx->outfile, ","); + outputQuotedEscaping(ctx, member, RDB_bulkLen(p, member)); + fprintf(ctx->outfile, ":\"%.*s\"", len, score_str); + + /* state unchanged */ + + } else { + RDB_reportError(p, (RdbRes) RDBX_ERR_R2J_INVALID_STATE, + "toJsonZset(): Invalid state value: %d", ctx->state); + return (RdbRes) RDBX_ERR_R2J_INVALID_STATE; + } + + return RDB_OK; +} + static RdbRes toJsonHash(RdbParser *p, void *userData, RdbBulk field, RdbBulk value) { RdbxToJson *ctx = userData; @@ -441,6 +475,7 @@ RdbxToJson *RDBX_createHandlersToJson(RdbParser *p, const char *filename, RdbxTo callbacks.dataCb.handleListItem = toJsonList; callbacks.dataCb.handleHashField = toJsonHash; callbacks.dataCb.handleSetMember = toJsonSet; + callbacks.dataCb.handleZsetMember = toJsonZset; callbacks.dataCb.handleFunction = (conf->includeFunc) ? toJsonFunction : NULL; callbacks.dataCb.handleModule = toJsonModule; RDB_createHandlersData(p, &callbacks.dataCb, ctx, deleteRdbToJsonCtx); @@ -461,6 +496,10 @@ RdbxToJson *RDBX_createHandlersToJson(RdbParser *p, const char *filename, RdbxTo callbacks.structCb.handleSetPlain = toJsonSet; callbacks.structCb.handleSetIS = toJsonStruct; callbacks.structCb.handleSetLP = toJsonStruct; + /* zset */ + callbacks.structCb.handleZsetPlain = toJsonZset; + callbacks.structCb.handleZsetZL = toJsonStruct; + callbacks.structCb.handleZsetLP = toJsonStruct; /* function */ callbacks.structCb.handleFunction = (conf->includeFunc) ? toJsonFunction : NULL; /* module */ diff --git a/src/ext/handlersToResp.c b/src/ext/handlersToResp.c index d6e793a..f21b45d 100644 --- a/src/ext/handlersToResp.c +++ b/src/ext/handlersToResp.c @@ -349,6 +349,35 @@ static RdbRes toRespSet(RdbParser *p, void *userData, RdbBulk member) { return writevWrap(ctx, iov, 7, 1, 1); } +static RdbRes toRespZset(RdbParser *p, void *userData, RdbBulk member, double score) { + RdbxToResp *ctx = userData; + char keyLenStr[32], valLenStr[32], scoreLenStr[32]; + + int valLen = RDB_bulkLen(p, member); + + struct iovec iov[10]; + /* write ZADD */ + IOV_CONST(&iov[0], "*4\r\n$4\r\nZADD\r\n$"); + /* write key */ + IOV_VALUE(&iov[1], ctx->keyCtx.keyLen, keyLenStr); + IOV_STRING(&iov[2], ctx->keyCtx.key, ctx->keyCtx.keyLen); + IOV_CONST(&iov[3], "\r\n$"); + + /* write score */ + char score_str[MAX_D2STRING_CHARS]; + int len = d2string(score_str, sizeof(score_str), score); + assert(len != 0); + IOV_VALUE(&iov[4], len, scoreLenStr); + IOV_STRING(&iov[5], score_str, strlen(score_str)); + IOV_CONST(&iov[6], "\r\n$"); + + /* write member */ + IOV_VALUE(&iov[7], valLen, valLenStr); + IOV_STRING(&iov[8], member, valLen); + IOV_CONST(&iov[9], "\r\n"); + return writevWrap(ctx, iov, 10, 1, 1); +} + static RdbRes toRespEndRdb(RdbParser *p, void *userData) { UNUSED(p); RdbxToResp *ctx = userData; @@ -590,6 +619,7 @@ _LIBRDB_API RdbxToResp *RDBX_createHandlersToResp(RdbParser *p, RdbxToRespConf * dataCb.handleListItem = toRespList; dataCb.handleHashField = toRespHash; dataCb.handleSetMember = toRespSet; + dataCb.handleZsetMember = toRespZset; dataCb.handleEndRdb = toRespEndRdb; dataCb.handleFunction = toRespFunction; RDB_createHandlersData(p, &dataCb, ctx, deleteRdbToRespCtx); diff --git a/src/lib/parser.c b/src/lib/parser.c index 57cead3..b074143 100644 --- a/src/lib/parser.c +++ b/src/lib/parser.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "../../deps/redis/crc64.h" #include "bulkAlloc.h" #include "parser.h" @@ -30,6 +31,7 @@ #include "../../deps/redis/zipmap.h" #include "../../deps/redis/intset.h" #include "../../deps/redis/lzf.h" +#include "../../deps/redis/t_zset.h" #define DONE_FILL_BULK SIZE_MAX @@ -65,6 +67,11 @@ struct ParsingElementInfo peInfo[PE_MAX] = { [PE_SET] = {elementSet, "elementSet", "Parsing set"}, [PE_SET_IS] = {elementSetIS, "elementSetIS", "Parsing set Intset"}, [PE_SET_LP] = {elementSetLP, "elementSetLP", "Parsing set Listpack"}, + /* zset */ + [PE_ZSET] = {elementZset, "elementZset", "Parsing zset"}, + [PE_ZSET_2] = {elementZset, "elementZset", "Parsing zset_2"}, + [PE_ZSET_ZL] = {elementZsetZL, "elementZsetZL", "Parsing zset Ziplist"}, + [PE_ZSET_LP] = {elementZsetLP, "elementZsetLP", "Parsing zset Listpack"}, [PE_FUNCTION] = {elementFunction, "elementFunction", "Parsing Function"}, [PE_MODULE] = {elementModule, "elementModule", "Parsing silently Module element"}, [PE_MODULE_AUX] = {elementModule, "elementModule", "Parsing silently Module Auxiliary data"}, @@ -89,6 +96,11 @@ struct ParsingElementInfo peInfo[PE_MAX] = { [PE_RAW_SET] = {elementRawSet, "elementRawSet", "Parsing raw set"}, [PE_RAW_SET_IS] = {elementRawSetIS, "elementRawSetIS", "Parsing raw set Intset"}, [PE_RAW_SET_LP] = {elementRawSetLP, "elementRawSetLP", "Parsing raw set Listpack"}, + /* zset */ + [PE_RAW_ZSET] = {elementRawZset, "elementRawZset", "Parsing raw zset"}, + [PE_RAW_ZSET_2] = {elementRawZset, "elementRawZset", "Parsing raw zset_2"}, + [PE_RAW_ZSET_ZL] = {elementRawZsetZL, "elementRawZsetZL", "Parsing raw zset Ziplist"}, + [PE_RAW_ZSET_LP] = {elementRawZsetLP, "elementRawZsetLP", "Parsing raw zset Listpack"}, /* module */ [PE_RAW_MODULE] = {elementRawModule, "elementRawModule", "Parsing raw Module element"}, [PE_RAW_MODULE_AUX] = {elementRawModule, "elementRawModule(aux)", "Parsing Module Auxiliary data"}, @@ -1086,6 +1098,52 @@ void moduleTypeNameByID(char *name, uint64_t moduleid) { } } +/* return either RDB_STATUS_OK or RDB_STATUS_ERROR */ +static RdbStatus zsetZiplistItem(RdbParser *p, BulkInfo *ziplistBulk) { + + int ret = ziplistValidateIntegrity(ziplistBulk->ref, ziplistBulk->len, p->deepIntegCheck, NULL, NULL); + + if (unlikely(!ret)) { + RDB_reportError(p, RDB_ERR_ZSET_ZL_INTEG_CHECK, "zsetZiplistItem(): Ziplist integrity check failed"); + return RDB_STATUS_ERROR; + } + + if (p->elmCtx.key.handleByLevel == RDB_LEVEL_STRUCT) { + registerAppBulkForNextCb(p, ziplistBulk); + CALL_HANDLERS_CB(p, NOP, RDB_LEVEL_STRUCT, rdbStruct.handleZsetZL, ziplistBulk->ref); + return RDB_STATUS_OK; + } + + unsigned char *offsetZL = ziplistIndex(ziplistBulk->ref, 0); + while (offsetZL != NULL) { + unsigned char *item1, *item2; + unsigned int item1Len, item2Len; + long long item1Val, item2Val; + double score; + EmbeddedBulk embBulk; + + ziplistGet(offsetZL, &item1, &item1Len, &item1Val); + offsetZL = ziplistNext(ziplistBulk->ref, offsetZL); + + ziplistGet(offsetZL, &item2, &item2Len, &item2Val); + offsetZL = ziplistNext(ziplistBulk->ref, offsetZL); + + score = item2 ? zzlStrtod(item2, item2Len) : (double) item2Val; + + if (!allocEmbeddedBulk(p, item1, item1Len, item1Val, &embBulk)) + return RDB_STATUS_ERROR; + + registerAppBulkForNextCb(p, embBulk.binfo); + CALL_HANDLERS_CB(p, + restoreEmbeddedBulk(&embBulk);, /*finalize*/ + RDB_LEVEL_DATA, + rdbData.handleZsetMember, + embBulk.binfo->ref, + score); + } + return RDB_STATUS_OK; +} + /*** Parsing Common Elements ***/ RdbStatus elementRdbHeader(RdbParser *p) { @@ -1267,10 +1325,10 @@ RdbStatus elementNextRdbType(RdbParser *p) { case RDB_OPCODE_EOF: return nextParsingElement(p, PE_END_OF_FILE); /* zset (TBD) */ - case RDB_TYPE_ZSET: - case RDB_TYPE_ZSET_2: - case RDB_TYPE_ZSET_ZIPLIST: - case RDB_TYPE_ZSET_LISTPACK: + case RDB_TYPE_ZSET: return nextParsingElementKeyValue(p, PE_RAW_ZSET, PE_ZSET); + case RDB_TYPE_ZSET_2: return nextParsingElementKeyValue(p, PE_RAW_ZSET, PE_ZSET); + case RDB_TYPE_ZSET_ZIPLIST: return nextParsingElementKeyValue(p, PE_RAW_ZSET_ZL, PE_ZSET_ZL); + case RDB_TYPE_ZSET_LISTPACK: return nextParsingElementKeyValue(p, PE_RAW_ZSET_LP, PE_ZSET_LP); /* stream (TBD) */ case RDB_TYPE_STREAM_LISTPACKS: @@ -1664,6 +1722,115 @@ RdbStatus elementSetLP(RdbParser *p) { return nextParsingElement(p, PE_END_KEY); } +RdbStatus elementZset(RdbParser *p) { + ElementCtx *ctx = &p->elmCtx; + enum ZSET_STATES { + ST_ZSET_HEADER=0, /* Retrieve number of nodes */ + ST_ZSET_NEXT_ITEM /* Process next node and callback to app (Iterative) */ + }; + switch (ctx->state) { + case ST_ZSET_HEADER: + IF_NOT_OK_RETURN(rdbLoadLen(p, NULL, (uint64_t *) &(ctx->key.numItemsHint), NULL, NULL)); + + /*** ENTER SAFE STATE ***/ + + ctx->zset.left = ctx->key.numItemsHint; + + updateElementState(p, ST_ZSET_NEXT_ITEM); /* fall-thru */ + + case ST_ZSET_NEXT_ITEM: { + double score; + BulkInfo *binfoItem; + + + IF_NOT_OK_RETURN(rdbLoadString(p, RQ_ALLOC_APP_BULK, NULL, &binfoItem)); + + if (p->currOpcode == RDB_TYPE_ZSET_2) { + IF_NOT_OK_RETURN(rdbLoadBinaryDoubleValue(p, &score)); + } else { + IF_NOT_OK_RETURN(rdbLoadDoubleValue(p, &score)); + } + + /*** ENTER SAFE STATE ***/ + + registerAppBulkForNextCb(p, binfoItem); + if (p->elmCtx.key.handleByLevel == RDB_LEVEL_STRUCT) + CALL_HANDLERS_CB(p, NOP, RDB_LEVEL_STRUCT, rdbStruct.handleZsetPlain, binfoItem->ref, score); + else + CALL_HANDLERS_CB(p, NOP, RDB_LEVEL_DATA, rdbData.handleZsetMember, binfoItem->ref, score); + + return (--ctx->zset.left) ? updateElementState(p, ST_ZSET_NEXT_ITEM) : nextParsingElement(p, PE_END_KEY); + } + default: + RDB_reportError(p, RDB_ERR_PLAIN_ZSET_INVALID_STATE, + "elementZset(): invalid parsing element state: %d", ctx->state); + return RDB_STATUS_ERROR; + } +} + +RdbStatus elementZsetZL(RdbParser *p) { + BulkInfo *ziplistBulk; + + IF_NOT_OK_RETURN(rdbLoadString(p, RQ_ALLOC_APP_BULK, NULL, &ziplistBulk)); + + /*** ENTER SAFE STATE ***/ + + if (RDB_STATUS_ERROR == zsetZiplistItem(p, ziplistBulk)) + return RDB_STATUS_ERROR; + + return nextParsingElement(p, PE_END_KEY); +} + +RdbStatus elementZsetLP(RdbParser *p) { + unsigned char *iterator, *item1, *item2; + unsigned int item1Len, item2Len; + long long item1Val, item2Val; + BulkInfo *listpackBulk; + + IF_NOT_OK_RETURN(rdbLoadString(p, RQ_ALLOC_APP_BULK, NULL, &listpackBulk)); + + /*** ENTER SAFE STATE ***/ + + /* Doesn't check for duplication */ + if (!lpValidateIntegrity(listpackBulk->ref, listpackBulk->len, p->deepIntegCheck, NULL, 0)) { + RDB_reportError(p, RDB_ERR_ZSET_LP_INTEG_CHECK, "elementZsetLP(): LISTPACK integrity check failed"); + return RDB_STATUS_ERROR; + } + + p->elmCtx.key.numItemsHint = lpLength(listpackBulk->ref); + + if (p->elmCtx.key.handleByLevel == RDB_LEVEL_STRUCT) { + registerAppBulkForNextCb(p, listpackBulk); + CALL_HANDLERS_CB(p, NOP, RDB_LEVEL_STRUCT, rdbStruct.handleZsetLP, listpackBulk->ref); + } else { + iterator = lpFirst(listpackBulk->ref); + while (iterator) { + double score; + EmbeddedBulk embBulk; + + item1 = lpGetValue(iterator, &item1Len, &item1Val); + iterator = lpNext(listpackBulk->ref, iterator); + item2 = lpGetValue(iterator, &item2Len, &item2Val); + + score = item2 ? zzlStrtod(item2, item2Len) : (double) item2Val; + + if (!allocEmbeddedBulk(p, item1, item1Len, item1Val, &embBulk)) + return RDB_STATUS_ERROR; + + registerAppBulkForNextCb(p, embBulk.binfo); + CALL_HANDLERS_CB(p, + restoreEmbeddedBulk(&embBulk);, /*finalize*/ + RDB_LEVEL_DATA, + rdbData.handleZsetMember, + embBulk.binfo->ref, + score); + + iterator = lpNext(listpackBulk->ref, iterator); + } + } + return nextParsingElement(p, PE_END_KEY); +} + RdbStatus elementEndOfFile(RdbParser *p) { /* Verify the checksum if RDB version is >= 5 */ if (p->rdbversion >= 5) { @@ -1767,7 +1934,7 @@ RdbStatus elementModule(RdbParser *p) { } case ST_MODULE_OPCODE_DOUBLE: { double val; /*UNUSED*/ - IF_NOT_OK_RETURN(rdbLoadDoubleValue(p, &val)); + IF_NOT_OK_RETURN(rdbLoadBinaryDoubleValue(p, &val)); /*** ENTER SAFE STATE ***/ updateElementState(p, ST_MODULE_NEXT_OPCODE); break; @@ -1824,19 +1991,78 @@ RdbStatus elementModule(RdbParser *p) { /*** Loaders from RDB ***/ RdbStatus rdbLoadFloatValue(RdbParser *p, float *val) { - BulkInfo *binfoUnused; - IF_NOT_OK_RETURN(rdbLoad(p, sizeof(*val), RQ_ALLOC_REF, (char *) val, &binfoUnused)); + BulkInfo *binfo; + IF_NOT_OK_RETURN(rdbLoad(p, sizeof(*val), RQ_ALLOC, NULL, &binfo)); + *val = *((float*) binfo->ref); memrev32ifbe(val); return RDB_STATUS_OK; } -RdbStatus rdbLoadDoubleValue(RdbParser *p, double *val) { - BulkInfo *binfoUnused; - IF_NOT_OK_RETURN(rdbLoad(p, sizeof(*val), RQ_ALLOC_REF, (char *) val, &binfoUnused)); +RdbStatus rdbLoadBinaryDoubleValue(RdbParser *p, double *val) { + BulkInfo *binfo; + IF_NOT_OK_RETURN(rdbLoad(p, sizeof(*val), RQ_ALLOC, NULL, &binfo)); + *val = *((double*) binfo->ref); memrev64ifbe(val); return RDB_STATUS_OK; } +/* + * For RDB_TYPE_ZSET, doubles are saved as strings prefixed by an unsigned + * 8 bit integer specifying the length of the representation. + * This 8 bit integer has special values in order to specify the following + * conditions: + * 253: not a number + * 254: + inf + * 255: - inf + */ +RdbStatus rdbLoadDoubleValue(RdbParser *p, double *val) { + unsigned char len; + BulkInfo *binfo; + + IF_NOT_OK_RETURN(rdbLoad(p, 1, RQ_ALLOC, NULL, &binfo)); + len = *((unsigned char*)binfo->ref); + + switch (len) { + case 255: *val = -INFINITY; return RDB_STATUS_OK; + case 254: *val = INFINITY; return RDB_STATUS_OK; + case 253: *val = NAN; return RDB_STATUS_OK; + default: + IF_NOT_OK_RETURN(rdbLoad(p, len, RQ_ALLOC, NULL, &binfo)); + if (sscanf(binfo->ref, "%lg", val) != 1) + return RDB_STATUS_ERROR; + + return RDB_STATUS_OK; + } +} + +/* Try to read double value and then copy it to the destination including one + * byte prefix. See rdbLoadDoubleValue() for details. */ +RdbStatus rdbLoadDoubleValueToBuff(RdbParser *p, char *buff, int *written) { + double val; + unsigned char len; + BulkInfo *binfo; + + IF_NOT_OK_RETURN(rdbLoad(p, 1, RQ_ALLOC, NULL, &binfo)); + len = *((unsigned char*)binfo->ref); + + *buff++ = len; + *written = 1; + + switch (len) { + case 255: /* -INFINITY */ + case 254: /* INFINITY */ + case 253: /* NAN */ + return RDB_STATUS_OK; + default: + IF_NOT_OK_RETURN(rdbLoad(p, len, RQ_ALLOC_REF, buff, &binfo)); + if (sscanf(buff, "%lg", &val) != 1) + return RDB_STATUS_ERROR; + + *written += len; + return RDB_STATUS_OK; + } +} + RdbStatus rdbLoadInteger(RdbParser *p, int enctype, AllocTypeRq type, char *refBuf, BulkInfo **binfo) { long long val; diff --git a/src/lib/parser.h b/src/lib/parser.h index c8191b1..b4b9617 100644 --- a/src/lib/parser.h +++ b/src/lib/parser.h @@ -138,6 +138,10 @@ typedef enum ParsingElementType { PE_SET, PE_SET_IS, PE_SET_LP, + PE_ZSET, + PE_ZSET_2, + PE_ZSET_ZL, + PE_ZSET_LP, PE_MODULE, PE_FUNCTION, PE_MODULE_AUX, @@ -156,6 +160,10 @@ typedef enum ParsingElementType { PE_RAW_SET, PE_RAW_SET_IS, PE_RAW_SET_LP, + PE_RAW_ZSET, + PE_RAW_ZSET_2, + PE_RAW_ZSET_ZL, + PE_RAW_ZSET_LP, PE_RAW_MODULE, PE_RAW_MODULE_AUX, @@ -182,6 +190,10 @@ typedef struct { uint64_t left; } ElementSetCtx; +typedef struct { + uint64_t left; +} ElementZsetCtx; + typedef struct { uint64_t numFields; uint64_t visitingField; @@ -214,6 +226,10 @@ typedef struct { uint64_t numItems; } ElementRawSetCtx; +typedef struct { + uint64_t numItems; +} ElementRawZsetCtx; + typedef struct { uint64_t numFields; uint64_t visitField; @@ -229,6 +245,7 @@ typedef struct ElementCtx { ElementKeyCtx key; ElementListCtx list; ElementSetCtx set; + ElementZsetCtx zset; ElementHashCtx hash; ElementModuleCtx module; @@ -236,6 +253,7 @@ typedef struct ElementCtx { ElementRawStringCtx rawString; ElementRawListCtx rawList; ElementRawSetCtx rawSet; + ElementRawZsetCtx rawZset; ElementRawHashCtx rawHash; ElementRawModuleAux rawModAux; @@ -425,7 +443,9 @@ void subElementCallEnd(RdbParser *p, RdbBulk *bulkResult, size_t *len); /*** Loaders from RDB ***/ RdbStatus rdbLoadFloatValue(RdbParser *p, float *val); +RdbStatus rdbLoadBinaryDoubleValue(RdbParser *p, double *val); RdbStatus rdbLoadDoubleValue(RdbParser *p, double *val); +RdbStatus rdbLoadDoubleValueToBuff(RdbParser *p, char *buff, int *written); RdbStatus rdbLoadLen(RdbParser *p, int *isencoded, uint64_t *lenptr, unsigned char* outbuff, int *outbufflen); RdbStatus rdbLoadInteger(RdbParser *p, int enctype, AllocTypeRq type, char *refBuf, BulkInfo **out); RdbStatus rdbLoadString(RdbParser *p, AllocTypeRq type, char *refBuf, BulkInfo **out); @@ -473,6 +493,9 @@ RdbStatus elementHashZM(RdbParser *p); RdbStatus elementSet(RdbParser *p); RdbStatus elementSetIS(RdbParser *p); RdbStatus elementSetLP(RdbParser *p); +RdbStatus elementZset(RdbParser *p); +RdbStatus elementZsetZL(RdbParser *p); +RdbStatus elementZsetLP(RdbParser *p); RdbStatus elementFunction(RdbParser *p); RdbStatus elementModule(RdbParser *p); @@ -490,6 +513,9 @@ RdbStatus elementRawHashZM(RdbParser *p); RdbStatus elementRawSet(RdbParser *p); RdbStatus elementRawSetIS(RdbParser *p); RdbStatus elementRawSetLP(RdbParser *p); +RdbStatus elementRawZset(RdbParser *p); +RdbStatus elementRawZsetLP(RdbParser *p); +RdbStatus elementRawZsetZL(RdbParser *p); RdbStatus elementRawModule(RdbParser *p); #endif /*LIBRDB_PARSER_H*/ diff --git a/src/lib/parserRaw.c b/src/lib/parserRaw.c index 4dbd8d8..f980048 100644 --- a/src/lib/parserRaw.c +++ b/src/lib/parserRaw.c @@ -572,6 +572,78 @@ RdbStatus elementRawSet(RdbParser *p) { } } +RdbStatus elementRawZsetLP(RdbParser *p) { + return singleStringTypeHandling(p, listpackValidateIntegrityCb, "elementRawZsetLP"); +} + +RdbStatus elementRawZsetZL(RdbParser *p) { + return singleStringTypeHandling(p, ziplistValidateIntegrityCb, "elementRawZsetZL"); +} + +RdbStatus elementRawZset(RdbParser *p) { + enum RAW_ZSET_STATES { + ST_RAW_ZSET_HEADER=0, /* Retrieve number of members */ + ST_RAW_ZSET_READ_MEMBER, /* Read next member string */ + ST_RAW_ZSET_READ_SCORE, /* Read score of the member */ + }; + + ElementRawZsetCtx *zsetCtx = &p->elmCtx.rawZset; + RawContext *rawCtx = &p->rawCtx; + + switch (p->elmCtx.state) { + + case ST_RAW_ZSET_HEADER: { + int headerLen = 0; + + aggMakeRoom(p, 10); /* worse case 9 bytes for len */ + + IF_NOT_OK_RETURN(rdbLoadLen(p, NULL, &zsetCtx->numItems, + (unsigned char *) rawCtx->at, &headerLen)); + + /*** ENTER SAFE STATE ***/ + + IF_NOT_OK_RETURN(cbHandleBegin(p, DATA_SIZE_UNKNOWN_AHEAD)); + IF_NOT_OK_RETURN(aggUpdateWritten(p, headerLen)); + } + updateElementState(p, ST_RAW_ZSET_READ_MEMBER); /* fall-thru */ + + case ST_RAW_ZSET_READ_MEMBER: + return subElementCall(p, PE_RAW_STRING, ST_RAW_ZSET_READ_SCORE); + + case ST_RAW_ZSET_READ_SCORE: { + size_t len; + unsigned char *encodedItem; + + /* return from sub-element string parsing */ + subElementCallEnd(p, (char **) &encodedItem, &len); + + /* For RDB_TYPE_ZSET, worst case < 255 */ + IF_NOT_OK_RETURN(aggMakeRoom(p, 255)); + + if (p->currOpcode == RDB_TYPE_ZSET_2) { + IF_NOT_OK_RETURN(rdbLoadBinaryDoubleValue(p, (double *) rawCtx->at)); + IF_NOT_OK_RETURN(aggUpdateWritten(p, sizeof(double))); + } else { + int written; + IF_NOT_OK_RETURN(rdbLoadDoubleValueToBuff(p, rawCtx->at, &written)); + IF_NOT_OK_RETURN(aggUpdateWritten(p, written)); + } + + /*** ENTER SAFE STATE ***/ + + + if (--zsetCtx->numItems == 0) + return nextParsingElement(p, PE_RAW_END_KEY); /* done */ + + return updateElementState(p, ST_RAW_ZSET_READ_MEMBER); + } + default: + RDB_reportError(p, RDB_ERR_PLAIN_ZSET_INVALID_STATE, + "elementRawZset(): invalid parsing element state: %d", p->elmCtx.state); + return RDB_STATUS_ERROR; + } +} + RdbStatus elementRawModule(RdbParser *p) { typedef enum RAW_MODULE_STATES { @@ -656,7 +728,7 @@ RdbStatus elementRawModule(RdbParser *p) { case ST_RAW_MODULE_OPCODE_DOUBLE: { IF_NOT_OK_RETURN(aggMakeRoom(p, sizeof(double))); - IF_NOT_OK_RETURN(rdbLoadDoubleValue(p, (double *) rawCtx->at)); + IF_NOT_OK_RETURN(rdbLoadBinaryDoubleValue(p, (double *) rawCtx->at)); /*** ENTER SAFE STATE ***/ IF_NOT_OK_RETURN(aggUpdateWritten(p, sizeof(double))); updateElementState(p, ST_RAW_MODULE_NEXT_OPCODE); diff --git a/test/dumps/plain_zset_2_v11.rdb b/test/dumps/plain_zset_2_v11.rdb new file mode 100644 index 0000000..ee7f8f2 Binary files /dev/null and b/test/dumps/plain_zset_2_v11.rdb differ diff --git a/test/dumps/plain_zset_2_v11_data.json b/test/dumps/plain_zset_2_v11_data.json new file mode 100644 index 0000000..514363d --- /dev/null +++ b/test/dumps/plain_zset_2_v11_data.json @@ -0,0 +1,6 @@ +"redis-ver":"255.255.255", +"redis-bits":"64", +"ctime":"1695705520", +"used-mem":"967352", +"aof-base":"0", +"myzset":{"a9":"inf","a8":"inf","a23":"1.000033e+25","a11":"9007199254740992","a21":"1125899906842624","a17":"65536","a15":"255","a13":"8.888888","a7":"2.2","a6":"0","a5":"0","a4":"-0","a3":"-0","a2":"0","a1":"0","a19":"-1","a20":"-1.1","a14":"-9.99999","a16":"-255","a18":"-65536","a22":"-1125899906842624","a12":"-9007199254740992","a24":"-4.329000123123131e+28","a10":"-inf"} diff --git a/test/dumps/plain_zset_2_v11_raw.json b/test/dumps/plain_zset_2_v11_raw.json new file mode 100644 index 0000000..a00f2bd --- /dev/null +++ b/test/dumps/plain_zset_2_v11_raw.json @@ -0,0 +1,6 @@ +"redis-ver":"255.255.255", +"redis-bits":"64", +"ctime":"1695705520", +"used-mem":"967352", +"aof-base":"0", +"myzset":"\x05\x18\x02a9\x00\x00\x00\x00\x00\x00\xf0\x7f\x02a8\x00\x00\x00\x00\x00\x00\xf0\x7f\x03a23\xbc\t\x7f\xf3M\x8b E\x03a11\x00\x00\x00\x00\x00\x00@C\x03a21\x00\x00\x00\x00\x00\x00\x10C\x03a17\x00\x00\x00\x00\x00\x00\xf0@\x03a15\x00\x00\x00\x00\x00\xe0o@\x03a13\x1b\x9d\xf3S\x1c\xc7!@\x02a7\x9a\x99\x99\x99\x99\x99\x01@\x02a6\x00\x00\x00\x00\x00\x00\x00\x00\x02a5\x00\x00\x00\x00\x00\x00\x00\x00\x02a4\x00\x00\x00\x00\x00\x00\x00\x80\x02a3\x00\x00\x00\x00\x00\x00\x00\x80\x02a2\x00\x00\x00\x00\x00\x00\x00\x00\x02a1\x00\x00\x00\x00\x00\x00\x00\x00\x03a19\x00\x00\x00\x00\x00\x00\xf0\xbf\x03a20\x9a\x99\x99\x99\x99\x99\xf1\xbf\x03a14r\xa7t\xb0\xfe\xff#\xc0\x03a16\x00\x00\x00\x00\x00\xe0o\xc0\x03a18\x00\x00\x00\x00\x00\x00\xf0\xc0\x03a22\x00\x00\x00\x00\x00\x00\x10\xc3\x03a12\x00\x00\x00\x00\x00\x00@\xc3\x03a24\xc0\xe1\xa2\xca\x14|\xe1\xc5\x03a10\x00\x00\x00\x00\x00\x00\xf0\xff" diff --git a/test/dumps/plain_zset_2_v11_struct.json b/test/dumps/plain_zset_2_v11_struct.json new file mode 100644 index 0000000..514363d --- /dev/null +++ b/test/dumps/plain_zset_2_v11_struct.json @@ -0,0 +1,6 @@ +"redis-ver":"255.255.255", +"redis-bits":"64", +"ctime":"1695705520", +"used-mem":"967352", +"aof-base":"0", +"myzset":{"a9":"inf","a8":"inf","a23":"1.000033e+25","a11":"9007199254740992","a21":"1125899906842624","a17":"65536","a15":"255","a13":"8.888888","a7":"2.2","a6":"0","a5":"0","a4":"-0","a3":"-0","a2":"0","a1":"0","a19":"-1","a20":"-1.1","a14":"-9.99999","a16":"-255","a18":"-65536","a22":"-1125899906842624","a12":"-9007199254740992","a24":"-4.329000123123131e+28","a10":"-inf"} diff --git a/test/dumps/plain_zset_v6.rdb b/test/dumps/plain_zset_v6.rdb new file mode 100644 index 0000000..a6da72a Binary files /dev/null and b/test/dumps/plain_zset_v6.rdb differ diff --git a/test/dumps/plain_zset_v6_data.json b/test/dumps/plain_zset_v6_data.json new file mode 100644 index 0000000..421336e --- /dev/null +++ b/test/dumps/plain_zset_v6_data.json @@ -0,0 +1 @@ +"myzset":{"a23":"1.000033e+25","a19":"-1","a9":"inf","a24":"-4.329000123123131e+28","a12":"-9007199254740992","a7":"2.2","a13":"8.888888","a17":"65536","a21":"1125899906842624","a20":"-1.1","a22":"-1125899906842624","a3":"-0","a2":"0","a15":"255","a8":"inf","a11":"9007199254740992","a10":"-inf","a5":"0","a14":"-9.99999","a6":"0","a16":"-255","a4":"-0","a1":"0","a18":"-65536"} diff --git a/test/dumps/plain_zset_v6_raw.json b/test/dumps/plain_zset_v6_raw.json new file mode 100644 index 0000000..f026b50 --- /dev/null +++ b/test/dumps/plain_zset_v6_raw.json @@ -0,0 +1 @@ +"myzset":"\x03\x18\x03a23\f1.000033e+25\x03a19\x02-1\x02a9\xfe\x03a24\x17-4.3290001231231309e+28\x03a12\x11-9007199254740992\x02a7\x122.2000000000000002\x03a13\x128.8888879999999997\x03a17\x0565536\x03a21\x101125899906842624\x03a20\x13-1.1000000000000001\x03a22\x11-1125899906842624\x02a3\x02-0\x02a2\x010\x03a15\x03255\x02a8\xfe\x03a11\x109007199254740992\x03a10\xff\x02a5\x010\x03a14\x13-9.9999900000000004\x02a6\x010\x03a16\x04-255\x02a4\x02-0\x02a1\x010\x03a18\x06-65536" diff --git a/test/dumps/plain_zset_v6_struct.json b/test/dumps/plain_zset_v6_struct.json new file mode 100644 index 0000000..421336e --- /dev/null +++ b/test/dumps/plain_zset_v6_struct.json @@ -0,0 +1 @@ +"myzset":{"a23":"1.000033e+25","a19":"-1","a9":"inf","a24":"-4.329000123123131e+28","a12":"-9007199254740992","a7":"2.2","a13":"8.888888","a17":"65536","a21":"1125899906842624","a20":"-1.1","a22":"-1125899906842624","a3":"-0","a2":"0","a15":"255","a8":"inf","a11":"9007199254740992","a10":"-inf","a5":"0","a14":"-9.99999","a6":"0","a16":"-255","a4":"-0","a1":"0","a18":"-65536"} diff --git a/test/dumps/zset_lp_v11.rdb b/test/dumps/zset_lp_v11.rdb new file mode 100644 index 0000000..8f016e5 Binary files /dev/null and b/test/dumps/zset_lp_v11.rdb differ diff --git a/test/dumps/zset_lp_v11_data.json b/test/dumps/zset_lp_v11_data.json new file mode 100644 index 0000000..3b246a3 --- /dev/null +++ b/test/dumps/zset_lp_v11_data.json @@ -0,0 +1,6 @@ +"redis-ver":"255.255.255", +"redis-bits":"64", +"ctime":"1695704816", +"used-mem":"969032", +"aof-base":"0", +"myzset":{"a10":"-inf","a24":"-4.329000123123131e+28","a12":"-9007199254740992","a22":"-1125899906842624","a18":"-65536","a16":"-255","a14":"-9.99999","a20":"-1.1","a19":"-1","a1":"0","a2":"0","a3":"0","a4":"0","a5":"0","a6":"0","a7":"2.2","a13":"8.888888","a15":"255","a17":"65536","a21":"1125899906842624","a11":"9007199254740992","a23":"1.000033e+25","a8":"inf","a9":"inf"} diff --git a/test/dumps/zset_lp_v11_raw.json b/test/dumps/zset_lp_v11_raw.json new file mode 100644 index 0000000..90e13c1 --- /dev/null +++ b/test/dumps/zset_lp_v11_raw.json @@ -0,0 +1,6 @@ +"redis-ver":"255.255.255", +"redis-bits":"64", +"ctime":"1695704816", +"used-mem":"969032", +"aof-base":"0", +"myzset":"\x11\xc3@\xe6A\x12\x1f\x12\x01\x00\x000\x00\x83a10\x04\x84-inf\x05\x83a24\x04\x96-4.329000\x02123@\x02\x0631e+28\x17 '\x032\x04\xf4\x00`\x00\x02\xe0\xff\t +\xe0\x00\x0e\x00\xfc@\x0e\x0718\x04\xf2\x00\x00\xff\x04 \t\x046\x04\xdf\x01\x02 \x07\x064\x04\x88-9.9@\x00@/@f\x021.1 f\x0e19\x04\xdf\xff\x02\x82a1\x03\x00\x01\x82a2`\x05\x003`\x05\x004`\x05\x005`\x05\x006`\x05\x057\x03\x832.2@V\x053\x04\x888.8`\x00 N\x0415\x04\xc0\xff@e\x007@w\x00\x01 \x0121\xc0\x9f\x01\x04\x00@ \xe0\x00\x0e\x00 @\x0e\x0523\x04\x8c1. \xd6\x02033 \xd0\x065\r\x82a8\x03\x83 \xf6\x03\x04\x82a9`\b\x01\x04\xff" diff --git a/test/dumps/zset_lp_v11_struct.json b/test/dumps/zset_lp_v11_struct.json new file mode 100644 index 0000000..c05c7a9 --- /dev/null +++ b/test/dumps/zset_lp_v11_struct.json @@ -0,0 +1,6 @@ +"redis-ver":"255.255.255", +"redis-bits":"64", +"ctime":"1695704816", +"used-mem":"969032", +"aof-base":"0", +"myzset":["\x12\x01\x00\x000\x00\x83a10\x04\x84-inf\x05\x83a24\x04\x96-4.329000123123131e+28\x17\x83a12\x04\xf4\x00\x00\x00\x00\x00\x00\xe0\xff\t\x83a22\x04\xf4\x00\x00\x00\x00\x00\x00\xfc\xff\t\x83a18\x04\xf2\x00\x00\xff\x04\x83a16\x04\xdf\x01\x02\x83a14\x04\x88-9.99999\t\x83a20\x04\x84-1.1\x05\x83a19\x04\xdf\xff\x02\x82a1\x03\x00\x01\x82a2\x03\x00\x01\x82a3\x03\x00\x01\x82a4\x03\x00\x01\x82a5\x03\x00\x01\x82a6\x03\x00\x01\x82a7\x03\x832.2\x04\x83a13\x04\x888.888888\t\x83a15\x04\xc0\xff\x02\x83a17\x04\xf2\x00\x00\x01\x04\x83a21\x04\xf4\x00\x00\x00\x00\x00\x00\x04\x00\t\x83a11\x04\xf4\x00\x00\x00\x00\x00\x00 \x00\t\x83a23\x04\x8c1.000033e+25\r\x82a8\x03\x83inf\x04\x82a9\x03\x83inf\x04\xff"] diff --git a/test/dumps/zset_zl_v6.rdb b/test/dumps/zset_zl_v6.rdb new file mode 100644 index 0000000..6e62303 Binary files /dev/null and b/test/dumps/zset_zl_v6.rdb differ diff --git a/test/dumps/zset_zl_v6_data.json b/test/dumps/zset_zl_v6_data.json new file mode 100644 index 0000000..6e97b2e --- /dev/null +++ b/test/dumps/zset_zl_v6_data.json @@ -0,0 +1 @@ +"myzset":{"a10":"-inf","a24":"-4.329000123123131e+28","a12":"-9007199254740992","a22":"-1125899906842624","a18":"-65536","a16":"-255","a14":"-9.99999","a20":"-1.1","a19":"-1","a1":"0","a2":"0","a3":"-0","a4":"-0","a5":"0","a6":"0","a7":"2.2","a13":"8.888888","a15":"255","a17":"65536","a21":"1125899906842624","a11":"9007199254740992","a23":"1.000033e+25","a8":"inf","a9":"inf"} diff --git a/test/dumps/zset_zl_v6_raw.json b/test/dumps/zset_zl_v6_raw.json new file mode 100644 index 0000000..27d70ad --- /dev/null +++ b/test/dumps/zset_zl_v6_raw.json @@ -0,0 +1 @@ +"myzset":"\f\xc3A\x0fAP\x04P\x01\x00\x00J \x03\x1f0\x00\x00\x03a10\x05\x04-inf\x06\x03a24\x05\x17-4.329000123@\x02\x07309e+28\x19 (\x032\x05\xe0\x00`\x00\x02\xe0\xff\n ,\xe0\x00\x0e\x00\xfc@\x0e\x0718\x05\xf0\x00\x00\xff\x05 \t\x056\x05\xc0\x01\xff\x04 \b\x064\x05\x13-9.9@\x00 P\xa0\x00\x014\x15 ;\x000 \x19\x021.1\xa0\x12\xa0\x00\x001 \x19\x0e19\x05\xfe\xff\x03\x02a1\x04\xf1\x02\x02a2`\x05\b3\x04\x02-0\x04\x02a4\xa0\x07\x005`\x15\x006`\x05\x057\x04\x122.2\xa0@\xa0\x00\x012\x14 G\x053\x05\x128.8@\x00\x007@\x7f`\x00\x007@\x18\x045\x05\xc0\xff\x00@\x9d\x007@\xb0\x00\x01 \xb0\x0121\xc0\xd8\x01\x04\x00 \xd8\x001\xe0\x00\x0e\x00 @\x0e\x0523\x05\f1.@Z\x0133!\t\x065\x0e\x02a8\x04\x03!0\x03\x05\x02a9@\b\x01f\xff" diff --git a/test/dumps/zset_zl_v6_struct.json b/test/dumps/zset_zl_v6_struct.json new file mode 100644 index 0000000..8dd8a52 --- /dev/null +++ b/test/dumps/zset_zl_v6_struct.json @@ -0,0 +1 @@ +"myzset":["P\x01\x00\x00J\x01\x00\x000\x00\x00\x03a10\x05\x04-inf\x06\x03a24\x05\x17-4.3290001231231309e+28\x19\x03a12\x05\xe0\x00\x00\x00\x00\x00\x00\xe0\xff\n\x03a22\x05\xe0\x00\x00\x00\x00\x00\x00\xfc\xff\n\x03a18\x05\xf0\x00\x00\xff\x05\x03a16\x05\xc0\x01\xff\x04\x03a14\x05\x13-9.9999900000000004\x15\x03a20\x05\x13-1.1000000000000001\x15\x03a19\x05\xfe\xff\x03\x02a1\x04\xf1\x02\x02a2\x04\xf1\x02\x02a3\x04\x02-0\x04\x02a4\x04\x02-0\x04\x02a5\x04\xf1\x02\x02a6\x04\xf1\x02\x02a7\x04\x122.2000000000000002\x14\x03a13\x05\x128.8888879999999997\x14\x03a15\x05\xc0\xff\x00\x04\x03a17\x05\xf0\x00\x00\x01\x05\x03a21\x05\xe0\x00\x00\x00\x00\x00\x00\x04\x00\n\x03a11\x05\xe0\x00\x00\x00\x00\x00\x00 \x00\n\x03a23\x05\f1.000033e+25\x0e\x02a8\x04\x03inf\x05\x02a9\x04\x03inf\xff"] \ No newline at end of file diff --git a/test/test_rdb_to_json.c b/test/test_rdb_to_json.c index 2dc4e65..9e82f1a 100644 --- a/test/test_rdb_to_json.c +++ b/test/test_rdb_to_json.c @@ -281,6 +281,78 @@ static void test_r2j_set_lp_raw(void **state) { testRdbToJsonCommon(DUMP_FOLDER("set_lp_v11.rdb"), DUMP_FOLDER("set_lp_v11_raw.json"), &r2jConf); } +static void test_r2j_plain_zset_data(void **state) { + UNUSED(state); + RdbxToJsonConf r2jConf = DEF_CONF(RDB_LEVEL_DATA); + testRdbToJsonCommon(DUMP_FOLDER("plain_zset_v6.rdb"), DUMP_FOLDER("plain_zset_v6_data.json"), &r2jConf); +} + +static void test_r2j_plain_zset_struct(void **state) { + UNUSED(state); + RdbxToJsonConf r2jConf = DEF_CONF(RDB_LEVEL_STRUCT); + testRdbToJsonCommon(DUMP_FOLDER("plain_zset_v6.rdb"), DUMP_FOLDER("plain_zset_v6_struct.json"), &r2jConf); +} + +static void test_r2j_plain_zset_raw(void **state) { + UNUSED(state); + RdbxToJsonConf r2jConf = DEF_CONF(RDB_LEVEL_RAW); + testRdbToJsonCommon(DUMP_FOLDER("plain_zset_v6.rdb"), DUMP_FOLDER("plain_zset_v6_raw.json"), &r2jConf); +} + +static void test_r2j_plain_zset_2_data(void **state) { + UNUSED(state); + RdbxToJsonConf r2jConf = DEF_CONF(RDB_LEVEL_DATA); + testRdbToJsonCommon(DUMP_FOLDER("plain_zset_2_v11.rdb"), DUMP_FOLDER("plain_zset_2_v11_data.json"), &r2jConf); +} + +static void test_r2j_plain_zset_2_struct(void **state) { + UNUSED(state); + RdbxToJsonConf r2jConf = DEF_CONF(RDB_LEVEL_STRUCT); + testRdbToJsonCommon(DUMP_FOLDER("plain_zset_2_v11.rdb"), DUMP_FOLDER("plain_zset_2_v11_struct.json"), &r2jConf); +} + +static void test_r2j_plain_zset_2_raw(void **state) { + UNUSED(state); + RdbxToJsonConf r2jConf = DEF_CONF(RDB_LEVEL_RAW); + testRdbToJsonCommon(DUMP_FOLDER("plain_zset_2_v11.rdb"), DUMP_FOLDER("plain_zset_2_v11_raw.json"), &r2jConf); +} + +static void test_r2j_zset_lp_data(void **state) { + UNUSED(state); + RdbxToJsonConf r2jConf = DEF_CONF(RDB_LEVEL_DATA); + testRdbToJsonCommon(DUMP_FOLDER("zset_lp_v11.rdb"), DUMP_FOLDER("zset_lp_v11_data.json"), &r2jConf); +} + +static void test_r2j_zset_lp_struct(void **state) { + UNUSED(state); + RdbxToJsonConf r2jConf = DEF_CONF(RDB_LEVEL_STRUCT); + testRdbToJsonCommon(DUMP_FOLDER("zset_lp_v11.rdb"), DUMP_FOLDER("zset_lp_v11_struct.json"), &r2jConf); +} + +static void test_r2j_zset_lp_raw(void **state) { + UNUSED(state); + RdbxToJsonConf r2jConf = DEF_CONF(RDB_LEVEL_RAW); + testRdbToJsonCommon(DUMP_FOLDER("zset_lp_v11.rdb"), DUMP_FOLDER("zset_lp_v11_raw.json"), &r2jConf); +} + +static void test_r2j_zset_zl_data(void **state) { + UNUSED(state); + RdbxToJsonConf r2jConf = DEF_CONF(RDB_LEVEL_DATA); + testRdbToJsonCommon(DUMP_FOLDER("zset_zl_v6.rdb"), DUMP_FOLDER("zset_zl_v6_data.json"), &r2jConf); +} + +static void test_r2j_zset_zl_struct(void **state) { + UNUSED(state); + RdbxToJsonConf r2jConf = DEF_CONF(RDB_LEVEL_STRUCT); + testRdbToJsonCommon(DUMP_FOLDER("zset_zl_v6.rdb"), DUMP_FOLDER("zset_zl_v6_struct.json"), &r2jConf); +} + +static void test_r2j_zset_zl_raw(void **state) { + UNUSED(state); + RdbxToJsonConf r2jConf = DEF_CONF(RDB_LEVEL_RAW); + testRdbToJsonCommon(DUMP_FOLDER("zset_zl_v6.rdb"), DUMP_FOLDER("zset_zl_v6_raw.json"), &r2jConf); +} + static void test_r2j_quicklist_data(void **state) { UNUSED(state); RdbxToJsonConf r2jConf = DEF_CONF(RDB_LEVEL_DATA); @@ -449,6 +521,20 @@ int group_rdb_to_json(void) { cmocka_unit_test(test_r2j_set_lp_struct), cmocka_unit_test(test_r2j_set_lp_raw), + /* zset */ + cmocka_unit_test(test_r2j_plain_zset_data), + cmocka_unit_test(test_r2j_plain_zset_struct), + cmocka_unit_test(test_r2j_plain_zset_raw), + cmocka_unit_test(test_r2j_plain_zset_2_data), + cmocka_unit_test(test_r2j_plain_zset_2_struct), + cmocka_unit_test(test_r2j_plain_zset_2_raw), + cmocka_unit_test(test_r2j_zset_lp_data), + cmocka_unit_test(test_r2j_zset_lp_struct), + cmocka_unit_test(test_r2j_zset_lp_raw), + cmocka_unit_test(test_r2j_zset_zl_data), + cmocka_unit_test(test_r2j_zset_zl_struct), + cmocka_unit_test(test_r2j_zset_zl_raw), + /* function */ cmocka_unit_test(test_r2j_function), diff --git a/test/test_rdb_to_redis.c b/test/test_rdb_to_redis.c index 84ca079..26618df 100644 --- a/test/test_rdb_to_redis.c +++ b/test/test_rdb_to_redis.c @@ -178,6 +178,26 @@ static void test_rdb_to_redis_set_lp(void **state) { test_rdb_to_redis_common(DUMP_FOLDER("set_lp_v11.rdb"), 1, "$4\r\nSADD", NULL); } +static void test_rdb_to_redis_plain_zset(void **state) { + UNUSED(state); + test_rdb_to_redis_common(DUMP_FOLDER("plain_zset_v6.rdb"), 1, "$4\r\nZADD", NULL); +} + +static void test_rdb_to_redis_plain_zset_2(void **state) { + UNUSED(state); + test_rdb_to_redis_common(DUMP_FOLDER("plain_zset_2_v11.rdb"), 1, "$4\r\nZADD", NULL); +} + +static void test_rdb_to_redis_zset_lp(void **state) { + UNUSED(state); + test_rdb_to_redis_common(DUMP_FOLDER("zset_lp_v11.rdb"), 1, "$4\r\nZADD", NULL); +} + +static void test_rdb_to_redis_zset_zl(void **state) { + UNUSED(state); + test_rdb_to_redis_common(DUMP_FOLDER("zset_zl_v6.rdb"), 1, "$4\r\nZADD", NULL); +} + static void test_rdb_to_redis_multiple_dbs(void **state) { UNUSED(state); test_rdb_to_redis_common(DUMP_FOLDER("multiple_dbs.rdb"), 1, NULL, NULL); @@ -319,6 +339,11 @@ int group_rdb_to_redis(void) { cmocka_unit_test_setup(test_rdb_to_redis_plain_set, setupTest), cmocka_unit_test_setup(test_rdb_to_redis_set_is, setupTest), cmocka_unit_test_setup(test_rdb_to_redis_set_lp, setupTest), + /* zset */ + cmocka_unit_test_setup(test_rdb_to_redis_plain_zset, setupTest), + cmocka_unit_test_setup(test_rdb_to_redis_plain_zset_2, setupTest), + cmocka_unit_test_setup(test_rdb_to_redis_zset_lp, setupTest), + cmocka_unit_test_setup(test_rdb_to_redis_zset_zl, setupTest), /* expired keys */ cmocka_unit_test_setup(test_rdb_to_redis_set_expired, setupTest), diff --git a/test/test_rdb_to_resp.c b/test/test_rdb_to_resp.c index f981313..fc419ac 100644 --- a/test/test_rdb_to_resp.c +++ b/test/test_rdb_to_resp.c @@ -219,6 +219,26 @@ static void test_r2r_set_lp(void **state) { runWithAndWithoutRestore("set_is_v11.rdb"); } +static void test_r2r_plain_zset(void **state) { + UNUSED(state); + runWithAndWithoutRestore("plain_zset_v6.rdb"); +} + +static void test_r2r_plain_zset_2(void **state) { + UNUSED(state); + runWithAndWithoutRestore("plain_zset_2_v11.rdb"); +} + +static void test_r2r_zset_lp(void **state) { + UNUSED(state); + runWithAndWithoutRestore("zset_lp_v11.rdb"); +} + +static void test_r2r_zset_zl(void **state) { + UNUSED(state); + runWithAndWithoutRestore("zset_zl_v6.rdb"); +} + static void test_r2r_module(void **state) { UNUSED(state); unsigned char expRespData[] = { @@ -280,6 +300,11 @@ int group_rdb_to_resp(void) { cmocka_unit_test(test_r2r_plain_set), cmocka_unit_test(test_r2r_set_is), cmocka_unit_test(test_r2r_set_lp), + /* zset */ + cmocka_unit_test(test_r2r_plain_zset), + cmocka_unit_test(test_r2r_plain_zset_2), + cmocka_unit_test(test_r2r_zset_lp), + cmocka_unit_test(test_r2r_zset_zl), /* misc */ cmocka_unit_test(test_r2r_multiple_lists_and_strings),