Lua binding completed(tested)

This commit is contained in:
lionsoul 2018-10-03 20:05:14 +08:00
parent 5f073d814e
commit 86ad8057aa
5 changed files with 183 additions and 40 deletions

View File

@ -18,7 +18,7 @@ ip2region - 最自由的ip地址查询库ip到地区的映射库提供Bina
### 4. 多查询客户端的支持0.0x毫秒级别的查询 ### 4. 多查询客户端的支持0.0x毫秒级别的查询
已经集成的客户端有java, C#, php, c, pythonnodejsphp扩展(支持linux, php5, php7版本已支持)golang 已经集成的客户端有java、C#、php、c、python、nodejs、php扩展(php5和php7)、golang、rust、lua
提供了两种查询算法,响应时间如下: 提供了两种查询算法,响应时间如下:
客户端/binary算法/b-tree算法/Memory算法 客户端/binary算法/b-tree算法/Memory算法
@ -27,6 +27,7 @@ ip2region - 最自由的ip地址查询库ip到地区的映射库提供Bina
php/0.x毫秒/0.1x毫秒/0.1x毫秒 php/0.x毫秒/0.1x毫秒/0.1x毫秒
c/0.0x毫秒/0.0x毫秒/0.00x毫秒(b-tree算法基本稳定在0.02x毫秒级别) c/0.0x毫秒/0.0x毫秒/0.00x毫秒(b-tree算法基本稳定在0.02x毫秒级别)
python/0.x毫秒/0.1x毫秒/未知 python/0.x毫秒/0.1x毫秒/未知
lua/0.x毫秒/0.x毫秒/0.x毫秒
任何客户端b-tree都比binary算法快当然Memory算法固然是最快的 任何客户端b-tree都比binary算法快当然Memory算法固然是最快的
@ -70,6 +71,12 @@ python:
python binding/python/testSearcher.py ./data/ip2region.db python binding/python/testSearcher.py ./data/ip2region.db
``` ```
lua:
```shell
cd binding/lua/
lua testSearcher.lua ../../data/ip2region.db
```
均会看到如下界面: 均会看到如下界面:
```shell ```shell
initializing B-tree ... initializing B-tree ...

View File

