mirror of
https://github.com/lionsoul2014/ip2region.git
synced 2025-12-08 19:25:22 +00:00
188 lines
4.1 KiB
Lua
188 lines
4.1 KiB
Lua
--[[
|
|
Ip2region lua binding
|
|
|
|
@author chenxin<chenxin619315@gmail.com>
|
|
@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;
|