diff --git a/.gitignore b/.gitignore index a167fe30935c2b05e1e38545698a5050722982e3..ce243fab7ff9c66c4b3d3a2d3518d0f41d72446f 100644 --- a/.gitignore +++ b/.gitignore @@ -37,10 +37,14 @@ META-INF/ /binding/java/target/ /binding/java/*.jar +# clang /binding/c/xdb_searcher /binding/c/util_test /binding/c/cmake-build-debug +# lua/luc_c +/binding/lua_c/cmake-build-debug + # golang /binding/golang/searcher /binding/golang/xdb_searcher diff --git a/binding/lua_c/Makefile b/binding/lua_c/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..cd0f1ffa260a2196e7bb6268567deca069d7ee7a --- /dev/null +++ b/binding/lua_c/Makefile @@ -0,0 +1,16 @@ +LuaVersion = 5.3 +LIB_DIR = /usr/local/share/lua/$(LuaVersion) + +all: ../c/xdb_searcher.h ../c/xdb_searcher.c xdb_searcher.c + gcc -g -O2 -I../c/ -I/usr/include/lua$(LuaVersion) ../c/xdb_searcher.c xdb_searcher.c -fPIC -shared -o xdb_searcher.so + +install: + sudo mkdir -p $(LIB_DIR); \ + sudo cp xdb_searcher.so $(LIB_DIR);\ + echo "install xdb searcher to $(LIB_DIR) successfully.";\ + +clean: + find . -name \*.so | xargs rm -f + find . -name \*.o | xargs rm -f + +.PHONY: clean \ No newline at end of file diff --git a/binding/lua_c/bench_test.lua b/binding/lua_c/bench_test.lua new file mode 100644 index 0000000000000000000000000000000000000000..1c02d11da402a1ddb03aa6412d948efefc4d8481 --- /dev/null +++ b/binding/lua_c/bench_test.lua @@ -0,0 +1,7 @@ +-- Copyright 2022 The Ip2Region Authors. All rights reserved. +-- Use of this source code is governed by a Apache2.0-style +-- license that can be found in the LICENSE file. +-- +-- --- +-- @Author Lion +-- @Date 2022/06/30 diff --git a/binding/lua_c/search_test.lua b/binding/lua_c/search_test.lua new file mode 100644 index 0000000000000000000000000000000000000000..1c02d11da402a1ddb03aa6412d948efefc4d8481 --- /dev/null +++ b/binding/lua_c/search_test.lua @@ -0,0 +1,7 @@ +-- Copyright 2022 The Ip2Region Authors. All rights reserved. +-- Use of this source code is governed by a Apache2.0-style +-- license that can be found in the LICENSE file. +-- +-- --- +-- @Author Lion +-- @Date 2022/06/30 diff --git a/binding/lua_c/util_test.lua b/binding/lua_c/util_test.lua new file mode 100644 index 0000000000000000000000000000000000000000..174845380300bb7e62641bc4d81212f86787ff26 --- /dev/null +++ b/binding/lua_c/util_test.lua @@ -0,0 +1,84 @@ +-- Copyright 2022 The Ip2Region Authors. All rights reserved. +-- Use of this source code is governed by a Apache2.0-style +-- license that can be found in the LICENSE file. +-- +-- --- +-- @Author Lion +-- @Date 2022/06/30 + +local xdb = require("xdb_searcher") + +---- ip checking testing +print("--- testing check_ip and long2ip ... ") +local ip_list = { + "1.2.3.4", "192.168.2.3", "120.24.78.129", "255.255.255.0", + "256.7.12.9", "12.56.78.320", "32.12.45.192", "222.221.220.219", + "192.168.1.101 ", "132.96.12.98a", "x23.12.2.12" +} + +local s_time = xdb.now() +for _, ip_src in ipairs(ip_list) do + ip, ok = xdb.check_ip(ip_src) + if ok == false then + print(string.format("invalid ip address `%s`", ip_src)) + else + ip_dst = xdb.long2ip(ip) + io.write(string.format("long(%-15s)=%10d, long2ip(%-10d)=%-15s", ip_src, ip, ip, ip_dst)) + if ip_src ~= ip_dst then + print(" --[Failed]") + else + print(" --[Ok]") + end + end +end + +---- buffer loading test +print("\n--- testing load header ... ") +header, err = xdb.load_header("../../data/ip2region.xdb") +if err ~= nil then + print("failed to load header: ", err) +else + local tpl = [[ +header: { + version: %d + index_policy: %d + created_at: %d + start_index_ptr: %d + end_index_ptr: %d +}]] + + print(string.format(tpl, + header["version"], header["index_policy"], + header["created_at"], header["start_index_ptr"], header["end_index_ptr"]) + ) +end + + +print("\n--- testing load vector index ... ") +v_index, err = xdb.load_vector_index("../../data/ip2region.xdb") +if err ~= nil then + print("failed to load vector index: ", err) +else + print("xdb vector index loaded") +end + + +print("\n--- testing load content buffer ... ") +c_buffer, err = xdb.load_content("../../data/ip2region.xdb") +if err ~= nil then + print("failed to load content: ", err) +else + print("xdb content loaded") +end + +print("\n--- testing search ... ") +local ip_str = "1.2.3.4" +searcher, err = xdb.new_with_file_only("../../data/ip2region.xdb") +local t_start = xdb.now() +region, err = searcher:search(ip_str) +print(string.format("search(%s): {region=%s, io_count: %d, took: %dμs, err=%s}", + ip_str, region, searcher:get_io_count(), xdb.now() - t_start, err)) + + +print("") +print(string.format("all tests done, elapsed %d μs", xdb.now() - s_time)) \ No newline at end of file diff --git a/binding/lua_c/xdb_searcher.c b/binding/lua_c/xdb_searcher.c new file mode 100644 index 0000000000000000000000000000000000000000..2e27d5ca2a265f404519b9d1f7488dc878005e22 --- /dev/null +++ b/binding/lua_c/xdb_searcher.c @@ -0,0 +1,370 @@ +// Copyright 2022 The Ip2Region Authors. All rights reserved. +// Use of this source code is governed by a Apache2.0-style +// license that can be found in the LICENSE file. + +// --- +// @Author Lion +// @Date 2022/06/30 + + +#include "stdio.h" +#include "lua.h" +#include "lauxlib.h" +#include "../c/xdb_searcher.h" + +#define XDB_METATABLE_NAME "xdb_searcher_mt" + +static int lua_xdb_new_with_file_only(lua_State *L) { + int err; + xdb_searcher_t *searcher; + const char *db_path = NULL; + + // check the db path + db_path = luaL_checkstring(L, 1); + if (db_path == NULL) { + return luaL_error(L, "xdb file path expected"); + } + + searcher = (xdb_searcher_t *) lua_newuserdata(L, sizeof(xdb_searcher_t)); + if (searcher == NULL) { + return luaL_error(L, "failed to alloc xdb searcher entry"); + } + + // init the xdb searcher + err = xdb_new_with_file_only(searcher, db_path); + if (err != 0) { + return luaL_error(L, "failed to init xdb searcher on `%s`: errcode=%d", db_path, err); + } + + // push the metatable onto the stack and + // set it as the metatable of the current searcher + luaL_getmetatable(L, XDB_METATABLE_NAME); + lua_setmetatable(L, -2); + + return 1; +} + +static int lua_xdb_new_with_vector_index(lua_State *L) { + xdb_searcher_t *searcher; + const char *v_index; + const char *db_path; + int err, top_len; + + top_len = lua_gettop(L); + luaL_argcheck(L, top_len >= 2, 1, "xdb file path and vector index buffer expected"); + + // check the db path and vector index buffer + db_path = luaL_checkstring(L, 1); + if (db_path == NULL) { + return luaL_error(L, "ip2region.xdb file path expected"); + } + + v_index = luaL_checkstring(L, 2); + if (v_index == NULL) { + return luaL_error(L, "vector index buffer expected"); + } + + // alloc the searcher + searcher = (xdb_searcher_t *) lua_newuserdata(L, sizeof(xdb_searcher_t)); + if (searcher == NULL) { + return luaL_error(L, "failed to alloc xdb searcher entry"); + } + + // init the xdb searcher + err = xdb_new_with_vector_index(searcher, db_path, v_index); + if (err != 0) { + return luaL_error(L, "failed to init vector index cached xdb searcher on `%s` with errcode=%d", db_path, err); + } + + // push the metatable onto the stack and + // set it as the metatable of the current searcher + luaL_getmetatable(L, XDB_METATABLE_NAME); + lua_setmetatable(L, -2); + + return 1; +} + +static int lua_xdb_new_with_buffer(lua_State *L) { + xdb_searcher_t *searcher; + const char *c_buffer; + int err; + + c_buffer = luaL_checkstring(L, 1); + if (c_buffer == NULL) { + return luaL_error(L, "xdb content buffer expected"); + } + + // alloc the searcher + searcher = (xdb_searcher_t *) lua_newuserdata(L, sizeof(xdb_searcher_t)); + if (searcher == NULL) { + return luaL_error(L, "failed to alloc xdb searcher entry"); + } + + // init the xdb searcher + err = xdb_new_with_buffer(searcher, c_buffer); + if (err != 0) { + return luaL_error(L, "failed to init content cached xdb searcher with errcode=%d", err); + } + + // push the metatable onto the stack and + // set it as the metatable of the current searcher + luaL_getmetatable(L, XDB_METATABLE_NAME); + lua_setmetatable(L, -2); + + return 1; +} + +static int lua_xdb_close(lua_State *L) { + xdb_searcher_t *searcher; + + searcher = (xdb_searcher_t *) luaL_checkudata(L, 1, XDB_METATABLE_NAME); + if (searcher == NULL) { + return luaL_error(L, "broken xdb searcher instance"); + } + + xdb_close(searcher); + + return 0; +} + +static int lua_xdb_search(lua_State *L) { + int err; + const char *ip_str; + unsigned int ip_int; + char region_buffer[1024] = {'\0'}; + xdb_searcher_t *searcher; + + int top_len = lua_gettop(L); + luaL_argcheck(L, top_len >= 2, 2, "string or long ip address expected"); + + // get the searcher + searcher = (xdb_searcher_t *) luaL_checkudata(L, 1, XDB_METATABLE_NAME); + if (searcher == NULL) { + return luaL_error(L, "call via ':' or broken xdb searcher instance"); + } + + // input ip type checking + if (lua_isinteger(L, 2)) { + ip_int = lua_tointeger(L, 2); + } else if (lua_isstring(L, 2)) { + ip_str = lua_tostring(L, 2); + err = xdb_check_ip(ip_str, &ip_int); + if (err != 0) { + luaL_error(L, "invalid string ip `%s`: errcode=%d", ip_str, err); + } + } else { + return luaL_error(L, "input ip not integer or string"); + } + + // do the search + err = xdb_search(searcher, ip_int, region_buffer, sizeof(region_buffer)); + if (err != 0) { + lua_pushinteger(L, err); + lua_pushfstring(L, "err=%d", err); + } else { + lua_pushstring(L, region_buffer); + lua_pushnil(L); + } + + return 2; +} + +static int lua_xdb_get_io_count(lua_State *L) { + xdb_searcher_t *searcher; + + searcher = (xdb_searcher_t *) luaL_checkudata(L, 1, XDB_METATABLE_NAME); + if (searcher == NULL) { + return luaL_error(L, "broken xdb searcher instance"); + } + + lua_pushinteger(L, xdb_get_io_count(searcher)); + return 1; +} + +static int lua_xdb_tostring(lua_State *L) { + lua_pushliteral(L, "xdb searcher object"); + return 1; +} + +// -- buffer util function + +static int lua_xdb_load_header_from_file(lua_State *L) { + int err; + const char *db_path; + xdb_header_t header; + + db_path = luaL_checkstring(L, 1); + if (db_path == NULL) { + return luaL_error(L, "xdb file path expected"); + } + + err = xdb_load_header_from_file(db_path, &header); + if (err != 0) { + lua_pushnil(L); + lua_pushfstring(L, "failed to load header from `%s` with errcode=%d", db_path, err); + return 2; + } + + // push the header table onto the stack as return + lua_newtable(L); + lua_pushinteger(L, header.version); + lua_setfield(L, -2, "version"); + + lua_pushinteger(L, header.index_policy); + lua_setfield(L, -2, "index_policy"); + + lua_pushinteger(L, header.created_at); + lua_setfield(L, -2, "created_at"); + + lua_pushinteger(L, header.start_index_ptr); + lua_setfield(L, -2, "start_index_ptr"); + + lua_pushinteger(L, header.end_index_ptr); + lua_setfield(L, -2, "end_index_ptr"); + + lua_pushnil(L); // empty error + return 2; +} + +static int lua_xdb_load_vector_index_from_file(lua_State *L) { + char *v_index; + const char *db_path; + + db_path = luaL_checkstring(L, 1); + if (db_path == NULL) { + return luaL_error(L, "xdb file path expected"); + } + + v_index = xdb_load_vector_index_from_file(db_path); + if (v_index == NULL) { + lua_pushnil(L); + lua_pushfstring(L, "load xdb vector index from `%s`", db_path); + return 2; + } + + // push the vector index onto the stack + lua_pushstring(L, v_index); + lua_pushnil(L); + + // free the local vector index allocation. + // the lua engine will manager the allocation in the VM + xdb_free(v_index); + v_index = NULL; + + return 2; +} + +static int lua_xdb_load_content_from_file(lua_State *L) { + char *c_buffer; + const char *db_path; + + db_path = luaL_checkstring(L, 1); + if (db_path == NULL) { + return luaL_error(L, "xdb file path expected"); + } + + c_buffer = xdb_load_content_from_file(db_path); + if (c_buffer == NULL) { + lua_pushnil(L); + lua_pushfstring(L, "load xdb content from `%s`", db_path); + return 2; + } + + // push the content buffer onto the stack + lua_pushstring(L, c_buffer); + lua_pushnil(L); + + // free the local content buffer allocation. + // let the lua VM manager it. + xdb_free(c_buffer); + c_buffer = NULL; + + return 2; +} + +// -- static util function + +static int lua_xdb_check_ip(lua_State *L) { + int err; + unsigned int ip; + const char *ip_str; + + ip_str = luaL_checkstring(L, 1); + if (ip_str == NULL) { + return luaL_error(L, "ip expected, eg: 1.2.3.4"); + } + + err = xdb_check_ip(ip_str, &ip); + if (err != 0) { + lua_pushinteger(L, err); + lua_pushboolean(L, 0); + } else { + lua_pushinteger(L, ip); + lua_pushboolean(L, 1); + } + + return 2; +} + +static int lua_xdb_long2ip(lua_State *L) { + unsigned int ip; + char ip_buff[16] = {'\0'}; + + ip = luaL_checkinteger(L, 1); + xdb_long2ip(ip, ip_buff); + lua_pushstring(L, ip_buff); + + return 1; +} + +static int lua_xdb_now(lua_State *L) { + lua_pushinteger(L, xdb_now()); + return 1; +} + + +// module method define, should be access via ':' +static const struct luaL_Reg xdb_searcher_methods[] = { + {"search", lua_xdb_search}, + {"get_io_count",lua_xdb_get_io_count}, + {"close", lua_xdb_close}, + {"__gc", lua_xdb_close}, + {"__tostring", lua_xdb_tostring}, + {NULL, NULL}, +}; + +// module function define, should be access via '.' +static const struct luaL_Reg xdb_searcher_functions[] = { + {"new_with_file_only", lua_xdb_new_with_file_only}, + {"new_with_vector_index", lua_xdb_new_with_vector_index}, + {"new_with_buffer", lua_xdb_new_with_buffer}, + {"load_header", lua_xdb_load_header_from_file}, + {"load_vector_index", lua_xdb_load_vector_index_from_file}, + {"load_content", lua_xdb_load_content_from_file}, + {"check_ip", lua_xdb_check_ip}, + {"long2ip", lua_xdb_long2ip}, + {"now", lua_xdb_now}, + {NULL, NULL} +}; + +// module register function +int luaopen_xdb_searcher(lua_State *L) +{ + // create a metatable and push it onto the stack + luaL_newmetatable(L, XDB_METATABLE_NAME); + + // Duplicate the metatable on the stack + lua_pushvalue(L, -1); + + // Pop the first metatable off the stack + // and assign it to the __index of the second one. + // so we set the metatable to the table itself. + lua_setfield(L, -2, "__index"); + + // Set the methods fo the metatable that could and should be + // access via object:func in lua block + luaL_setfuncs(L, xdb_searcher_methods, 0); + luaL_setfuncs(L, xdb_searcher_functions, 0); + + return 1; +} \ No newline at end of file