@ -1,5 +1,5 @@
--[[ --[[
Ip2region lua binding _M lua binding
@author chenxin<chenxin619315@gmail.com> @author chenxin<chenxin619315@gmail.com>
@date 2018/10/02 @date 2018/10/02
@ -7,23 +7,22 @@ Ip2region lua binding
require("bit32"); require("bit32");
local Ip2region = { local INDEX_BLOCK_LENGTH = 12;
local TOTAL_HEADER_LENGTH = 8192;
local _M = {
dbFile = "", dbFile = "",
dbFileHandler = "", dbFileHandler = nil,
dbBinStr = "", dbBinStr = "",
HeaderSip = "", HeaderSip = nil,
HeaderPtr = "", HeaderPtr = nil,
headerLen = "", headerLen = 0,
firstIndexPtr = 0, firstIndexPtr = 0,
lastIndexPtr = 0, lastIndexPtr = 0,
totalBlocks = 0 totalBlocks = 0
}; };
_G["Ip2region"] = _M;
-- common constants function _M:new(obj)
local INDEX_BLOCK_LENGTH = 12;
local TOTAL_HEADER_LENGTH = 8192;
function Ip2region:new(obj)
obj = obj or {}; obj = obj or {};
setmetatable(obj, {__index = self}); setmetatable(obj, {__index = self});
return obj; return obj;
@ -58,27 +57,37 @@ internal function to convert the string ip to a long value
@param ip @param ip
@return Integer @return Integer
]]-- ]]--
function ip2long(ip) function _M:ip2long(ip)
local ini = 1; local ini = 1;
local iip = 0; local iip = 0;
local off = 24; local off = 24;
local int = 0;
while true do while true do
local pos = string.find(ip, '.', ini, true); local pos = string.find(ip, '.', ini, true);
if ( not pos or off <= 0 ) then if ( not pos ) then
break; break;
end end
int = tonumber(string.sub(ip, ini, pos - 1)); local sub = string.sub(ip, ini, pos - 1);
iip = bit32.bor(iip, bit32.lshift(int, off)); if ( string.len(sub) < 1 ) then
return nil;
end
iip = bit32.bor(iip, bit32.lshift(tonumber(sub), off));
ini = pos + 1; ini = pos + 1;
off = off - 8; off = off - 8;
end end
int = tonumber(string.sub(ip, ini)); -- check if it is a valid ip address
iip = bit32.bor(iip, bit32.lshift(int, off)); if ( off ~= 0 or ini > string.len(ip) ) then
return nil;
end
return iip; local sub = string.sub(ip, ini);
if ( string.len(sub) < 1 ) then
return nil;
end
return bit32.bor(iip, bit32.lshift(tonumber(sub), off));
end end
@ -108,7 +117,15 @@ then search the memory only and this will a lot faster than disk base search
@param ip @param ip
@return table or nil for failed @return table or nil for failed
]]-- ]]--
function Ip2region:memorySearch(ip) function _M:memorySearch(ip)
-- string ip conversion
if ( type(ip) == "string" ) then
ip = self:ip2long(ip);
if ( ip == nil ) then
return nil;
end
end;
-- check and load the binary string for the first time -- check and load the binary string for the first time
if ( self.dbBinStr == "" ) then if ( self.dbBinStr == "" ) then
self.dbBinStr = get_file_contents(self.dbFile); self.dbBinStr = get_file_contents(self.dbFile);
@ -121,9 +138,6 @@ function Ip2region:memorySearch(ip)
self.totalBlocks = (self.lastIndexPtr - self.firstIndexPtr)/INDEX_BLOCK_LENGTH + 1; self.totalBlocks = (self.lastIndexPtr - self.firstIndexPtr)/INDEX_BLOCK_LENGTH + 1;
end end
if ( type(ip) == "string" ) then
ip = ip2long(ip);
end;
-- binary search to define the data -- binary search to define the data
local l = 0; local l = 0;
@ -168,15 +182,18 @@ or long ip numeric with binary search algorithm
@param ip @param ip
@return table or nil for failed @return table or nil for failed
]]-- ]]--
function Ip2region:binarySearch(ip) function _M:binarySearch(ip)
-- check and conver the ip address -- check and conver the ip address
if ( type(ip) == "string" ) then if ( type(ip) == "string" ) then
ip = ip2long(ip); ip = self:ip2long(ip);
if ( ip == nil ) then
return nil;
end
end end
if ( self.totalBlocks == 0 ) then if ( self.totalBlocks == 0 ) then
-- check and open the original db file -- check and open the original db file
if ( self.dbFileHandler == "" ) then if ( self.dbFileHandler == nil ) then
self.dbFileHandler = io.open(self.dbFile, "r"); self.dbFileHandler = io.open(self.dbFile, "r");
if ( not self.dbFileHandler ) then if ( not self.dbFileHandler ) then
return nil; return nil;
@ -239,15 +256,19 @@ get the data block associated with the specified ip with b-tree search algorithm
@param ip @param ip
@return table or nil for failed @return table or nil for failed
]]-- ]]--
function Ip2region:btreeSearch(ip) function _M:btreeSearch(ip)
if ( type(ip) ) then -- string ip to integer conversion
ip = ip2long(ip); if ( type(ip) == "string" ) then
ip = self:ip2long(ip);
if ( ip == nil ) then
return nil;
end
end end
-- check and load the header -- check and load the header
if ( self.HeaderSip == "" ) then if ( self.headerLen == 0 ) then
-- check and open the original db file -- check and open the original db file
if ( self.dbFileHandler == "" ) then if ( self.dbFileHandler == nil ) then
self.dbFileHandler = io.open(self.dbFile, 'r'); self.dbFileHandler = io.open(self.dbFile, 'r');
if ( not self.dbFileHandler ) then if ( not self.dbFileHandler ) then
return nil; return nil;
@ -277,6 +298,7 @@ function Ip2region:btreeSearch(ip)
self.headerLen = idx; self.headerLen = idx;
end end
-- 1. define the index block with the binary search -- 1. define the index block with the binary search
local l = 0; local l = 0;
local h = self.headerLen; local h = self.headerLen;
@ -368,4 +390,4 @@ function Ip2region:btreeSearch(ip)
end end
return Ip2region; return _M;

49
binding/lua/README.md Normal file
View File

