From 86ad8057aa72f677be80373b6c9982e8d5712645 Mon Sep 17 00:00:00 2001 From: lionsoul Date: Wed, 3 Oct 2018 20:05:14 +0800 Subject: [PATCH] Lua binding completed(tested) --- README.md | 9 +++- binding/lua/Ip2region.lua | 86 ++++++++++++++++++++++-------------- binding/lua/README.md | 49 ++++++++++++++++++++ binding/lua/testSearcher.lua | 77 +++++++++++++++++++++++++++++--- binding/php/testSearcher.php | 2 +- 5 files changed, 183 insertions(+), 40 deletions(-) create mode 100644 binding/lua/README.md diff --git a/README.md b/README.md index eeae92e..fbbbfc1 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ ip2region - 最自由的ip地址查询库,ip到地区的映射库,提供Bina ### 4. 多查询客户端的支持,0.0x毫秒级别的查询 -已经集成的客户端有:java, C#, php, c, python,nodejs,php扩展(支持linux, php5, php7版本已支持),golang。 +已经集成的客户端有:java、C#、php、c、python、nodejs、php扩展(php5和php7)、golang、rust、lua。 提供了两种查询算法,响应时间如下: 客户端/binary算法/b-tree算法/Memory算法: @@ -27,6 +27,7 @@ ip2region - 最自由的ip地址查询库,ip到地区的映射库,提供Bina php/0.x毫秒/0.1x毫秒/0.1x毫秒 c/0.0x毫秒/0.0x毫秒/0.00x毫秒(b-tree算法基本稳定在0.02x毫秒级别) python/0.x毫秒/0.1x毫秒/未知 + lua/0.x毫秒/0.x毫秒/0.x毫秒 任何客户端b-tree都比binary算法快,当然Memory算法固然是最快的! @@ -70,6 +71,12 @@ python: python binding/python/testSearcher.py ./data/ip2region.db ``` +lua: +```shell +cd binding/lua/ +lua testSearcher.lua ../../data/ip2region.db +``` + 均会看到如下界面: ```shell initializing B-tree ... diff --git a/binding/lua/Ip2region.lua b/binding/lua/Ip2region.lua index 5aea89f..05cbcc6 100644 --- a/binding/lua/Ip2region.lua +++ b/binding/lua/Ip2region.lua @@ -1,5 +1,5 @@ --[[ -Ip2region lua binding +_M lua binding @author chenxin @date 2018/10/02 @@ -7,23 +7,22 @@ Ip2region lua binding require("bit32"); -local Ip2region = { +local INDEX_BLOCK_LENGTH = 12; +local TOTAL_HEADER_LENGTH = 8192; +local _M = { dbFile = "", - dbFileHandler = "", + dbFileHandler = nil, dbBinStr = "", - HeaderSip = "", - HeaderPtr = "", - headerLen = "", + HeaderSip = nil, + HeaderPtr = nil, + headerLen = 0, firstIndexPtr = 0, lastIndexPtr = 0, totalBlocks = 0 }; - --- common constants -local INDEX_BLOCK_LENGTH = 12; -local TOTAL_HEADER_LENGTH = 8192; -function Ip2region:new(obj) +_G["Ip2region"] = _M; +function _M:new(obj) obj = obj or {}; setmetatable(obj, {__index = self}); return obj; @@ -58,27 +57,37 @@ internal function to convert the string ip to a long value @param ip @return Integer ]]-- -function ip2long(ip) +function _M:ip2long(ip) local ini = 1; local iip = 0; local off = 24; - local int = 0; while true do local pos = string.find(ip, '.', ini, true); - if ( not pos or off <= 0 ) then + if ( not pos ) then break; end - int = tonumber(string.sub(ip, ini, pos - 1)); - iip = bit32.bor(iip, bit32.lshift(int, off)); + local sub = string.sub(ip, ini, pos - 1); + if ( string.len(sub) < 1 ) then + return nil; + end + + iip = bit32.bor(iip, bit32.lshift(tonumber(sub), off)); ini = pos + 1; off = off - 8; end - int = tonumber(string.sub(ip, ini)); - iip = bit32.bor(iip, bit32.lshift(int, off)); + -- check if it is a valid ip address + 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 @@ -108,7 +117,15 @@ then search the memory only and this will a lot faster than disk base search @param ip @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 if ( self.dbBinStr == "" ) then 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; end - if ( type(ip) == "string" ) then - ip = ip2long(ip); - end; -- binary search to define the data local l = 0; @@ -168,15 +182,18 @@ or long ip numeric with binary search algorithm @param ip @return table or nil for failed ]]-- -function Ip2region:binarySearch(ip) +function _M:binarySearch(ip) -- check and conver the ip address if ( type(ip) == "string" ) then - ip = ip2long(ip); + ip = self:ip2long(ip); + if ( ip == nil ) then + return nil; + end end if ( self.totalBlocks == 0 ) then -- check and open the original db file - if ( self.dbFileHandler == "" ) then + if ( self.dbFileHandler == nil ) then self.dbFileHandler = io.open(self.dbFile, "r"); if ( not self.dbFileHandler ) then return nil; @@ -239,15 +256,19 @@ get the data block associated with the specified ip with b-tree search algorithm @param ip @return table or nil for failed ]]-- -function Ip2region:btreeSearch(ip) - if ( type(ip) ) then - ip = ip2long(ip); +function _M:btreeSearch(ip) + -- string ip to integer conversion + if ( type(ip) == "string" ) then + ip = self:ip2long(ip); + if ( ip == nil ) then + return nil; + end end -- check and load the header - if ( self.HeaderSip == "" ) then + if ( self.headerLen == 0 ) then -- check and open the original db file - if ( self.dbFileHandler == "" ) then + if ( self.dbFileHandler == nil ) then self.dbFileHandler = io.open(self.dbFile, 'r'); if ( not self.dbFileHandler ) then return nil; @@ -277,6 +298,7 @@ function Ip2region:btreeSearch(ip) self.headerLen = idx; end + -- 1. define the index block with the binary search local l = 0; local h = self.headerLen; @@ -368,4 +390,4 @@ function Ip2region:btreeSearch(ip) end -return Ip2region; +return _M; diff --git a/binding/lua/README.md b/binding/lua/README.md new file mode 100644 index 0000000..b5f8d2b --- /dev/null +++ b/binding/lua/README.md @@ -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; + +-- 查询,备注请使用“:”调用方法,不要使用“.” +-- 1,binary查询 +data = searcher:binarySearch("101.233.153.103"); + +-- 2,btree查询 +data = searcher:btreeSearch("101.233.153.103"); + +-- 3,memory查询 +data = searcher:memorySearch("101.233.153.103"); + +-- 返回结果如下 +print("city_id=", data.city_id, "region=", data.region); +``` diff --git a/binding/lua/testSearcher.lua b/binding/lua/testSearcher.lua index 8cb55f4..0dae8e7 100644 --- a/binding/lua/testSearcher.lua +++ b/binding/lua/testSearcher.lua @@ -1,9 +1,74 @@ --- ip2region test lua script -local cjson = require "cjson"; -local Ip2region = require "Ip2region"; +--[[ +ip2region lua client test script + +@author chenxin +]]-- + +-- 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:binarySearch("120.79.17.142"); -local data = searcher:btreeSearch("120.79.17.142"); -print(cjson.encode(data)); +-- local data = searcher:btreeSearch("120.79.17.142"); +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 diff --git a/binding/php/testSearcher.php b/binding/php/testSearcher.php index 6547f4f..7d63b88 100644 --- a/binding/php/testSearcher.php +++ b/binding/php/testSearcher.php @@ -8,7 +8,7 @@ if ( $argc < 2 ) { $usage = <<