[Tarantool-patches] [PATCH v1 1/1] misc: introduce misc.tonumber64()
imeevma at tarantool.org
imeevma at tarantool.org
Mon Nov 16 12:34:30 MSK 2020
This patch introduces function misc.tonumber64() that accepts number,
cdata number or string and returns 64-bit integer value.
Part of tarantool/tarantool#4738
---
https://github.com/tarantool/tarantool/issues/4738
https://github.com/tarantool/luajit/tree/imeevma/gh-4738-introduce-misc-tonumber64
src/lib_misc.c | 173 ++++++++++++++++++
...-4738-incorrect-result-tonumber64.test.lua | 38 ++++
2 files changed, 211 insertions(+)
create mode 100755 test/gh-4738-incorrect-result-tonumber64.test.lua
diff --git a/src/lib_misc.c b/src/lib_misc.c
index 6f7b9a9..4228836 100644
--- a/src/lib_misc.c
+++ b/src/lib_misc.c
@@ -10,11 +10,18 @@
#include "lua.h"
#include "lmisclib.h"
+#include "lauxlib.h"
+#include <errno.h>
+#include <ctype.h>
#include "lj_obj.h"
#include "lj_str.h"
#include "lj_tab.h"
#include "lj_lib.h"
+#include "lj_err.h"
+#include "lj_ctype.h"
+#include "lj_cdata.h"
+#include "lj_state.h"
/* ------------------------------------------------------------------------ */
@@ -63,6 +70,172 @@ LJLIB_CF(misc_getmetrics)
return 1;
}
+LJLIB_CF(misc_tonumber64)
+{
+ TValue *o = L->base;
+ if (o >= L->top)
+ lj_err_arg(L, 1, LJ_ERR_NOVAL);
+ int base = luaL_optint(L, 2, -1);
+ luaL_argcheck(L, (2 <= base && base <= 36) || base == -1, 2,
+ "base out of range");
+ uint32_t ctypeid = 0;
+ GCcdata *cd;
+ CTSize size;
+ CTState *cts;
+ double val_d;
+ switch (lua_type(L, 1)) {
+ case LUA_TNUMBER:
+ base = (base == -1 ? 10 : base);
+ if (base != 10)
+ return luaL_argerror(L, 1, "string expected");
+ val_d = numV(L->base);
+ if (val_d < (double)INT64_MIN || val_d >= (double)UINT64_MAX)
+ return luaL_argerror(L, 1, "cannot convert to 64-bit integer");
+ cts = ctype_cts(L);
+ if (val_d < 0.0)
+ ctypeid = CTID_INT64;
+ else
+ ctypeid = CTID_UINT64;
+ lj_ctype_info(cts, ctypeid, &size);
+ assert(size != CTSIZE_INVALID);
+ cd = lj_cdata_new(cts, ctypeid, size);
+ o = L->top;
+ setcdataV(L, o, cd);
+ incr_top(L);
+ if (val_d < 0.0)
+ *(int64_t *) cdataptr(cd) = (int64_t)val_d;
+ else
+ *(uint64_t *) cdataptr(cd) = (uint64_t)val_d;
+ return 1;
+ case LUA_TSTRING: {
+ GCstr *s = strV(o);
+ const char *arg = strdata(s);
+ size_t argl = s->len;
+ while (argl > 0 && isspace(arg[argl - 1]))
+ argl--;
+ while (isspace(*arg)) {
+ arg++;
+ argl--;
+ }
+
+ /*
+ * Check if we're parsing custom format:
+ * 1) '0x' or '0X' trim in case of base == 16 or base == -1
+ * 2) '0b' or '0B' trim in case of base == 2 or base == -1
+ * 3) '-' for negative numbers
+ * 4) LL, ULL, LLU - trim, but only for base == 2 or
+ * base == 16 or base == -1. For consistency do not bother
+ * with any non-common bases, since user may have specified
+ * base >= 22, in which case 'L' will be a digit.
+ */
+ char negative = 0;
+ if (arg[0] == '-') {
+ arg++;
+ argl--;
+ negative = 1;
+ }
+ if (argl > 2 && arg[0] == '0') {
+ if ((arg[1] == 'x' || arg[1] == 'X') &&
+ (base == 16 || base == -1)) {
+ base = 16; arg += 2; argl -= 2;
+ } else if ((arg[1] == 'b' || arg[1] == 'B') &&
+ (base == 2 || base == -1)) {
+ base = 2; arg += 2; argl -= 2;
+ }
+ }
+ int ull = 0;
+ if (argl > 2 && (base == 2 || base == 16 || base == -1)) {
+ if (arg[argl - 1] == 'u' || arg[argl - 1] == 'U') {
+ ull = 1;
+ --argl;
+ }
+ if ((arg[argl - 1] == 'l' || arg[argl - 1] == 'L') &&
+ (arg[argl - 2] == 'l' || arg[argl - 2] == 'L')) {
+ argl -= 2;
+ if (ull == 0 && (arg[argl - 1] == 'u' || arg[argl - 1] == 'U')) {
+ ull = 1;
+ --argl;
+ }
+ } else {
+ ull = 0;
+ }
+ }
+ base = (base == -1 ? 10 : base);
+ errno = 0;
+ char *arge;
+ unsigned long long result = strtoull(arg, &arge, base);
+ if (errno == 0 && arge == arg + argl) {
+ if (argl == 0) {
+ lua_pushnil(L);
+ } else if (negative) {
+ if (ull == 0 && result != 0 && result - 1 > INT64_MAX) {
+ lua_pushnil(L);
+ return 1;
+ }
+ cts = ctype_cts(L);
+ /*
+ * To test overflow, consider
+ * result > -INT64_MIN;
+ * result - 1 > -INT64_MIN - 1;
+ * Assumption:
+ * INT64_MAX == -(INT64_MIN + 1);
+ * Finally,
+ * result - 1 > INT64_MAX;
+ */
+ int64_t val_i;
+ uint64_t val_u;
+ if (ull != 0) {
+ val_u = (UINT64_MAX - result) + 1;
+ ctypeid = CTID_UINT64;
+ } else {
+ val_i = -result;
+ ctypeid = CTID_INT64;
+ }
+
+ lj_ctype_info(cts, ctypeid, &size);
+ assert(size != CTSIZE_INVALID);
+
+ cd = lj_cdata_new(cts, ctypeid, size);
+ TValue *o = L->top;
+ setcdataV(L, o, cd);
+ incr_top(L);
+ if (ull != 0)
+ *(uint64_t *) cdataptr(cd) = val_u;
+ else
+ *(int64_t *) cdataptr(cd) = val_i;
+ } else {
+ cts = ctype_cts(L);
+ uint64_t val_u = result;
+ ctypeid = CTID_UINT64;
+ lj_ctype_info(cts, ctypeid, &size);
+ assert(size != CTSIZE_INVALID);
+
+ cd = lj_cdata_new(cts, ctypeid, size);
+ TValue *o = L->top;
+ setcdataV(L, o, cd);
+ incr_top(L);
+ *(uint64_t *) cdataptr(cd) = val_u;
+ }
+ return 1;
+ }
+ break;
+ }
+ case LUA_TCDATA:
+ base = (base == -1 ? 10 : base);
+ if (base != 10)
+ return luaL_argerror(L, 1, "string expected");
+ cd = cdataV(L->base);
+ ctypeid = cd->ctypeid;
+ if (ctypeid >= CTID_INT8 && ctypeid <= CTID_DOUBLE) {
+ lua_pushvalue(L, 1);
+ return 1;
+ }
+ break;
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
/* ------------------------------------------------------------------------ */
#include "lj_libdef.h"
diff --git a/test/gh-4738-incorrect-result-tonumber64.test.lua b/test/gh-4738-incorrect-result-tonumber64.test.lua
new file mode 100755
index 0000000..1d6da4a
--- /dev/null
+++ b/test/gh-4738-incorrect-result-tonumber64.test.lua
@@ -0,0 +1,38 @@
+#!/usr/bin/env tarantool
+
+-- Miscellaneous test for LuaJIT bugs
+local tap = require('tap')
+local misc = require('misc')
+
+local test = tap.test("gh-4738-incorrect-result-tonumber64")
+test:plan(24)
+--
+-- gh-4738: Make sure that tonumber64 always returns cdata.
+--
+test:ok(misc.tonumber64(1) == 1)
+test:ok(misc.tonumber64(-1) == -1)
+test:ok(misc.tonumber64(1.5) == 1)
+test:ok(misc.tonumber64(-1.5) == -1)
+test:ok(misc.tonumber64(1LL) == 1)
+test:ok(misc.tonumber64(1ULL) == 1)
+test:ok(misc.tonumber64(1LLU) == 1)
+test:ok(misc.tonumber64(-1ULL) == 18446744073709551615ULL)
+test:ok(misc.tonumber64('1') == 1)
+test:ok(misc.tonumber64('1LL') == 1)
+test:ok(misc.tonumber64('1ULL') == 1)
+test:ok(misc.tonumber64('-1ULL') == 18446744073709551615ULL)
+
+test:is(type(misc.tonumber64(1)), 'cdata')
+test:is(type(misc.tonumber64(-1)), 'cdata')
+test:is(type(misc.tonumber64(1.5)), 'cdata')
+test:is(type(misc.tonumber64(-1.5)), 'cdata')
+test:is(type(misc.tonumber64(1LL)), 'cdata')
+test:is(type(misc.tonumber64(1ULL)), 'cdata')
+test:is(type(misc.tonumber64(1LLU)), 'cdata')
+test:is(type(misc.tonumber64(-1ULL)), 'cdata')
+test:is(type(misc.tonumber64('1')), 'cdata')
+test:is(type(misc.tonumber64('1LL')), 'cdata')
+test:is(type(misc.tonumber64('1ULL')), 'cdata')
+test:is(type(misc.tonumber64('-1ULL')), 'cdata')
+
+os.exit(test:check() and 0 or 1)
--
2.25.1
More information about the Tarantool-patches
mailing list