Merge branch 'master' of github.com:lionsoul2016/ip2region into nodejsclient
This commit is contained in:
commit
4a8aa275e0
|
|
@ -0,0 +1,7 @@
|
|||
### 1.2
|
||||
|
||||
1. 新增分列式升级算法,大大的增加了数据的准确率,基本避免之前各大网友反馈的些许ip定位错误!
|
||||
2. 数据升级是2016/06/30版!
|
||||
3. 优化数据文件生成算法,ip2region.db文件由原来的3.5M降为1.5M,42亿个IP地址,皆大欢喜啊!
|
||||
4. C/PHP/JAVA客户端增加纯内存搜索模式(python, php扩展,nodejs会在后续版本加上!)
|
||||
说明:因为数据文件只有1.5M,对于PHP,java这类IO优化类语言提升速度不大,C有一个数量级的提升!
|
||||
24
README.md
24
README.md
|
|
@ -1,6 +1,6 @@
|
|||
ip2region - ip到地区的映射库,妈妈再也不用担心我的ip定位。
|
||||
ip2region - 最自由的ip地址查询库,ip到地区的映射库,提供Binary,B树和纯内存三种查询算法,妈妈再也不用担心我的ip地址定位。
|
||||
|
||||
**1. 99.9%准确率,不定时更新:**
|
||||
**1. 99.9%准确率,定时更新:**
|
||||
|
||||
数据聚合了一些知名ip到地名查询提供商的数据,这些是他们官方的的准确率,经测试着实比纯真啥的准确多了。<br />
|
||||
每次聚合一下数据需要1-2天,会不定时更新。
|
||||
|
|
@ -14,20 +14,20 @@ ip2region - ip到地区的映射库,妈妈再也不用担心我的ip定位。
|
|||
|
||||
**3. 体积小:**
|
||||
|
||||
生成的数据库文件ip2region.db只有3.5M
|
||||
生成的数据库文件ip2region.db只有1.5M(1.2版本前是3.5M)
|
||||
|
||||
**4. 多查询客户端的支持,0.0x毫秒级别的查询**
|
||||
|
||||
已经集成的客户端有:java, php, c, python,php扩展(支持linux, php5, php7版本已支持)。
|
||||
|
||||
提供了两种查询算法,响应时间如下:
|
||||
客户端/binary算法/b-tree算法:
|
||||
java/0.x毫秒/0.x毫秒 (使用RandomAccessFile)
|
||||
php/0.x毫秒/0.1x毫秒
|
||||
c/0.0x毫秒/0.0x毫秒(b-tree算法基本稳定在0.02x毫秒级别)
|
||||
python/0.x毫秒/0.1x毫秒
|
||||
客户端/binary算法/b-tree算法/Memory算法:
|
||||
java/0.x毫秒/0.x毫秒/0.1x毫秒 (使用RandomAccessFile)
|
||||
php/0.x毫秒/0.1x毫秒/0.1x毫秒
|
||||
c/0.0x毫秒/0.0x毫秒/0.00x毫秒(b-tree算法基本稳定在0.02x毫秒级别)
|
||||
python/0.x毫秒/0.1x毫秒/未知
|
||||
|
||||
任何客户端b-tree都比binary算法快
|
||||
任何客户端b-tree都比binary算法快,当然Memory算法固然是最快的!
|
||||
|
||||
**5. 测试程序:**
|
||||
java:
|
||||
|
|
@ -61,10 +61,6 @@ python:
|
|||
p2region>> 101.105.35.57
|
||||
2163|中国|华南|广东省|深圳市|鹏博士 in 0.02295 millseconds
|
||||
|
||||
输入ip地址开始测试,第一次会稍微有点慢,在运行命令后面接入binary来尝试binary算法,建议使用b-tree算法。
|
||||
输入ip地址开始测试,第一次会稍微有点慢,在运行命令后面接入binary,memory来尝试其他算法,建议使用b-tree算法,速度和并发需求的可以使用memory算法。
|
||||
|
||||
具体集成请参考不同客户端的测试源码。
|
||||
|
||||
**6. 联系作者:**
|
||||
|
||||
狮子的魂: chenxin619315@gmail.com
|
||||
|
|
|
|||
|
|
@ -35,12 +35,15 @@ IP2R_API uint_t ip2region_create(ip2region_t ip2rObj, char *dbFile)
|
|||
if ( ip2rObj->dbHandler == NULL ) {
|
||||
IP2R_FREE(ip2rObj->HeaderSip);
|
||||
IP2R_FREE(ip2rObj->HeaderPtr);
|
||||
//fprintf(stderr, "Fail to open the db file %s\n", ip2rObj>dbFile);
|
||||
//exit(-1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ip2rObj->firstIndexPtr = 0;
|
||||
ip2rObj->lastIndexPtr = 0;
|
||||
ip2rObj->totalBlocks = 0;
|
||||
ip2rObj->dbBinStr = NULL;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -63,9 +66,94 @@ IP2R_API uint_t ip2region_destroy(ip2region_t ip2rObj)
|
|||
ip2rObj->dbHandler = NULL;
|
||||
}
|
||||
|
||||
//free the db binary string
|
||||
if ( ip2rObj->dbBinStr != NULL ) {
|
||||
IP2R_FREE(ip2rObj->dbBinStr);
|
||||
ip2rObj->dbBinStr = NULL;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the region associated with the specified ip address with the memory binary search algorithm
|
||||
*
|
||||
* @param ip2rObj
|
||||
* @param ip
|
||||
* @param datablock
|
||||
*/
|
||||
IP2R_API uint_t ip2region_memory_search(ip2region_t ip2rObj, uint_t ip, datablock_t datablock)
|
||||
{
|
||||
int l, h, m, p;
|
||||
uint_t sip, eip, dptr;
|
||||
int dataLen, dataptr;
|
||||
long filesize;
|
||||
char *buffer;
|
||||
|
||||
if ( ip2rObj->dbBinStr == NULL ) {
|
||||
//get the size of the file
|
||||
fseek(ip2rObj->dbHandler, 0, SEEK_END);
|
||||
filesize = ftell(ip2rObj->dbHandler);
|
||||
fseek(ip2rObj->dbHandler, 0, SEEK_SET);
|
||||
|
||||
//alloc the buffer size
|
||||
ip2rObj->dbBinStr = IP2R_MALLOC(filesize);
|
||||
if ( ip2rObj->dbBinStr == NULL ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
//now read the whole file
|
||||
if ( fread(ip2rObj->dbBinStr, filesize, 1, ip2rObj->dbHandler) != 1 ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
buffer = ip2rObj->dbBinStr;
|
||||
ip2rObj->firstIndexPtr = getUnsignedInt(buffer, 0);
|
||||
ip2rObj->lastIndexPtr = getUnsignedInt(buffer, 4);
|
||||
ip2rObj->totalBlocks = (ip2rObj->lastIndexPtr-ip2rObj->firstIndexPtr)/INDEX_BLOCK_LENGTH + 1;
|
||||
}
|
||||
|
||||
l = 0; h = ip2rObj->totalBlocks; dptr = 0;
|
||||
while ( l <= h ) {
|
||||
m = (l + h) >> 1;
|
||||
p = ip2rObj->firstIndexPtr + m * INDEX_BLOCK_LENGTH;
|
||||
|
||||
buffer = ip2rObj->dbBinStr + p;
|
||||
sip = getUnsignedInt(buffer, 0);
|
||||
if ( ip < sip ) {
|
||||
h = m - 1;
|
||||
} else {
|
||||
eip = getUnsignedInt(buffer, 4);
|
||||
if ( ip > eip ) {
|
||||
l = m + 1;
|
||||
} else {
|
||||
dptr = getUnsignedInt(buffer, 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( dptr == 0 ) return 0;
|
||||
|
||||
//get the data
|
||||
dataLen = ((dptr >> 24) & 0xFF);
|
||||
dataptr = (dptr & 0x00FFFFFF);
|
||||
buffer = ip2rObj->dbBinStr + dataptr;
|
||||
|
||||
//fill the data to the datablock
|
||||
datablock->city_id = getUnsignedInt(buffer, 0);
|
||||
dataLen -= 4; //reduce the length of the city_id
|
||||
memcpy(datablock->region, buffer + 4, dataLen);
|
||||
datablock->region[dataLen] = '\0';
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
IP2R_API uint_t ip2region_memory_search_string(ip2region_t ip2rObj, char *ip, datablock_t datablock)
|
||||
{
|
||||
return ip2region_memory_search(ip2rObj, ip2long(ip), datablock);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the region associated with the specifield ip address with binary search algorithm
|
||||
*
|
||||
|
|
|
|||
|
|
@ -52,7 +52,9 @@ typedef struct {
|
|||
uint_t *HeaderSip; //header start ip blocks
|
||||
uint_t *HeaderPtr; //header ptr blocks
|
||||
uint_t headerLen; //header block number
|
||||
char *dbFile; //path of db file
|
||||
FILE *dbHandler; //file handler
|
||||
char *dbBinStr; //db binary string for memory search mode
|
||||
|
||||
uint_t firstIndexPtr; //first index ptr
|
||||
uint_t lastIndexPtr; //last index ptr
|
||||
|
|
@ -79,14 +81,26 @@ typedef datablock_entry * datablock_t;
|
|||
IP2R_API uint_t ip2region_create(ip2region_t, char *);
|
||||
|
||||
/**
|
||||
* destroy the specifield ip2region object
|
||||
* destroy the specified ip2region object
|
||||
*
|
||||
* @param ip2region_t
|
||||
*/
|
||||
IP2R_API uint_t ip2region_destroy(ip2region_t);
|
||||
|
||||
/**
|
||||
* get the region associated with the specifield ip address with binary search algorithm
|
||||
* get the region associated with the specified ip address with the memory binary search algorithm
|
||||
*
|
||||
* @param ip2region_t
|
||||
* @param uint_t
|
||||
* @param datablock_t
|
||||
* @date 2016/06/30
|
||||
*/
|
||||
IP2R_API uint_t ip2region_memory_search(ip2region_t, uint_t, datablock_t);
|
||||
IP2R_API uint_t ip2region_memory_search_string(ip2region_t, char *, datablock_t);
|
||||
|
||||
|
||||
/**
|
||||
* get the region associated with the specified ip address with binary search algorithm
|
||||
*
|
||||
* @param ip2rObj
|
||||
* @param ip
|
||||
|
|
@ -97,7 +111,7 @@ IP2R_API uint_t ip2region_binary_search(ip2region_t, uint_t, datablock_t);
|
|||
IP2R_API uint_t ip2region_binary_search_string(ip2region_t, char *, datablock_t);
|
||||
|
||||
/**
|
||||
* get the region associated with the specifield ip address with b-tree algorithm
|
||||
* get the region associated with the specified ip address with b-tree algorithm
|
||||
*
|
||||
* @param ip2rObj
|
||||
* @param ip
|
||||
|
|
@ -108,7 +122,7 @@ IP2R_API uint_t ip2region_btree_search(ip2region_t, uint_t, datablock_t);
|
|||
IP2R_API uint_t ip2region_btree_search_string(ip2region_t, char *, datablock_t);
|
||||
|
||||
/**
|
||||
* get a unsinged long(4bytes) from a specifield buffer start from the specifield offset
|
||||
* get a unsinged long(4bytes) from a specified buffer start from the specified offset
|
||||
*
|
||||
* @param buffer
|
||||
* @param offset
|
||||
|
|
|
|||
|
|
@ -54,22 +54,27 @@ int main( int argc, char **argv )
|
|||
memset(&datablock, 0x00, sizeof(datablock_entry));
|
||||
|
||||
if ( argc < 2 ) {
|
||||
printf("Usage: a.out [ip2region db file path] [algorithm]");
|
||||
printf("Usage: a.out [ip2region db file path] [algorithm]\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dbFile = argv[1];
|
||||
algorithm = "B-tree";
|
||||
func_ptr = ip2region_btree_search_string;
|
||||
if ( argc >= 3 && strcmp(argv[2], "binary") == 0 ) {
|
||||
if ( argc >= 3 ) {
|
||||
if ( strcmp(argv[2], "binary") == 0 ) {
|
||||
algorithm = "Binary";
|
||||
func_ptr = ip2region_binary_search_string;
|
||||
} else if ( strcmp(argv[2], "memory") == 0 ) {
|
||||
algorithm = "Memory";
|
||||
func_ptr = ip2region_memory_search_string;
|
||||
}
|
||||
}
|
||||
|
||||
//create a new ip2rObj
|
||||
printf("+--initializing %s ... \n", algorithm);
|
||||
if ( ip2region_create(&ip2rEntry, dbFile) == 0 ) {
|
||||
println("Error: Fail to create the ip2region object");
|
||||
println("Error: Fail to create the ip2region object\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
<property name="jars" value="${basedir}"/>
|
||||
<property name="sources" value="${basedir}/src"/>
|
||||
<property name="classes" value="${basedir}/classes"/>
|
||||
<property name="version" value="1.0"/>
|
||||
<property name="version" value="1.2"/>
|
||||
<property name="api" value="${basedir}/doc"/>
|
||||
<mkdir dir="${classes}"/>
|
||||
<mkdir dir="${api}"/>
|
||||
|
|
|
|||
|
|
@ -17,10 +17,28 @@ public class DataBlock
|
|||
*/
|
||||
private String region;
|
||||
|
||||
public DataBlock( int city_id, String region )
|
||||
/**
|
||||
* region ptr in the db file
|
||||
*/
|
||||
private int dataPtr;
|
||||
|
||||
/**
|
||||
* construct method
|
||||
*
|
||||
* @param city_id
|
||||
* @param region region string
|
||||
* @param ptr data ptr
|
||||
*/
|
||||
public DataBlock( int city_id, String region, int dataPtr )
|
||||
{
|
||||
this.city_id = city_id;
|
||||
this.region = region;
|
||||
this.dataPtr = dataPtr;
|
||||
}
|
||||
|
||||
public DataBlock(int city_id, String region)
|
||||
{
|
||||
this(city_id, region, 0);
|
||||
}
|
||||
|
||||
public int getCityId()
|
||||
|
|
@ -45,12 +63,24 @@ public class DataBlock
|
|||
return this;
|
||||
}
|
||||
|
||||
public int getDataPtr()
|
||||
{
|
||||
return dataPtr;
|
||||
}
|
||||
|
||||
public DataBlock setDataPtr(int dataPtr)
|
||||
{
|
||||
this.dataPtr = dataPtr;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append(city_id).append('|').append(region);
|
||||
sb.append(city_id).append('|').append(region).append('|').append(dataPtr);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ import java.io.RandomAccessFile;
|
|||
public class DbSearcher
|
||||
{
|
||||
public static final int BTREE_ALGORITHM = 1;
|
||||
public static final int BIN_ALGORITHM = 2;
|
||||
public static final int BINARY_ALGORITHM = 2;
|
||||
public static final int MEMORY_ALGORITYM = 3;
|
||||
|
||||
/**
|
||||
* db config
|
||||
|
|
@ -38,6 +39,12 @@ public class DbSearcher
|
|||
private long lastIndexPtr = 0;
|
||||
private int totalIndexBlocks = 0;
|
||||
|
||||
/**
|
||||
* for memory mode
|
||||
* the original db binary string
|
||||
*/
|
||||
private byte[] dbBinStr = null;
|
||||
|
||||
/**
|
||||
* construct class
|
||||
*
|
||||
|
|
@ -51,6 +58,72 @@ public class DbSearcher
|
|||
raf = new RandomAccessFile(dbFile, "r");
|
||||
}
|
||||
|
||||
/**
|
||||
* get the region with a int ip address with memory binary search algorithm
|
||||
*
|
||||
* @param ip
|
||||
* @throws IOException
|
||||
*/
|
||||
public DataBlock memorySearch(long ip) throws IOException
|
||||
{
|
||||
int blen = IndexBlock.getIndexBlockLength();
|
||||
if ( dbBinStr == null ) {
|
||||
dbBinStr = new byte[(int)raf.length()];
|
||||
raf.seek(0L);
|
||||
raf.readFully(dbBinStr, 0, dbBinStr.length);
|
||||
|
||||
//initialize the global vars
|
||||
firstIndexPtr = Util.getIntLong(dbBinStr, 0);
|
||||
lastIndexPtr = Util.getIntLong(dbBinStr, 4);
|
||||
totalIndexBlocks = (int)((lastIndexPtr - firstIndexPtr)/blen) + 1;
|
||||
}
|
||||
|
||||
//search the index blocks to define the data
|
||||
int l = 0, h = totalIndexBlocks;
|
||||
long sip, eip, dataptr = 0;
|
||||
while ( l <= h ) {
|
||||
int m = (l + h) >> 1;
|
||||
int p = (int)(firstIndexPtr + m * blen);
|
||||
|
||||
sip = Util.getIntLong(dbBinStr, p);
|
||||
if ( ip < sip ) {
|
||||
h = m - 1;
|
||||
} else {
|
||||
eip = Util.getIntLong(dbBinStr, p + 4);
|
||||
if ( ip > eip ) {
|
||||
l = m + 1;
|
||||
} else {
|
||||
dataptr = Util.getIntLong(dbBinStr, p + 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//not matched
|
||||
if ( dataptr == 0 ) return null;
|
||||
|
||||
//get the data
|
||||
int dataLen = (int)((dataptr >> 24) & 0xFF);
|
||||
int dataPtr = (int)((dataptr & 0x00FFFFFF));
|
||||
int city_id = (int)Util.getIntLong(dbBinStr, dataPtr);
|
||||
String region = new String(dbBinStr, dataPtr + 4, dataLen - 4, "UTF-8");
|
||||
|
||||
return new DataBlock(city_id, region, dataPtr);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the region throught the ip address with memory binary search algorithm
|
||||
*
|
||||
* @param ip
|
||||
* @return DataBlock
|
||||
* @throws IOException
|
||||
*/
|
||||
public DataBlock memorySearch( String ip ) throws IOException
|
||||
{
|
||||
return memorySearch(Util.ip2long(ip));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get by index ptr
|
||||
*
|
||||
|
|
@ -76,7 +149,7 @@ public class DbSearcher
|
|||
int city_id = (int)Util.getIntLong(data, 0);
|
||||
String region = new String(data, 4, data.length - 4, "UTF-8");
|
||||
|
||||
return new DataBlock(city_id, region);
|
||||
return new DataBlock(city_id, region, dataPtr);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -204,7 +277,7 @@ public class DbSearcher
|
|||
int city_id = (int)Util.getIntLong(data, 0);
|
||||
String region = new String(data, 4, data.length - 4, "UTF-8");
|
||||
|
||||
return new DataBlock(city_id, region);
|
||||
return new DataBlock(city_id, region, dataPtr);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -274,7 +347,7 @@ public class DbSearcher
|
|||
int city_id = (int)Util.getIntLong(data, 0);
|
||||
String region = new String(data, 4, data.length - 4, "UTF-8");
|
||||
|
||||
return new DataBlock(city_id, region);
|
||||
return new DataBlock(city_id, region, dataPtr);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -308,6 +381,8 @@ public class DbSearcher
|
|||
{
|
||||
HeaderSip = null; //let gc do its work
|
||||
HeaderPtr = null;
|
||||
dbBinStr = null;
|
||||
raf.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import java.io.BufferedReader;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.lionsoul.ip2region.DataBlock;
|
||||
import org.lionsoul.ip2region.DbConfig;
|
||||
|
|
@ -25,23 +27,45 @@ public class TestSearcher
|
|||
return;
|
||||
}
|
||||
|
||||
int algorithm = DbSearcher.BTREE_ALGORITHM;
|
||||
File file = new File(argv[0]);
|
||||
if ( file.exists() == false ) {
|
||||
System.out.println("Error: Invalid ip2region.db file");
|
||||
return;
|
||||
}
|
||||
|
||||
int algorithm = DbSearcher.BTREE_ALGORITHM;
|
||||
String algoName = "B-tree";
|
||||
if ( argv.length > 1 ) {
|
||||
if ( argv[1].equalsIgnoreCase("binary")) algorithm = DbSearcher.BIN_ALGORITHM;
|
||||
if ( argv[1].equalsIgnoreCase("binary")) {
|
||||
algoName = "Binary";
|
||||
algorithm = DbSearcher.BINARY_ALGORITHM;
|
||||
} else if ( argv[1].equalsIgnoreCase("memory") ) {
|
||||
algoName = "Memory";
|
||||
algorithm = DbSearcher.MEMORY_ALGORITYM;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
System.out.println("initializing "+((algorithm==2)?"Binary":"B-tree")+" ... ");
|
||||
System.out.println("initializing "+algoName+" ... ");
|
||||
DbConfig config = new DbConfig();
|
||||
DbSearcher seacher = new DbSearcher(config, argv[0]);
|
||||
DbSearcher searcher = new DbSearcher(config, argv[0]);
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
|
||||
|
||||
//define the method
|
||||
Method method = null;
|
||||
switch ( algorithm )
|
||||
{
|
||||
case DbSearcher.BTREE_ALGORITHM:
|
||||
method = searcher.getClass().getMethod("btreeSearch", String.class);
|
||||
break;
|
||||
case DbSearcher.BINARY_ALGORITHM:
|
||||
method = searcher.getClass().getMethod("binarySearch", String.class);
|
||||
break;
|
||||
case DbSearcher.MEMORY_ALGORITYM:
|
||||
method = searcher.getClass().getMethod("memorySearch", String.class);
|
||||
break;
|
||||
}
|
||||
|
||||
System.out.println("+----------------------------------+");
|
||||
System.out.println("| ip2region test shell |");
|
||||
System.out.println("| Author: chenxin619315@gmail.com |");
|
||||
|
|
@ -62,13 +86,13 @@ public class TestSearcher
|
|||
}
|
||||
|
||||
sTime = System.nanoTime();
|
||||
dataBlock = algorithm==2 ? seacher.binarySearch(line) : seacher.btreeSearch(line);
|
||||
dataBlock = (DataBlock) method.invoke(searcher, line);
|
||||
cTime = (System.nanoTime() - sTime) / 1000000;
|
||||
System.out.printf("%s in %.5f millseconds\n", dataBlock, cTime);
|
||||
}
|
||||
|
||||
reader.close();
|
||||
seacher.close();
|
||||
searcher.close();
|
||||
System.out.println("+--Bye");
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
|
|
@ -76,6 +100,21 @@ public class TestSearcher
|
|||
} catch (DbMakerConfigException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchMethodException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (SecurityException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (IllegalArgumentException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,13 @@ class Ip2Region
|
|||
private $lastIndexPtr = 0;
|
||||
private $totalBlocks = 0;
|
||||
|
||||
/**
|
||||
* for memory mode only
|
||||
* the original db binary string
|
||||
*/
|
||||
private $dbBinStr = NULL;
|
||||
private $dbFile = NULL;
|
||||
|
||||
/**
|
||||
* construct method
|
||||
*
|
||||
|
|
@ -37,7 +44,65 @@ class Ip2Region
|
|||
*/
|
||||
public function __construct( $ip2regionFile )
|
||||
{
|
||||
$this->dbFileHandler = fopen($ip2regionFile, 'r');
|
||||
$this->dbFile = $ip2regionFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function memorySearch($ip)
|
||||
{
|
||||
//check and load the binary string for the first time
|
||||
if ( $this->dbBinStr == NULL ) {
|
||||
$this->dbBinStr = file_get_contents($this->dbFile);
|
||||
if ( $this->dbBinStr == false ) {
|
||||
throw new Exception("Fail to open the db file {$this->dbFile}");
|
||||
}
|
||||
|
||||
$this->firstIndexPtr = self::getLong($this->dbBinStr, 0);
|
||||
$this->lastIndexPtr = self::getLong($this->dbBinStr, 4);
|
||||
$this->totalBlocks = ($this->lastIndexPtr-$this->firstIndexPtr)/INDEX_BLOCK_LENGTH + 1;
|
||||
}
|
||||
|
||||
if ( is_string($ip) ) $ip = ip2long($ip);
|
||||
|
||||
//binary search to define the data
|
||||
$l = 0;
|
||||
$h = $this->totalBlocks;
|
||||
$dataPtr = 0;
|
||||
while ( $l <= $h ) {
|
||||
$m = (($l + $h) >> 1);
|
||||
$p = $this->firstIndexPtr + $m * INDEX_BLOCK_LENGTH;
|
||||
$sip = self::getLong($this->dbBinStr, $p);
|
||||
if ( $ip < $sip ) {
|
||||
$h = $m - 1;
|
||||
} else {
|
||||
$eip = self::getLong($this->dbBinStr, $p + 4);
|
||||
if ( $ip > $eip ) {
|
||||
$l = $m + 1;
|
||||
} else {
|
||||
$dataPtr = self::getLong($this->dbBinStr, $p + 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//not matched just stop it here
|
||||
if ( $dataPtr == 0 ) return NULL;
|
||||
|
||||
//get the data
|
||||
$dataLen = (($dataPtr >> 24) & 0xFF);
|
||||
$dataPtr = ($dataPtr & 0x00FFFFFF);
|
||||
|
||||
return array(
|
||||
'city_id' => self::getLong($this->dbBinStr, $dataPtr),
|
||||
'region' => substr($this->dbBinStr, $dataPtr + 4, $dataLen - 4)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -51,6 +116,14 @@ class Ip2Region
|
|||
//check and conver the ip address
|
||||
if ( is_string($ip) ) $ip = ip2long($ip);
|
||||
if ( $this->totalBlocks == 0 ) {
|
||||
//check and open the original db file
|
||||
if ( $this->dbFileHandler == NULL ) {
|
||||
$this->dbFileHandler = fopen($this->dbFile, 'r');
|
||||
if ( $this->dbFileHandler == false ) {
|
||||
throw new Exception("Fail to open the db file {$this->dbFile}");
|
||||
}
|
||||
}
|
||||
|
||||
fseek($this->dbFileHandler, 0);
|
||||
$superBlock = fread($this->dbFileHandler, 8);
|
||||
|
||||
|
|
@ -102,6 +175,7 @@ class Ip2Region
|
|||
|
||||
/**
|
||||
* get the data block associated with the specifield ip with b-tree search algorithm
|
||||
* @Note: not thread safe
|
||||
*
|
||||
* @param ip
|
||||
* @return Mixed Array for NULL for any error
|
||||
|
|
@ -112,6 +186,14 @@ class Ip2Region
|
|||
|
||||
//check and load the header
|
||||
if ( $this->HeaderSip == NULL ) {
|
||||
//check and open the original db file
|
||||
if ( $this->dbFileHandler == NULL ) {
|
||||
$this->dbFileHandler = fopen($this->dbFile, 'r');
|
||||
if ( $this->dbFileHandler == false ) {
|
||||
throw new Exception("Fail to open the db file {$this->dbFile}");
|
||||
}
|
||||
}
|
||||
|
||||
fseek($this->dbFileHandler, 8);
|
||||
$buffer = fread($this->dbFileHandler, TOTAL_HEADER_LENGTH);
|
||||
|
||||
|
|
@ -240,7 +322,11 @@ class Ip2Region
|
|||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if ( $this->dbFileHandler != NULL ) fclose($this->dbFileHandler);
|
||||
if ( $this->dbFileHandler != NULL ) {
|
||||
fclose($this->dbFileHandler);
|
||||
}
|
||||
|
||||
$this->dbBinStr = NULL;
|
||||
$this->HeaderSip = NULL;
|
||||
$this->HeaderPtr = NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,12 +15,19 @@ EOF;
|
|||
|
||||
array_shift($argv);
|
||||
$dbFile = $argv[0];
|
||||
$method = 1;
|
||||
$method = 'btreeSearch';
|
||||
$algorithm = 'B-tree';
|
||||
if ( isset($argv[1])
|
||||
&& strtolower($argv[1]) == 'binary' ) {
|
||||
$method = 2;
|
||||
if ( isset($argv[1]) ) {
|
||||
switch ( strtolower($argv[1]) ) {
|
||||
case 'binary':
|
||||
$algorithm = 'Binary';
|
||||
$method = 'binarySearch';
|
||||
break;
|
||||
case 'memory':
|
||||
$algorithm = 'Memory';
|
||||
$method = 'memorySearch';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
require dirname(__FILE__) . '/Ip2Region.class.php';
|
||||
|
|
@ -47,7 +54,7 @@ while ( true ) {
|
|||
}
|
||||
|
||||
$s_time = getTime();
|
||||
$data = $method==2 ? $ip2regionObj->binarySearch($line) : $ip2regionObj->btreeSearch($line);
|
||||
$data = $ip2regionObj->{$method}($line);
|
||||
$c_time = getTime() - $s_time;
|
||||
printf("%s|%s in %.5f millseconds\n", $data['city_id'], $data['region'], $c_time);
|
||||
}
|
||||
|
|
|
|||
39808
data/ip.merge.txt
39808
data/ip.merge.txt
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Loading…
Reference in New Issue