diff --git a/src/lib/core/tweaks.c b/src/lib/core/tweaks.c index 5b09549188395c035758ad2c2b8b9e02b37ded51..6550a4f520f42a9300ad6080e35bd90835bd2db2 100644 --- a/src/lib/core/tweaks.c +++ b/src/lib/core/tweaks.c @@ -6,6 +6,7 @@ #include "tweaks.h" #include <assert.h> +#include <inttypes.h> #include <stddef.h> #include <stdbool.h> #include <stdint.h> @@ -111,18 +112,53 @@ tweak_get_int(struct tweak *tweak, struct tweak_value *val) { assert(tweak->get == tweak_get_int); val->type = TWEAK_VALUE_INT; - val->ival = *(int *)tweak->data; + val->ival = *(int64_t *)tweak->data; } int tweak_set_int(struct tweak *tweak, const struct tweak_value *val) { assert(tweak->set == tweak_set_int); - if (val->type != TWEAK_VALUE_INT) { + if (val->type == TWEAK_VALUE_INT) { + *(int64_t *)tweak->data = val->ival; + } else if (val->type == TWEAK_VALUE_UINT) { + if (val->uval > INT64_MAX) { + diag_set(IllegalParams, "Invalid value, " + "must be <= %" PRIi64, INT64_MAX); + return -1; + } + *(int64_t *)tweak->data = (int64_t)val->uval; + } else { + diag_set(IllegalParams, "Invalid value, expected integer"); + return -1; + } + return 0; +} + +void +tweak_get_uint(struct tweak *tweak, struct tweak_value *val) +{ + assert(tweak->get == tweak_get_uint); + val->type = TWEAK_VALUE_UINT; + val->uval = *(uint64_t *)tweak->data; +} + +int +tweak_set_uint(struct tweak *tweak, const struct tweak_value *val) +{ + assert(tweak->set == tweak_set_uint); + if (val->type == TWEAK_VALUE_UINT) { + *(uint64_t *)tweak->data = val->uval; + } else if (val->type == TWEAK_VALUE_INT) { + if (val->ival < 0) { + diag_set(IllegalParams, "Invalid value, must be >= 0"); + return -1; + } + *(uint64_t *)tweak->data = (uint64_t)val->ival; + } else { diag_set(IllegalParams, "Invalid value, expected integer"); return -1; } - *(int *)tweak->data = val->ival; return 0; } @@ -140,6 +176,8 @@ tweak_set_double(struct tweak *tweak, const struct tweak_value *val) assert(tweak->set == tweak_set_double); if (val->type == TWEAK_VALUE_INT) { *(double *)tweak->data = val->ival; + } else if (val->type == TWEAK_VALUE_UINT) { + *(double *)tweak->data = val->uval; } else if (val->type == TWEAK_VALUE_DOUBLE) { *(double *)tweak->data = val->dval; } else { diff --git a/src/lib/core/tweaks.h b/src/lib/core/tweaks.h index a451e3ee03b3a713c10a05300b9c603c1a293fc5..1dfbc46ea92c82aba791e97aa4cf9d01db588433 100644 --- a/src/lib/core/tweaks.h +++ b/src/lib/core/tweaks.h @@ -7,6 +7,7 @@ #include <assert.h> #include <stdbool.h> +#include <stdint.h> #include "diag.h" #include "trivia/util.h" @@ -16,7 +17,7 @@ * an arbitrary C variable. To register a tweak, use a TWEAK_XXX macro * at the global level in a C source file, for example: * - * static int my_var; + * static int64_t my_var; * TWEAK_INT(my_var); * * This will create a tweak with name "my_var" that can be accessed with @@ -35,6 +36,7 @@ extern "C" { enum tweak_value_type { TWEAK_VALUE_BOOL, TWEAK_VALUE_INT, + TWEAK_VALUE_UINT, TWEAK_VALUE_DOUBLE, TWEAK_VALUE_STR, }; @@ -47,7 +49,9 @@ struct tweak_value { /** TWEAK_VALUE_BOOL */ bool bval; /** TWEAK_VALUE_INT */ - int ival; + int64_t ival; + /** TWEAK_VALUE_UINT */ + uint64_t uval; /** TWEAK_VALUE_DOUBLE */ double dval; /** TWEAK_VALUE_STR */ @@ -166,9 +170,22 @@ tweak_set_int(struct tweak *tweak, const struct tweak_value *val); /** Registers a tweak for an integer variable. */ #define TWEAK_INT(var) \ -STATIC_ASSERT_VAR_TYPE(var, int) \ +STATIC_ASSERT_VAR_TYPE(var, int64_t) \ TWEAK(var, tweak_get_int, tweak_set_int) +/** Unsigned integer tweak value getter. */ +void +tweak_get_uint(struct tweak *tweak, struct tweak_value *val); + +/** Unsigned integer tweak value setter. */ +int +tweak_set_uint(struct tweak *tweak, const struct tweak_value *val); + +/** Registers a tweak for an unsigned integer variable. */ +#define TWEAK_UINT(var) \ +STATIC_ASSERT_VAR_TYPE(var, uint64_t) \ +TWEAK(var, tweak_get_uint, tweak_set_uint) + /** Double tweak value getter. */ void tweak_get_double(struct tweak *tweak, struct tweak_value *val); diff --git a/src/lua/tweaks.c b/src/lua/tweaks.c index 563623ee9f2892420f89e15fa4b8e06368a6d22f..e74f044b9ef409e6ad9d019957bdd77a19c1482d 100644 --- a/src/lua/tweaks.c +++ b/src/lua/tweaks.c @@ -12,6 +12,8 @@ #include "core/tweaks.h" #include "diag.h" +#include "lua/msgpack.h" +#include "lua/serializer.h" #include "lua/utils.h" #include "trivia/util.h" @@ -26,7 +28,10 @@ luaT_push_tweak_value(struct lua_State *L, const struct tweak_value *v) lua_pushboolean(L, v->bval); return 1; case TWEAK_VALUE_INT: - lua_pushinteger(L, v->ival); + luaL_pushint64(L, v->ival); + return 1; + case TWEAK_VALUE_UINT: + luaL_pushuint64(L, v->uval); return 1; case TWEAK_VALUE_DOUBLE: lua_pushnumber(L, v->dval); @@ -71,26 +76,29 @@ luaT_tweaks_newindex(struct lua_State *L) return luaT_error(L); } struct tweak_value v; - switch (lua_type(L, 3)) { - case LUA_TBOOLEAN: + struct luaL_field field; + if (luaL_tofield(L, luaL_msgpack_default, 3, &field) != 0) + return luaT_error(L); + switch (field.type) { + case MP_BOOL: v.type = TWEAK_VALUE_BOOL; - v.bval = lua_toboolean(L, 3); + v.bval = field.bval; break; - case LUA_TNUMBER: { - int ival = lua_tointeger(L, 3); - double dval = lua_tonumber(L, 3); - if (ival == dval) { - v.type = TWEAK_VALUE_INT; - v.ival = ival; - } else { - v.type = TWEAK_VALUE_DOUBLE; - v.dval = dval; - } + case MP_INT: + v.type = TWEAK_VALUE_INT; + v.ival = field.ival; break; - } - case LUA_TSTRING: + case MP_UINT: + v.type = TWEAK_VALUE_UINT; + v.uval = (uint64_t)field.ival; + break; + case MP_DOUBLE: + v.type = TWEAK_VALUE_DOUBLE; + v.dval = field.dval; + break; + case MP_STR: v.type = TWEAK_VALUE_STR; - v.sval = lua_tostring(L, 3); + v.sval = field.sval.data; break; default: diag_set(IllegalParams, diff --git a/test/unit/lua_tweaks.c b/test/unit/lua_tweaks.c index 08b215ec72fa12c1c29dd46b8ff63c0a2610953f..6196d2da6a48e15abca9d1e0f7fab5ec40f42b99 100644 --- a/test/unit/lua_tweaks.c +++ b/test/unit/lua_tweaks.c @@ -2,12 +2,14 @@ #include "lualib.h" #include <lauxlib.h> #include <stdbool.h> +#include <stdint.h> #include <string.h> #include "core/tweaks.h" #include "diag.h" #include "fiber.h" #include "lua/error.h" +#include "lua/msgpack.h" #include "lua/tweaks.h" #include "lua/utils.h" #include "memory.h" @@ -22,9 +24,12 @@ static struct lua_State *L; static bool bool_var = true; TWEAK_BOOL(bool_var); -static int int_var = 42; +static int64_t int_var = 42; TWEAK_INT(int_var); +static uint64_t uint_var = 123; +TWEAK_UINT(uint_var); + static double double_var = 3.14; TWEAK_DOUBLE(double_var); @@ -45,7 +50,7 @@ TWEAK_ENUM(my_enum, enum_var); static void test_index(void) { - plan(10); + plan(12); header(); lua_settop(L, 0); is(luaT_dostring(L, "return tweaks.no_such_var"), 0, "no_such_var"); @@ -57,6 +62,9 @@ test_index(void) is(luaT_dostring(L, "return tweaks.int_var"), 0, "int_var"); ok(!lua_isnoneornil(L, 1), "int_var found"); lua_settop(L, 0); + is(luaT_dostring(L, "return tweaks.uint_var"), 0, "uint_var"); + ok(!lua_isnoneornil(L, 1), "uint_var found"); + lua_settop(L, 0); is(luaT_dostring(L, "return tweaks.double_var"), 0, "double_var"); ok(!lua_isnoneornil(L, 1), "double_var found"); lua_settop(L, 0); @@ -70,7 +78,7 @@ test_index(void) static void test_newindex(void) { - plan(6); + plan(4); header(); is(luaT_dostring(L, "tweaks.no_such_var = 1"), -1, "unknown option"); ok(!diag_is_empty(diag_get()) && @@ -82,12 +90,6 @@ test_newindex(void) strcmp(diag_last_error(diag_get())->errmsg, "Value must be boolean, number, or string") == 0, "check error"); - is(luaT_dostring(L, "tweaks.bool_var = 999999999999999999LLU"), -1, - "invalid value - big int"); - ok(!diag_is_empty(diag_get()) && - strcmp(diag_last_error(diag_get())->errmsg, - "Value must be boolean, number, or string") == 0, - "check error"); footer(); check_plan(); } @@ -121,7 +123,7 @@ test_bool_var(void) static void test_int_var(void) { - plan(10); + plan(22); header(); lua_settop(L, 0); is(luaT_dostring(L, "tweaks.int_var = true"), -1, @@ -130,6 +132,30 @@ test_int_var(void) strcmp(diag_last_error(diag_get())->errmsg, "Invalid value, expected integer") == 0, "check error"); + is(luaT_dostring(L, "tweaks.int_var = 1.5"), -1, + "set double value"); + ok(!diag_is_empty(diag_get()) && + strcmp(diag_last_error(diag_get())->errmsg, + "Invalid value, expected integer") == 0, + "check error"); + is(luaT_dostring(L, "tweaks.int_var = 9223372036854775808ULL"), -1, + "set too big value"); + ok(!diag_is_empty(diag_get()) && + strcmp(diag_last_error(diag_get())->errmsg, + "Invalid value, must be <= 9223372036854775807") == 0, + "check error"); + is(luaT_dostring(L, "tweaks.int_var = -9223372036854775808LL"), 0, + "set min value"); + is(int_var, INT64_MIN, "check C value"); + is(luaT_dostring(L, "return tweaks.int_var"), 0, "get value"); + is(luaL_toint64(L, 1), INT64_MIN, "check Lua value"); + lua_settop(L, 0); + is(luaT_dostring(L, "tweaks.int_var = 9223372036854775807LL"), 0, + "set max value"); + is(int_var, INT64_MAX, "check C value"); + is(luaT_dostring(L, "return tweaks.int_var"), 0, "get value"); + is(luaL_toint64(L, 1), INT64_MAX, "check Lua value"); + lua_settop(L, 0); is(luaT_dostring(L, "tweaks.int_var = 11"), 0, "set value"); is(int_var, 11, "check C value"); is(luaT_dostring(L, "return tweaks.int_var"), 0, "get value"); @@ -144,10 +170,54 @@ test_int_var(void) check_plan(); } +static void +test_uint_var(void) +{ + plan(18); + header(); + lua_settop(L, 0); + is(luaT_dostring(L, "tweaks.uint_var = true"), -1, + "set invalid value"); + ok(!diag_is_empty(diag_get()) && + strcmp(diag_last_error(diag_get())->errmsg, + "Invalid value, expected integer") == 0, + "check error"); + is(luaT_dostring(L, "tweaks.uint_var = 1.5"), -1, + "set double value"); + ok(!diag_is_empty(diag_get()) && + strcmp(diag_last_error(diag_get())->errmsg, + "Invalid value, expected integer") == 0, + "check error"); + is(luaT_dostring(L, "tweaks.uint_var = -1"), -1, + "set negative value"); + ok(!diag_is_empty(diag_get()) && + strcmp(diag_last_error(diag_get())->errmsg, + "Invalid value, must be >= 0") == 0, + "check error"); + is(luaT_dostring(L, "tweaks.uint_var = 18446744073709551615ULL"), 0, + "set max value"); + is(uint_var, UINT64_MAX, "check C value"); + is(luaT_dostring(L, "return tweaks.uint_var"), 0, "get value"); + is(luaL_touint64(L, 1), UINT64_MAX, "check Lua value"); + lua_settop(L, 0); + is(luaT_dostring(L, "tweaks.uint_var = 11"), 0, "set value"); + is(uint_var, 11, "check C value"); + is(luaT_dostring(L, "return tweaks.uint_var"), 0, "get value"); + is(lua_tointeger(L, 1), 11, "check Lua value"); + lua_settop(L, 0); + is(luaT_dostring(L, "tweaks.uint_var = 123"), 0, "set value"); + is(uint_var, 123, "check C value"); + is(luaT_dostring(L, "return tweaks.uint_var"), 0, "get value"); + is(lua_tointeger(L, 1), 123, "check Lua value"); + lua_settop(L, 0); + footer(); + check_plan(); +} + static void test_double_var(void) { - plan(10); + plan(18); header(); lua_settop(L, 0); is(luaT_dostring(L, "tweaks.double_var = true"), -1, @@ -161,6 +231,18 @@ test_double_var(void) is(luaT_dostring(L, "return tweaks.double_var"), 0, "get value"); is(lua_tonumber(L, 1), 11, "check Lua value"); lua_settop(L, 0); + is(luaT_dostring(L, "tweaks.double_var = -9223372036854775808LL"), 0, + "set min int value"); + is(double_var, (double)INT64_MIN, "check C value"); + is(luaT_dostring(L, "return tweaks.double_var"), 0, "get value"); + is(lua_tonumber(L, 1), (double)INT64_MIN, "check Lua value"); + lua_settop(L, 0); + is(luaT_dostring(L, "tweaks.double_var = 18446744073709551615ULL"), 0, + "set max int value"); + is(double_var, (double)UINT64_MAX, "check C value"); + is(luaT_dostring(L, "return tweaks.double_var"), 0, "get value"); + is(lua_tonumber(L, 1), (double)UINT64_MAX, "check Lua value"); + lua_settop(L, 0); is(luaT_dostring(L, "tweaks.double_var = 3.14"), 0, "set double value"); is(double_var, 3.14, "check C value"); is(luaT_dostring(L, "return tweaks.double_var"), 0, "get value"); @@ -205,7 +287,7 @@ test_enum_var(void) static void test_tweak_table(const char *method) { - plan(5); + plan(6); header(); lua_settop(L, 0); is(luaT_dostring(L, tt_sprintf("return getmetatable(tweaks).%s()", @@ -217,6 +299,9 @@ test_tweak_table(const char *method) lua_getfield(L, 1, "int_var"); is(lua_tointeger(L, 2), 42, "int_var"); lua_pop(L, 1); + lua_getfield(L, 1, "uint_var"); + is(lua_tointeger(L, 2), 123, "uint_var"); + lua_pop(L, 1); lua_getfield(L, 1, "double_var"); is(lua_tonumber(L, 2), 3.14, "double_var"); lua_pop(L, 1); @@ -251,12 +336,13 @@ test_autocomplete(void) static int test_lua_tweaks(void) { - plan(8); + plan(9); header(); test_index(); test_newindex(); test_bool_var(); test_int_var(); + test_uint_var(); test_double_var(); test_enum_var(); test_serialize(); @@ -272,6 +358,8 @@ main(void) fiber_init(fiber_c_invoke); L = luaT_newteststate(); + luaopen_msgpack(L); + lua_pop(L, 1); tarantool_lua_tweaks_init(L); /* diff --git a/test/unit/tweaks.c b/test/unit/tweaks.c index 46c7a5d36a74c4e55a9820ef1f81ce903ceb00d4..2a8683d4d16ff5b4f428790f7feb77fe0320b6d4 100644 --- a/test/unit/tweaks.c +++ b/test/unit/tweaks.c @@ -1,5 +1,6 @@ #include <stdbool.h> #include <stddef.h> +#include <stdint.h> #include <string.h> #include "diag.h" @@ -14,9 +15,12 @@ static bool bool_var = true; TWEAK_BOOL(bool_var); -static int int_var = 42; +static int64_t int_var = 42; TWEAK_INT(int_var); +static uint64_t uint_var = 123; +TWEAK_UINT(uint_var); + static double double_var = 3.14; TWEAK_DOUBLE(double_var); @@ -37,11 +41,12 @@ TWEAK_ENUM(my_enum, enum_var); static void test_lookup(void) { - plan(5); + plan(6); header(); ok(tweak_find("no_such_var") == NULL, "no_such_var not found"); ok(tweak_find("bool_var") != NULL, "bool_var found"); ok(tweak_find("int_var") != NULL, "int_var found"); + ok(tweak_find("uint_var") != NULL, "uint_var found"); ok(tweak_find("double_var") != NULL, "double_var found"); ok(tweak_find("enum_var") != NULL, "enum_var found"); footer(); @@ -60,6 +65,9 @@ test_foreach_cb(const char *name, struct tweak *tweak, void *arg) } else if (strcmp(name, "int_var") == 0) { is(v.type, TWEAK_VALUE_INT, "int_var tweak value type"); is(v.ival, 42, "int_var tweak value"); + } else if (strcmp(name, "uint_var") == 0) { + is(v.type, TWEAK_VALUE_UINT, "uint_var tweak value type"); + is(v.ival, 123, "uint_var tweak value"); } else if (strcmp(name, "double_var") == 0) { is(v.type, TWEAK_VALUE_DOUBLE, "double_var tweak value type"); is(v.dval, 3.14, "double_var tweak value"); @@ -73,7 +81,7 @@ test_foreach_cb(const char *name, struct tweak *tweak, void *arg) static void test_foreach(void) { - plan(8); + plan(10); header(); tweak_foreach(test_foreach_cb, NULL); footer(); @@ -154,7 +162,7 @@ test_bool_var(void) static void test_int_var(void) { - plan(15); + plan(22); header(); struct tweak *t; struct tweak_value v; @@ -172,16 +180,31 @@ test_int_var(void) "Invalid value, expected integer") == 0, "diag after set invalid tweak value type"); is(int_var, 42, "var value after failed set"); + v.type = TWEAK_VALUE_UINT; + v.uval = (uint64_t)INT64_MAX + 1; + is(tweak_set(t, &v), -1, "set too big value"); + ok(!diag_is_empty(diag_get()) && + strcmp(diag_last_error(diag_get())->errmsg, + "Invalid value, must be <= 9223372036854775807") == 0, + "diag after set too big value"); + is(int_var, 42, "var value after failed set"); tweak_get(t, &v); is(v.type, TWEAK_VALUE_INT, "tweak value type after failed set"); is(v.ival, 42, "tweak value after failed set"); v.type = TWEAK_VALUE_INT; + v.ival = -11; + is(tweak_set(t, &v), 0, "set tweak value to int"); + is(int_var, -11, "var value after set to int"); + tweak_get(t, &v); + is(v.type, TWEAK_VALUE_INT, "tweak value type after set to int"); + is(v.ival, -11, "tweak value after set to int"); + v.type = TWEAK_VALUE_UINT; v.ival = 11; - is(tweak_set(t, &v), 0, "set tweak value"); - is(int_var, 11, "var value after set"); + is(tweak_set(t, &v), 0, "set tweak value to uint"); + is(int_var, 11, "var value after set to uint"); tweak_get(t, &v); - is(v.type, TWEAK_VALUE_INT, "tweak value type after set"); - is(v.ival, 11, "tweak value after set"); + is(v.type, TWEAK_VALUE_INT, "tweak value type after set to uint"); + is(v.ival, 11, "tweak value after set to uint"); int_var = 42; tweak_get(t, &v); is(v.type, TWEAK_VALUE_INT, "tweak value type after var update"); @@ -190,10 +213,64 @@ test_int_var(void) check_plan(); } +static void +test_uint_var(void) +{ + plan(22); + header(); + struct tweak *t; + struct tweak_value v; + t = tweak_find("uint_var"); + ok(t != NULL, "tweak found"); + tweak_get(t, &v); + is(uint_var, 123, "init var value"); + is(v.type, TWEAK_VALUE_UINT, "init tweak value type"); + is(v.uval, 123, "init tweak value"); + v.type = TWEAK_VALUE_BOOL; + v.bval = true; + is(tweak_set(t, &v), -1, "set invalid tweak value type"); + ok(!diag_is_empty(diag_get()) && + strcmp(diag_last_error(diag_get())->errmsg, + "Invalid value, expected integer") == 0, + "diag after set invalid tweak value type"); + is(uint_var, 123, "var value after failed set"); + v.type = TWEAK_VALUE_INT; + v.ival = -1; + is(tweak_set(t, &v), -1, "set negative value"); + ok(!diag_is_empty(diag_get()) && + strcmp(diag_last_error(diag_get())->errmsg, + "Invalid value, must be >= 0") == 0, + "diag after set negative value"); + is(uint_var, 123, "var value after failed set"); + tweak_get(t, &v); + is(v.type, TWEAK_VALUE_UINT, "tweak value type after failed set"); + is(v.uval, 123, "tweak value after failed set"); + v.type = TWEAK_VALUE_INT; + v.ival = 11; + is(tweak_set(t, &v), 0, "set tweak value to int"); + is(uint_var, 11, "var value after set to int"); + tweak_get(t, &v); + is(v.type, TWEAK_VALUE_UINT, "tweak value type after set to int"); + is(v.uval, 11, "tweak value after set to int"); + v.type = TWEAK_VALUE_UINT; + v.uval = 22; + is(tweak_set(t, &v), 0, "set tweak value to uint"); + is(uint_var, 22, "var value after set to uint"); + tweak_get(t, &v); + is(v.type, TWEAK_VALUE_UINT, "tweak value type after set to uint"); + is(v.uval, 22, "tweak value after set to uint"); + uint_var = 123; + tweak_get(t, &v); + is(v.type, TWEAK_VALUE_UINT, "tweak value type after var update"); + is(v.uval, 123, "tweak value after var update"); + footer(); + check_plan(); +} + static void test_double_var(void) { - plan(19); + plan(23); header(); struct tweak *t; struct tweak_value v; @@ -215,12 +292,19 @@ test_double_var(void) is(v.type, TWEAK_VALUE_DOUBLE, "tweak value type after failed set"); is(v.dval, 3.14, "tweak value after failed set"); v.type = TWEAK_VALUE_INT; - v.ival = 11; + v.ival = -11; is(tweak_set(t, &v), 0, "set tweak value to int"); - is(double_var, 11, "var value after set to int"); + is(double_var, -11, "var value after set to int"); tweak_get(t, &v); is(v.type, TWEAK_VALUE_DOUBLE, "tweak value type after set to int"); - is(v.dval, 11, "tweak value after set to int"); + is(v.dval, -11, "tweak value after set to int"); + v.type = TWEAK_VALUE_UINT; + v.uval = 11; + is(tweak_set(t, &v), 0, "set tweak value to uint"); + is(double_var, 11, "var value after set to uint"); + tweak_get(t, &v); + is(v.type, TWEAK_VALUE_DOUBLE, "tweak value type after set to uint"); + is(v.dval, 11, "tweak value after set to uint"); v.type = TWEAK_VALUE_DOUBLE; v.dval = 0.5; is(tweak_set(t, &v), 0, "set tweak value to double"); @@ -285,13 +369,14 @@ test_enum_var(void) static int test_tweaks(void) { - plan(7); + plan(8); header(); test_lookup(); test_foreach(); test_foreach_break(); test_bool_var(); test_int_var(); + test_uint_var(); test_double_var(); test_enum_var(); footer();