From 932a5d80b47f27d76105151fc178ecd05a6e443e Mon Sep 17 00:00:00 2001 From: lionsoul Date: Tue, 2 Oct 2018 22:36:57 +0800 Subject: [PATCH] lua binding basic framework with memory search completed(tested) --- binding/lua/Ip2region.lua | 187 ++++++++++++++++++++++++++++++++ binding/lua/testSearch.lua | 7 ++ binding/php/Ip2Region.class.php | 1 - 3 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 binding/lua/Ip2region.lua create mode 100644 binding/lua/testSearch.lua diff --git a/binding/lua/Ip2region.lua b/binding/lua/Ip2region.lua new file mode 100644 index 0000000..66014a6 --- /dev/null +++ b/binding/lua/Ip2region.lua @@ -0,0 +1,187 @@ +--[[ +Ip2region lua binding + +@author chenxin +@date 2018/10/02 +]]-- + +require("bit32"); + +local Ip2region = { + dbFileHandler = "", + HeaderSip = "", + HeaderPtr = "", + headerLen = "", + firstIndexPtr = "", + lastIndexPtr = "", + totalBlocks = "", + dbBinStr = "", + dbFile = "" +}; + + + +--[[ +internal function to get a integer from a binary string + +@param dbBinStr +@param idx +@return Integer +]]-- +function getLong(bs, idx) + local a1 = string.byte(string.sub(bs, idx, idx)); + local a2 = bit32.lshift(string.byte(string.sub(bs, idx+1, idx+1)), 8); + local a3 = bit32.lshift(string.byte(string.sub(bs, idx+2, idx+2)), 16); + local a4 = bit32.lshift(string.byte(string.sub(bs, idx+3, idx+3)), 24); + + local val = bit32.bor(a1, a2); + val = bit32.bor(val, a3); + val = bit32.bor(val, a4); + + return val; +end + + +--[[ +internal function to convert the string ip to a long value + +@param ip +@return Integer +]]-- +function 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 + break; + end + + int = tonumber(string.sub(ip, ini, pos - 1)); + iip = bit32.bor(iip, bit32.lshift(int, off)); + ini = pos + 1; + off = off - 8; + end + + int = tonumber(string.sub(ip, ini)); + iip = bit32.bor(iip, bit32.lshift(int, off)); + + return iip; +end + + +--[[ +internal function to get the whole content of a file + +@param file +@return String +]]-- +function get_file_contents(file) + local fi = io.input(file); + if ( not fi ) then + return nil; + end + + local str = io.read("*a"); + io.close(); + return str; +end + + + + +-- common constants +local INDEX_BLOCK_LENGTH = 12; +local TOTAL_HEADER_LENGTH = 8192; + +function Ip2region:new(obj) + obj = obj or {}; + setmetatable(obj, {__index = self}); + return obj; +end + +--[[ +all the db binary string will be loaded into memory +then search the memory only and this will a lot faster than disk base search +@Note: invoke it once before put it to public invoke could make it thread safe + +@param ip +@return table or nil for failed +]]-- +function Ip2region:memorySearch(ip) + -- check and load the binary string for the first time + if ( self.dbBinStr == "" ) then + self.dbBinStr = get_file_contents(self.dbFile); + if ( not self.dbBinStr ) then + return nil; + end + + self.firstIndexPtr = getLong(self.dbBinStr, 1); + self.lastIndexPtr = getLong(self.dbBinStr, 5); + 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; + local h = self.totalBlocks; + local dataPtr = 0; + while ( l <= h ) do + local m = math.floor((l + h) / 2); + local p = self.firstIndexPtr + m * INDEX_BLOCK_LENGTH; + local sip = getLong(self.dbBinStr, p + 1); + if ( ip < sip ) then + h = m - 1; + else + local eip = getLong(self.dbBinStr, p + 5); -- 4 + 1 + if ( ip > eip ) then + l = m + 1; + else + dataPtr = getLong(self.dbBinStr, p + 9); -- 8 + 1 + break; + end + end + end + + -- not matched just stop it here + if ( dataPtr == 0 ) then return nil end + + -- get the data + local dataLen = bit32.band(bit32.rshift(dataPtr, 24), 0xFF); + dataPtr = bit32.band(dataPtr, 0x00FFFFFF); + local dptr = dataPtr + 5; -- 4 + 1 + + return { + city_id = getLong(self.dbBinStr, dataPtr), + region = string.sub(self.dbBinStr, dptr, dptr + dataLen - 5) + }; +end + + +--[[ +get the data block through the specified ip address +or long ip numeric with binary search algorithm + +@param ip +@return table or nil for failed +]]-- +function Ip2region:binarySearch(ip) +end + + +--[[ +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) +end + + +return Ip2region; diff --git a/binding/lua/testSearch.lua b/binding/lua/testSearch.lua new file mode 100644 index 0000000..140c854 --- /dev/null +++ b/binding/lua/testSearch.lua @@ -0,0 +1,7 @@ +-- ip2region test lua script +local cjson = require "cjson"; +local Ip2region = require "Ip2region"; + +local searcher = Ip2region:new({dbFile = "../../data/ip2region.db"}); +local data = searcher:memorySearch("120.79.17.142"); +print(cjson.encode(data)); diff --git a/binding/php/Ip2Region.class.php b/binding/php/Ip2Region.class.php index f15dc08..12bc0e0 100644 --- a/binding/php/Ip2Region.class.php +++ b/binding/php/Ip2Region.class.php @@ -358,4 +358,3 @@ class Ip2Region $this->HeaderPtr = NULL; } } -?>