diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt index 08a67f4ac997564a981bcab580428a248e77b7b1..764d85c154a1099d74d74ddd10116b891b206649 100644 --- a/src/box/CMakeLists.txt +++ b/src/box/CMakeLists.txt @@ -62,3 +62,5 @@ add_library(box lua/error.cc lua/session.cc ${bin_sources}) + +# target_link_libraries(box salad) diff --git a/src/ffisyms.cc b/src/ffisyms.cc index 808d7af999b4c1ff84bdc87ea22c4d683cc4bea2..e0d34d7703811759ade585ed49df800b980dd77f 100644 --- a/src/ffisyms.cc +++ b/src/ffisyms.cc @@ -11,6 +11,7 @@ #include "lua/bsdsocket.h" #include "lua/digest.h" #include "base64.h" +#include <lib/salad/guava.h> /* * A special hack to cc/ld to keep symbols in an optimized binary. @@ -50,5 +51,6 @@ void *ffi_symbols[] = { (void *) base64_decode, (void *) base64_encode, (void *) base64_bufsize, - (void *) SHA1internal + (void *) SHA1internal, + (void *) guava }; diff --git a/src/lib/salad/CMakeLists.txt b/src/lib/salad/CMakeLists.txt index 9db3d800e80b8288bb0ed709e7b0e65865a53c06..26b8a26eacdd53eefbdc7f2b85746e4117a6e1bd 100644 --- a/src/lib/salad/CMakeLists.txt +++ b/src/lib/salad/CMakeLists.txt @@ -1,3 +1,3 @@ -set(lib_sources rope.c rlist.c rtree.c) +set(lib_sources rope.c rlist.c rtree.c guava.c) set_source_files_compile_flags(${lib_sources}) add_library(salad ${lib_sources}) diff --git a/src/lib/salad/guava.c b/src/lib/salad/guava.c new file mode 100644 index 0000000000000000000000000000000000000000..84fc5cbfa2f91e7a6a1219513ca7af6f5c2e4d59 --- /dev/null +++ b/src/lib/salad/guava.c @@ -0,0 +1,55 @@ +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``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 + * <COPYRIGHT HOLDER> 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 "lib/salad/guava.h" + +#include <stdint.h> + +static const int64_t K = 2862933555777941757; +static const double D = 0x1.0p31; + +static inline double lcg(int64_t *state) +{ + return (double )((int32_t)(((uint64_t )*state >> 33) + 1)) / D; +} + +int32_t +guava(int64_t state, int32_t buckets) +{ + int32_t candidate = 0; + int32_t next; + while (1) { + state = K * state + 1; + next = (int32_t)((candidate + 1) / lcg(&state)); + if (next >= 0 && next < buckets) + candidate = next; + else + return candidate; + } +} diff --git a/src/lib/salad/guava.h b/src/lib/salad/guava.h new file mode 100644 index 0000000000000000000000000000000000000000..6a9e877dad8c892c7a59b2c1e381c70132b3c7de --- /dev/null +++ b/src/lib/salad/guava.h @@ -0,0 +1,46 @@ +#ifndef TARANTOOL_LIB_GUAVA_H_INCLUDED +#define TARANTOOL_LIB_GUAVA_H_INCLUDED + +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``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 + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdint.h> + +#if defined(__cplusplus) +extern "C" { +#endif + +int32_t +guava(int64_t state, int32_t buckets); + +#if defined(__cplusplus) +} /* extern C */ +#endif + +#endif /* TARANTOOL_LIB_GUAVA_H_INCLUDED */ diff --git a/src/lua/digest.lua b/src/lua/digest.lua index 1e41483f6622795b1e4d47b870e3fdc2edae0041..724bfb24d7825ae9c86009c879495f59f9c4f75d 100644 --- a/src/lua/digest.lua +++ b/src/lua/digest.lua @@ -21,9 +21,9 @@ ffi.cdef[[ /* from libc */ int snprintf(char *str, size_t size, const char *format, ...); - typedef uint32_t (*crc32_func)(uint32_t crc, const unsigned char *buf, unsigned int len); + extern int32_t guava(int64_t state, int32_t buckets); extern crc32_func crc32_calc; /* base64 */ @@ -53,13 +53,12 @@ end local def = { sha = { 'SHA', 20 }, --- sha1 = { 'SHA1', 20 }, sha224 = { 'SHA224', 28 }, sha256 = { 'SHA256', 32 }, sha384 = { 'SHA384', 48 }, sha512 = { 'SHA512', 64 }, md5 = { 'MD5', 16 }, - md4 = { 'MD4', 16 } + md4 = { 'MD4', 16 }, } local hexres = ffi.new('char[129]') @@ -131,7 +130,11 @@ local m = { end local r = ffi.C.SHA1internal(str, #str, nil) return tohex(r, 20) - end + end, + + guava = function(state, buckets) + return ffi.C.guava(state, buckets) + end, } if ssl ~= nil then @@ -149,7 +152,7 @@ if ssl ~= nil then local r = ssl[hfunction](str, string.len(str), nil) return ffi.string(r, hsize) end - + m[ pname .. '_hex' ] = function(str) if str == nil then str = '' diff --git a/test/box/digest.result b/test/box/digest.result index a5b1e4b69da769ac69226535e1c0a55b8c54ec1b..f290ccc0823c6ee878a1dc2859630557c0922cc0 100644 --- a/test/box/digest.result +++ b/test/box/digest.result @@ -193,19 +193,39 @@ digest.base64_decode(b) == s ... digest.base64_decode(nil) --- -- error: 'builtin/digest.lua:89: Usage: digest.base64_decode(string)' +- error: 'builtin/digest.lua:88: Usage: digest.base64_decode(string)' ... digest.base64_encode(nil) --- -- error: 'builtin/digest.lua:78: Usage: digest.base64_encode(string)' +- error: 'builtin/digest.lua:77: Usage: digest.base64_encode(string)' ... digest.base64_encode(123) --- -- error: 'builtin/digest.lua:78: Usage: digest.base64_encode(string)' +- error: 'builtin/digest.lua:77: Usage: digest.base64_encode(string)' ... digest.base64_decode(123) --- -- error: 'builtin/digest.lua:89: Usage: digest.base64_decode(string)' +- error: 'builtin/digest.lua:88: Usage: digest.base64_decode(string)' +... +digest.guava('hello', 0) +--- +- error: 'bad argument #1 to ''?'' (cannot convert ''string'' to ''int64_t'')' +... +digest.guava(1, 'nope_') +--- +- error: 'bad argument #2 to ''?'' (cannot convert ''string'' to ''int'')' +... +digest.guava(10863919174838991, 11) +--- +- 8 +... +digest.guava(2016238256797177309, 11) +--- +- 7 +... +digest.guava(1673758223894951030, 11) +--- +- 7 ... digest = nil --- diff --git a/test/box/digest.test.lua b/test/box/digest.test.lua index 6475bf6b650136b9cd62cc81a0d1b134d9989b7d..a4583eece4f2fb356fd3df99330086a35c11c338 100644 --- a/test/box/digest.test.lua +++ b/test/box/digest.test.lua @@ -59,4 +59,10 @@ digest.base64_encode(nil) digest.base64_encode(123) digest.base64_decode(123) +digest.guava('hello', 0) +digest.guava(1, 'nope_') +digest.guava(10863919174838991, 11) +digest.guava(2016238256797177309, 11) +digest.guava(1673758223894951030, 11) + digest = nil diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 8812c891540a2ca88c68cbc7b02d56a91f69f396..0df204d057626b048ddfba8928dc078377577bfa 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -73,3 +73,6 @@ add_executable(scramble.test scramble.c ${CMAKE_SOURCE_DIR}/third_party/sha1.c ${CMAKE_SOURCE_DIR}/third_party/base64.c ${CMAKE_SOURCE_DIR}/src/random.c) + +add_executable(guava.test guava.c) +target_link_libraries(guava.test salad) diff --git a/test/unit/guava.c b/test/unit/guava.c new file mode 100644 index 0000000000000000000000000000000000000000..6b38871db32fa29ab0c2708fb7a5101654c1d250 --- /dev/null +++ b/test/unit/guava.c @@ -0,0 +1,68 @@ +#include <stdlib.h> +#include <stdio.h> +#include <time.h> + +#include "unit.h" +#include "salad/guava.h" + +static void +check_guava_correctness(uint64_t code) +{ + int32_t last = 0; + for (int32_t shards = 1; shards <= 100000; shards++) { + int32_t b = guava(code, shards); + if (b != last) { + fail_if(shards - 1 != b); + last = b; + } + } +} + +static void +correctness_check() +{ + header(); + int64_t i_vals[] = {0, 1, 2}; + for (size_t i = 0; i < sizeof(i_vals) / sizeof(int64_t); ++i) + check_guava_correctness(i_vals[i]); + srand(time(NULL)); + for (size_t i = 0; i < 20; ++i) + check_guava_correctness(rand() % 7); + footer(); +} + +static void +sameresult_check() +{ + header(); + fail_if(guava(100, 20) != guava(100, 20)); + footer(); +} + +static void +lcg_compat_check() +{ + header(); + int32_t golden100[] = { + 0, 55, 62, 8, 45, 59, 86, 97, 82, 59, + 73, 37, 17, 56, 86, 21, 90, 37, 38, 83 + }; + for (size_t i = 0; i < sizeof(golden100) / sizeof(int64_t); ++i) + check_guava_correctness(golden100[i]); + + fail_if(6 != guava(10863919174838991ULL, 11)); + fail_if(3 != guava(2016238256797177309ULL, 11)); + fail_if(5 != guava(1673758223894951030ULL, 11)); + fail_if(80343 != guava(2, 100001)); + fail_if(22152 != guava(2201, 100001)); + fail_if(15018 != guava(2202, 100001)); + footer(); +} + +int +main(void) +{ + correctness_check(); + lcg_compat_check(); + sameresult_check(); +} diff --git a/test/unit/guava.result b/test/unit/guava.result new file mode 100644 index 0000000000000000000000000000000000000000..0e996c6ce096f5be6f9d9e2bbf4c8caf1c3ececb --- /dev/null +++ b/test/unit/guava.result @@ -0,0 +1,7 @@ + *** correctness_check *** + *** correctness_check: done *** + *** lcg_compat_check *** + *** lcg_compat_check: done *** + *** sameresult_check *** + *** sameresult_check: done *** + \ No newline at end of file