lua binding forward with binarySearch and btreeSearch interface implemented
This commit is contained in:
parent
04f093d9bd
commit
5f073d814e
|
|
@ -8,18 +8,28 @@ Ip2region lua binding
|
|||
require("bit32");
|
||||
|
||||
local Ip2region = {
|
||||
dbFile = "",
|
||||
dbFileHandler = "",
|
||||
dbBinStr = "",
|
||||
HeaderSip = "",
|
||||
HeaderPtr = "",
|
||||
headerLen = "",
|
||||
firstIndexPtr = "",
|
||||
lastIndexPtr = "",
|
||||
totalBlocks = "",
|
||||
dbBinStr = "",
|
||||
dbFile = ""
|
||||
firstIndexPtr = 0,
|
||||
lastIndexPtr = 0,
|
||||
totalBlocks = 0
|
||||
};
|
||||
|
||||
|
||||
-- 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
|
||||
|
||||
|
||||
|
||||
--[[
|
||||
internal function to get a integer from a binary string
|
||||
|
|
@ -90,18 +100,6 @@ function get_file_contents(file)
|
|||
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
|
||||
|
|
@ -171,6 +169,67 @@ or long ip numeric with binary search algorithm
|
|||
@return table or nil for failed
|
||||
]]--
|
||||
function Ip2region:binarySearch(ip)
|
||||
-- check and conver the ip address
|
||||
if ( type(ip) == "string" ) then
|
||||
ip = ip2long(ip);
|
||||
end
|
||||
|
||||
if ( self.totalBlocks == 0 ) then
|
||||
-- check and open the original db file
|
||||
if ( self.dbFileHandler == "" ) then
|
||||
self.dbFileHandler = io.open(self.dbFile, "r");
|
||||
if ( not self.dbFileHandler ) then
|
||||
return nil;
|
||||
end
|
||||
end
|
||||
|
||||
self.dbFileHandler:seek("set", 0);
|
||||
local superBlock = self.dbFileHandler:read(8);
|
||||
|
||||
self.firstIndexPtr = getLong(superBlock, 1); -- 0 + 1
|
||||
self.lastIndexPtr = getLong(superBlock, 5); -- 4 + 1
|
||||
self.totalBlocks = (self.lastIndexPtr-self.firstIndexPtr)/INDEX_BLOCK_LENGTH + 1;
|
||||
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 = m * INDEX_BLOCK_LENGTH;
|
||||
self.dbFileHandler:seek("set", self.firstIndexPtr + p);
|
||||
local buffer = self.dbFileHandler:read(INDEX_BLOCK_LENGTH);
|
||||
local sip = getLong(buffer, 1); -- 0 + 1
|
||||
if ( ip < sip ) then
|
||||
h = m - 1;
|
||||
else
|
||||
local eip = getLong(buffer, 5); -- 4 + 1
|
||||
if ( ip > eip ) then
|
||||
l = m + 1;
|
||||
else
|
||||
dataPtr = getLong(buffer, 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);
|
||||
|
||||
self.dbFileHandler:seek("set", dataPtr);
|
||||
local data = self.dbFileHandler:read(dataLen);
|
||||
|
||||
return {
|
||||
city_id = getLong(data, 1), -- 0 + 1
|
||||
region = string.sub(data, 5) -- 4 + 1
|
||||
};
|
||||
end
|
||||
|
||||
|
||||
|
|
@ -181,6 +240,131 @@ get the data block associated with the specified ip with b-tree search algorithm
|
|||
@return table or nil for failed
|
||||
]]--
|
||||
function Ip2region:btreeSearch(ip)
|
||||
if ( type(ip) ) then
|
||||
ip = ip2long(ip);
|
||||
end
|
||||
|
||||
-- check and load the header
|
||||
if ( self.HeaderSip == "" ) then
|
||||
-- check and open the original db file
|
||||
if ( self.dbFileHandler == "" ) then
|
||||
self.dbFileHandler = io.open(self.dbFile, 'r');
|
||||
if ( not self.dbFileHandler ) then
|
||||
return nil;
|
||||
end
|
||||
end
|
||||
|
||||
self.dbFileHandler:seek("set", 8);
|
||||
local buffer = self.dbFileHandler:read(TOTAL_HEADER_LENGTH);
|
||||
|
||||
-- fill the header
|
||||
local i = 0;
|
||||
local idx = 0;
|
||||
self.HeaderSip = {};
|
||||
self.HeaderPtr = {};
|
||||
for i=0, TOTAL_HEADER_LENGTH, 8 do
|
||||
local startIp = getLong(buffer, i + 1); -- 0 + 1
|
||||
local dataPtr = getLong(buffer, i + 5); -- 4 + 1
|
||||
if ( dataPtr == 0 ) then
|
||||
break;
|
||||
end
|
||||
|
||||
table.insert(self.HeaderSip, startIp);
|
||||
table.insert(self.HeaderPtr, dataPtr);
|
||||
idx = idx + 1;
|
||||
end
|
||||
|
||||
self.headerLen = idx;
|
||||
end
|
||||
|
||||
-- 1. define the index block with the binary search
|
||||
local l = 0;
|
||||
local h = self.headerLen;
|
||||
local sptr = 0;
|
||||
local eptr = 0;
|
||||
while ( l <= h ) do
|
||||
local m = math.floor((l + h) / 2);
|
||||
-- perfetc matched, just return it
|
||||
if ( ip == self.HeaderSip[m] ) then
|
||||
if ( m > 0 ) then
|
||||
sptr = self.HeaderPtr[m-1];
|
||||
eptr = self.HeaderPtr[m ];
|
||||
else
|
||||
sptr = self.HeaderPtr[m ];
|
||||
eptr = self.HeaderPtr[m+1];
|
||||
end
|
||||
|
||||
break;
|
||||
end
|
||||
|
||||
-- less then the middle value
|
||||
if ( ip < self.HeaderSip[m] ) then
|
||||
if ( m == 0 ) then
|
||||
sptr = self.HeaderPtr[m ];
|
||||
eptr = self.HeaderPtr[m+1];
|
||||
break;
|
||||
elseif ( ip > self.HeaderSip[m-1] ) then
|
||||
sptr = self.HeaderPtr[m-1];
|
||||
eptr = self.HeaderPtr[m ];
|
||||
break;
|
||||
end
|
||||
h = m - 1;
|
||||
else
|
||||
if ( m == self.headerLen - 1 ) then
|
||||
sptr = self.HeaderPtr[m-1];
|
||||
eptr = self.HeaderPtr[m ];
|
||||
break;
|
||||
elseif ( ip <= self.HeaderSip[m+1] ) then
|
||||
sptr = self.HeaderPtr[m ];
|
||||
eptr = self.HeaderPtr[m+1];
|
||||
break;
|
||||
end
|
||||
l = m + 1;
|
||||
end
|
||||
end
|
||||
|
||||
-- match nothing just stop it
|
||||
if ( sptr == 0 ) then return nil; end
|
||||
|
||||
-- 2. search the index blocks to define the data
|
||||
self.dbFileHandler:seek("set", sptr);
|
||||
local blockLen = eptr - sptr;
|
||||
local index = self.dbFileHandler:read(blockLen + INDEX_BLOCK_LENGTH);
|
||||
local dataPtr = 0;
|
||||
|
||||
l = 0;
|
||||
h = blockLen / INDEX_BLOCK_LENGTH;
|
||||
while ( l <= h ) do
|
||||
local m = math.floor((l + h) / 2);
|
||||
local p = m * INDEX_BLOCK_LENGTH;
|
||||
local sip = getLong(index, p + 1); -- 0 + 1
|
||||
if ( ip < sip ) then
|
||||
h = m - 1;
|
||||
else
|
||||
local eip = getLong(index, p + 5); -- 4 + 1
|
||||
if ( ip > eip ) then
|
||||
l = m + 1;
|
||||
else
|
||||
dataPtr = getLong(index, p + 9); -- 8 + 1
|
||||
break;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- not matched
|
||||
if ( dataPtr == 0 ) then return nil; end
|
||||
|
||||
-- 3. get the data
|
||||
local dataLen = bit32.band(bit32.rshift(dataPtr, 24), 0xFF);
|
||||
dataPtr = bit32.band(dataPtr, 0x00FFFFFF);
|
||||
|
||||
self.dbFileHandler:seek("set", dataPtr);
|
||||
local data = self.dbFileHandler:read(dataLen);
|
||||
|
||||
return {
|
||||
city_id = getLong(data, 1), -- 0 + 1
|
||||
region = string.sub(data, 5) -- 4 + 1
|
||||
};
|
||||
end
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,5 +3,7 @@ local cjson = require "cjson";
|
|||
local Ip2region = require "Ip2region";
|
||||
|
||||
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:btreeSearch("120.79.17.142");
|
||||
print(cjson.encode(data));
|
||||
|
|
@ -266,7 +266,7 @@ class Ip2Region
|
|||
fseek($this->dbFileHandler, $sptr);
|
||||
$index = fread($this->dbFileHandler, $blockLen + INDEX_BLOCK_LENGTH);
|
||||
|
||||
$dataptr = 0;
|
||||
$dataPtr = 0;
|
||||
$l = 0; $h = $blockLen / INDEX_BLOCK_LENGTH;
|
||||
while ( $l <= $h ) {
|
||||
$m = (($l + $h) >> 1);
|
||||
|
|
@ -279,18 +279,18 @@ class Ip2Region
|
|||
if ( $ip > $eip ) {
|
||||
$l = $m + 1;
|
||||
} else {
|
||||
$dataptr = self::getLong($index, $p + 8);
|
||||
$dataPtr = self::getLong($index, $p + 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//not matched
|
||||
if ( $dataptr == 0 ) return NULL;
|
||||
if ( $dataPtr == 0 ) return NULL;
|
||||
|
||||
//3. get the data
|
||||
$dataLen = (($dataptr >> 24) & 0xFF);
|
||||
$dataPtr = ($dataptr & 0x00FFFFFF);
|
||||
$dataLen = (($dataPtr >> 24) & 0xFF);
|
||||
$dataPtr = ($dataPtr & 0x00FFFFFF);
|
||||
|
||||
fseek($this->dbFileHandler, $dataPtr);
|
||||
$data = fread($this->dbFileHandler, $dataLen);
|
||||
|
|
|
|||
Loading…
Reference in New Issue