# -*- coding:utf-8 -*- """ " ip2region python seacher client module " " Author: koma " Date : 2015-11-06 """ import struct, io, socket, sys class Ip2Region(object): def __init__(self, dbfile): self.__INDEX_BLOCK_LENGTH = 12 self.__TOTAL_HEADER_LENGTH = 8192 self.__f = None self.__headerSip = [] self.__headerPtr = [] self.__headerLen = 0 self.__indexSPtr = 0 self.__indexLPtr = 0 self.__indexCount = 0 self.__dbBinStr = '' 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.__indexSPtr = self.getLong(self.__dbBinStr, 0) self.__indexLPtr = self.getLong(self.__dbBinStr, 4) self.__indexCount = int((self.__indexLPtr - self.__indexSPtr) / self.__INDEX_BLOCK_LENGTH) + 1 l, h, dataPtr = (0, self.__indexCount, 0) while l <= h: m = int((l + h) >> 1) p = self.__indexSPtr + m * self.__INDEX_BLOCK_LENGTH sip = self.getLong(self.__dbBinStr, p) if ip < sip: h = m - 1 else: eip = self.getLong(self.__dbBinStr, p + 4) if ip > eip: l = m + 1 else: dataPtr = self.getLong(self.__dbBinStr, p + 8) break if dataPtr == 0: raise Exception("Data pointer not found") return self.returnData(dataPtr) def binarySearch(self, ip): """ " binary search method " param: ip """ if not ip.isdigit(): ip = self.ip2long(ip) if self.__indexCount == 0: self.__f.seek(0) superBlock = self.__f.read(8) self.__indexSPtr = self.getLong(superBlock, 0) self.__indexLPtr = self.getLong(superBlock, 4) self.__indexCount = int((self.__indexLPtr - self.__indexSPtr) / self.__INDEX_BLOCK_LENGTH) + 1 l, h, dataPtr = (0, self.__indexCount, 0) while l <= h: m = int((l + h) >> 1) p = m * self.__INDEX_BLOCK_LENGTH self.__f.seek(self.__indexSPtr + p) buffer = self.__f.read(self.__INDEX_BLOCK_LENGTH) sip = self.getLong(buffer, 0) if ip < sip: h = m - 1 else: eip = self.getLong(buffer, 4) if ip > eip: l = m + 1 else: dataPtr = self.getLong(buffer, 8) break if dataPtr == 0: raise Exception("Data pointer not found") return self.returnData(dataPtr) def btreeSearch(self, ip): """ " b-tree search method " param: ip """ if not ip.isdigit(): ip = self.ip2long(ip) if len(self.__headerSip) < 1: headerLen = 0 # pass the super block self.__f.seek(8) # read the header block b = self.__f.read(self.__TOTAL_HEADER_LENGTH) # parse the header block for i in range(0, len(b), 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 += 1 self.__headerLen = headerLen l, h, sptr, eptr = (0, self.__headerLen, 0, 0) while l <= h: m = int((l + h) >> 1) if ip == self.__headerSip[m]: if m > 0: sptr = self.__headerPtr[m - 1] eptr = self.__headerPtr[m] else: sptr = self.__headerPtr[m] eptr = self.__headerPtr[m + 1] break if ip < self.__headerSip[m]: 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 else: if m == self.__headerLen - 1: 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 if sptr == 0: raise Exception("Index pointer not found") indexLen = eptr - sptr self.__f.seek(sptr) index = self.__f.read(indexLen + self.__INDEX_BLOCK_LENGTH) l, h, dataPrt = (0, int(indexLen / self.__INDEX_BLOCK_LENGTH), 0) while l <= h: m = int((l + h) >> 1) offset = int(m * self.__INDEX_BLOCK_LENGTH) sip = self.getLong(index, offset) if ip < sip: h = m - 1 else: eip = self.getLong(index, offset + 4) if ip > eip: l = m + 1 else: dataPrt = self.getLong(index, offset + 8) break if dataPrt == 0: raise Exception("Data pointer not found") return self.returnData(dataPrt) def initDatabase(self, dbfile): """ " initialize the database for search " param: dbFile """ try: self.__f = io.open(dbfile, "rb") except IOError as e: print("[Error]: %s" % e) sys.exit() def returnData(self, dataPtr): """ " get ip data from db file by data start ptr " param: dsptr """ dataLen = (dataPtr >> 24) & 0xFF dataPtr = dataPtr & 0x00FFFFFF 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): if self.__f != None: self.__f.close() self.__dbBinStr = None self.__headerPtr = None self.__headerSip = None