diff --git a/binding/python3/ip2Region.py b/binding/python3/ip2Region.py new file mode 100644 index 0000000..f2e638b --- /dev/null +++ b/binding/python3/ip2Region.py @@ -0,0 +1,240 @@ +#-*- coding:utf-8 -*- +""" +" ip2region python seacher client module +" +" Author: koma +" Date : 2015-11-06 +""" +import struct, io, socket, sys + +class Ip2Region(object): + __headerSip = [] + __headerPtr = [] + __f = None + __sPtr = 0 + __indexLen = 0 + __dbBinStr = '' + + def __init__(self, dbfile): + self.initDatabase(dbfile) + + def memorySearch(self, ip): + """ + " memory search method + " param: ip + """ + if not ip.isdigit(): ip = self.ip2long(ip) + + if self.__dbBinStr == '': + self.__dbBinStr = self.__f.read() #read all the contents in file + self.__sPtr = self.getLong(self.__dbBinStr, 0) + endPtr = self.getLong(self.__dbBinStr, 4) + self.__indexLen = endPtr - self.__sPtr + + startPtr = self.__sPtr + indexLen = self.__indexLen + dbBinStr = self.__dbBinStr + + l, h, mixPtr = (0, int(indexLen/12), 0) + while l <= h: + m = int((l+h)/2) + ptr = startPtr + m*12 + + sip = self.getLong(dbBinStr, ptr) + eip = self.getLong(dbBinStr, ptr+4) + + if ip >= sip: + if ip > eip: + l = m + 1 + else: + mixPtr = self.getLong(dbBinStr, ptr+8) + break; + else: + h = m - 1 + + if mixPtr == 0: return "N2" + + return self.returnData(mixPtr) + + def binarySearch(self, ip): + """ + " binary search method + " param: ip + """ + if not ip.isdigit(): ip = self.ip2long(ip) + + if self.__indexLen < 1: + self.__f.seek(0) + b = self.__f.read(8) + self.__sPtr = self.getLong(b, 0) + endPtr = self.getLong(b, 4) + self.__indexLen = endPtr - self.__sPtr + + startPtr = self.__sPtr + indexLen = self.__indexLen + + self.__f.seek(startPtr) + b = self.__f.read(indexLen+12) + + l, h, mixPtr = (0, int(indexLen/12), 0) + while l <= h: + m = int((l+h)/2) + ptr = startPtr + m*12 + self.__f.seek(ptr) + + b = self.__f.read(12) + sip = self.getLong(b, 0) + eip = self.getLong(b, 4) + + if ip >= sip: + if ip > eip: + l = m + 1 + else: + mixPtr = self.getLong(b, 8) + break; + else: + h = m - 1 + + if mixPtr == 0: return "N2" + + return self.returnData(mixPtr) + + def btreeSearch(self, ip): + """ + " b-tree search method + " param: ip + """ + if not ip.isdigit(): ip = self.ip2long(ip) + + if len(self.__headerSip) < 1: + #pass the super block + self.__f.seek(8) + #read the header block + b = self.__f.read(4086) + #parse the header block + sip = None + ptr = None + for i in range(0, len(b)-1, 8): + sip = self.getLong(b, i) + ptr = self.getLong(b, i+4) + if ptr == 0: + break + self.__headerSip.append(sip) + self.__headerPtr.append(ptr) + + headerLen = len(self.__headerSip) - 1 + l, h, sptr, eptr = (0, headerLen, 0, 0) + while l <= h: + m = int((l+h)/2) + + if ip == self.__headerSip[m]: + if m > 0: + sptr = self.__headerPtr[m-1] + eptr = self.__headerPtr[m] + break; + else: + sptr = self.__headerPtr[m] + eptr = self.__headerPtr[m+1] + break; + + if ip > self.__headerSip[m]: + if m == headerLen: + sptr = self.__headerPtr[m-1] + eptr = self.__headerPtr[m] + break; + elif ip < self.__headerSip[m+1]: + sptr = self.__headerPtr[m] + eptr = self.__headerPtr[m+1] + break; + + l = m + 1 + else: + if m == 0: + sptr = self.__headerPtr[m] + eptr = self.__headerPtr[m+1] + break; + elif ip > self.__headerSip[m-1]: + sptr = self.__headerPtr[m-1] + eptr = self.__headerPtr[m] + break; + + h = m - 1 + + if sptr == 0: return "N1" + + indexLen = eptr - sptr + self.__f.seek(sptr) + b = self.__f.read(indexLen + 12) + + l, h, mixPtr = (0, int(indexLen/12), 0) + while l <= h: + m = int((l+h)/2) + offset = m * 12 + + if ip >= self.getLong(b, offset): + if ip > self.getLong(b, offset+4): + l = m + 1 + else: + mixPtr = self.getLong(b, offset+8) + break; + else: + h = m - 1 + + if mixPtr == 0: return "N2" + + return self.returnData(mixPtr) + + def initDatabase(self, dbfile): + """ + " initialize the database for search + " param: dbFile + """ + try: + self.__f = io.open(dbfile, "rb") + except IOError as e: + print ("[Error]: ", e) + sys.exit() + + def returnData(self, dsptr): + """ + " get ip data from db file by data start ptr + " param: dsptr + """ + dataPtr = dsptr & 0x00FFFFFF + dataLen = (dsptr >> 24) & 0xFF + + self.__f.seek(dataPtr) + data = self.__f.read(dataLen) + + return { + "city_id": self.getLong(data, 0), + "region" : data[4:] + } + + def ip2long(self, ip): + _ip = socket.inet_aton(ip) + + return struct.unpack("!L", _ip)[0] + + def isip(self, ip): + p = ip.split(".") + + if len(p) != 4 : return False + for pp in p: + if not pp.isdigit() : return False + if len(pp) > 3 : return False + if int(pp) > 255 : return False + + return True + + def getLong(self, b, offset): + if len( b[offset:offset+4] ) == 4: + return struct.unpack('I', b[offset:offset+4])[0] + + return 0 + + def close(self): + self.__headerSip = None + self.__headerPtr = None + self.__f.close() + self.__f = None diff --git a/binding/python3/testSearcher.py b/binding/python3/testSearcher.py new file mode 100644 index 0000000..fd36f10 --- /dev/null +++ b/binding/python3/testSearcher.py @@ -0,0 +1,79 @@ +#-*- coding:utf-8 -*- +""" +" ip2region python seacher client module +" +" Autho: koma +" Date : 2015-11-06 +""" +import struct, sys, os, time +from ip2Region import Ip2Region + +def testSearch(): + """ + " ip2region test function + """ + llen = len(sys.argv) + + if llen < 2: + print ("Usage: python testSearcher.py [ip2region db file] [alrogrithm]") + print ("Algorithm: binary or b-tree") + return 0 + + dbFile = sys.argv[1] + method = 1 + algorithm = "b-tree" + + if (not os.path.isfile(dbFile)) or (not os.path.exists(dbFile)): + print ("[Error]: Specified db file is not exists.") + return 0 + + if llen > 2: + algorithm = sys.argv[2] + if algorithm == "binary": + method = 2 + elif algorithm == "memory": + method = 3 + + print ("initializing %s..."% algorithm) + print ("+----------------------------------+") + print ("| ip2region test program |") + print ("| Author: chenxin619315@gmail.com. |") + print ("| Type 'quit' or 'exit' to exit program |") + print ("+----------------------------------+") + + searcher = Ip2Region(dbFile); + + while True: + line = input("ip2region>> ") + line = line.strip() + + if line == "": + print ("[Error]: Invalid ip address.") + continue + + if line == "quit" or line == "exit" : + print ("[Info]: Thanks for your use, Bye.") + break + + if not searcher.isip(line): + print ("[Error]: Invalid ip address.") + continue + + sTime = time.time() * 1000 + if method == 1: + data = searcher.btreeSearch(line) + elif method == 2: + data = searcher.binarySearch(line) + else: + data = searcher.memorySearch(line) + eTime = time.time() * 1000 + + if isinstance(data, dict): + print ("%s|%s in %f millseconds" % (data["city_id"], data["region"].decode('utf-8'), eTime-sTime)) + else: + print ("[Error]: ", data) + + searcher.close() + +if __name__ == "__main__": + testSearch()