@ -0,0 +1,49 @@
# Lua ip2region binding
### 一,如何测试
* 1, cd到ip2region/binding/lua/根目录
* 2, 运行testSearcher测试程序
```shell
lua testSearcher.lua ../../data/ip2region.db
```
* 3, 输入ip地址开始测试即可
```shell
initializing btree
+----------------------------------+
| ip2region test script |
| Author: chenxin619315@gmail.com |
| Type 'quit' to exit program |
+----------------------------------+
ip2region>> 1.2.3.4
0|美国|0|华盛顿|0|0 in 8.001000 millseconds
ip2region>> 101.233.153.103
2163|中国|0|广东省|深圳市|鹏博士 in 0.176000 millseconds
ip2region>>
```
### 二,如何使用
* 1, 将Ip2region.lua拷贝到你的package.path下
* 2, 通过如下流程在你的lua程序中使用
```lua
-- 包含模块
require "Ip2region";
-- 创建查询对象
-- dbFile表示ip2region.db数据库文件的地址
local searcher = Ip2region:new({dbFile: "ip2region.db file path"});
local data;
-- 查询,备注请使用“:”调用方法,不要使用“.”
-- 1binary查询
data = searcher:binarySearch("101.233.153.103");
-- 2btree查询
data = searcher:btreeSearch("101.233.153.103");
-- 3memory查询
data = searcher:memorySearch("101.233.153.103");
-- 返回结果如下
print("city_id=", data.city_id, "region=", data.region);
```

View File

@ -1,9 +1,74 @@
-- ip2region test lua script --[[
local cjson = require "cjson"; ip2region lua client test script
local Ip2region = require "Ip2region";
@author chenxin<chenxin619315@gmail.com>
]]--
-- check the command line arguments
if ( not arg[1] ) then
print([[
Usage: lua testSearcher.lua [ip2region db file] [algorithm]
+-Optional Algorithm: binary, b-tree, memory]]);
os.exit();
end
local Ip2region = require "Ip2region";
-- local cjson = require "cjson";
-- local socket = require "socket";
-- check and parse the dbFile and the method algorithm
local searcher = Ip2region:new({dbFile = arg[1]});
local algorithm = "btree";
if ( arg[2] ~= nil ) then
local arg_2 = string.lower(arg[2]);
if ( arg_2 ~= "binary" and arg_2 ~= "memory" ) then
algorithm = "binary";
elseif ( arg_2 == "memory" ) then
algorithm = "memory";
end
end
local searcher = Ip2region:new({dbFile = "../../data/ip2region.db"});
-- local data = searcher:memorySearch("120.79.17.142"); -- local data = searcher:memorySearch("120.79.17.142");
-- local data = searcher:binarySearch("120.79.17.142"); -- local data = searcher:binarySearch("120.79.17.142");
local data = searcher:btreeSearch("120.79.17.142"); -- local data = searcher:btreeSearch("120.79.17.142");
print(cjson.encode(data)); print("initializing " .. algorithm ..[[
+----------------------------------+
| ip2region test script |
| Author: chenxin619315@gmail.com |
| Type 'quit' to exit program |
+----------------------------------+]]
);
while ( true ) do
io.write("ip2region>> ");
io.input(io.stdin);
local line = io.read();
if ( line == nil ) then
-- do nothing
break;
elseif ( line == "quit" ) then
break;
elseif ( searcher:ip2long(line) == nil ) then
print("Invalid ip address=", line);
else
local data;
local s_time = os.clock();
if ( algorithm == "btree" ) then
data = searcher:btreeSearch(line);
elseif ( algorithm == "binary" ) then
data = searcher:binarySearch(line);
elseif ( algorithm == "memory" ) then
data = searcher:memorySearch(line);
end
local cost_time = (os.clock() - s_time) * 1000; -- to millseconds
if ( data == nil ) then
io.write("Failed for ip=", line, " is it a valid ip address ?");
else
io.write(string.format("%u|%s in %5f millseconds\n", data.city_id, data.region, cost_time));
end
end
end

View File

@ -8,7 +8,7 @@
if ( $argc < 2 ) { if ( $argc < 2 ) {
$usage = <<<EOF $usage = <<<EOF
Usage: php Test.php [ip2region db file] [alrogrithm] Usage: php Test.php [ip2region db file] [alrogrithm]
+-Algorithm: binary or b-tree\n +-Optional Algorithm: binary, b-tree, memory\n
EOF; EOF;
exit($usage); exit($usage);
} }