Format golang binding

This commit is contained in:
wangsong 2021-09-09 11:26:36 +08:00
parent f92bcf3170
commit 549510a52c
2 changed files with 86 additions and 132 deletions

View File

@ -1,11 +1,13 @@
package ip2region package ip2region
import ( import (
"encoding/binary"
"errors"
"fmt"
"io/ioutil"
"net"
"os" "os"
"strings" "strings"
"strconv"
"io/ioutil"
"errors"
) )
const ( const (
@ -13,16 +15,11 @@ const (
TOTAL_HEADER_LENGTH = 8192 TOTAL_HEADER_LENGTH = 8192
) )
var err error
var ipInfo IpInfo
type Ip2Region struct { type Ip2Region struct {
// db file handler // db file handler
dbFileHandler *os.File dbFileHandler *os.File
//header block info //header block info
headerSip []int64 headerSip []int64
headerPtr []int64 headerPtr []int64
headerLen int64 headerLen int64
@ -34,7 +31,6 @@ type Ip2Region struct {
// for memory mode only // for memory mode only
// the original db binary string // the original db binary string
dbBinStr []byte dbBinStr []byte
dbFile string dbFile string
} }
@ -48,40 +44,36 @@ type IpInfo struct {
ISP string ISP string
} }
func (ip IpInfo)String() string { func (ip IpInfo) String() string {
return strconv.FormatInt(ip.CityId, 10) + "|" + ip.Country + "|" + ip.Region + "|" + ip.Province + "|" + ip.City + "|" + ip.ISP return fmt.Sprintf("%d|%s|%s|%s|%s|%s", ip.CityId, ip.Country, ip.Region, ip.Province, ip.City, ip.ISP)
} }
func getIpInfo(cityId int64, line []byte) IpInfo { func getIpInfo(cityId int64, line []byte) IpInfo {
lineSlice := strings.Split(string(line), "|") lineSlice := strings.Split(string(line), "|")
ipInfo := IpInfo{}
length := len(lineSlice) length := len(lineSlice)
ipInfo.CityId = cityId
if length < 5 { if length < 5 {
for i := 0; i <= 5 - length; i++ { for i := 0; i <= 5-length; i++ {
lineSlice = append(lineSlice, "") lineSlice = append(lineSlice, "")
} }
} }
return IpInfo{
ipInfo.Country = lineSlice[0] CityId: cityId,
ipInfo.Region = lineSlice[1] Country: lineSlice[0],
ipInfo.Province = lineSlice[2] Region: lineSlice[1],
ipInfo.City = lineSlice[3] Province: lineSlice[2],
ipInfo.ISP = lineSlice[4] City: lineSlice[3],
return ipInfo ISP: lineSlice[4],
}
} }
func New(path string) (*Ip2Region, error) { func New(path string) (*Ip2Region, error) {
file, err := os.Open(path) file, err := os.Open(path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &Ip2Region{ return &Ip2Region{
dbFile:path, dbFile: path,
dbFileHandler:file, dbFileHandler: file,
}, nil }, nil
} }
@ -91,56 +83,50 @@ func (this *Ip2Region) Close() {
func (this *Ip2Region) MemorySearch(ipStr string) (ipInfo IpInfo, err error) { func (this *Ip2Region) MemorySearch(ipStr string) (ipInfo IpInfo, err error) {
ipInfo = IpInfo{} ipInfo = IpInfo{}
if this.totalBlocks == 0 { if this.totalBlocks == 0 {
this.dbBinStr, err = ioutil.ReadFile(this.dbFile) this.dbBinStr, err = ioutil.ReadFile(this.dbFile)
if err != nil { if err != nil {
return ipInfo, err return ipInfo, err
} }
this.firstIndexPtr = getLong(this.dbBinStr, 0) this.firstIndexPtr = getLong(this.dbBinStr, 0)
this.lastIndexPtr = getLong(this.dbBinStr, 4) this.lastIndexPtr = getLong(this.dbBinStr, 4)
this.totalBlocks = (this.lastIndexPtr - this.firstIndexPtr) / INDEX_BLOCK_LENGTH + 1 this.totalBlocks = (this.lastIndexPtr-this.firstIndexPtr)/INDEX_BLOCK_LENGTH + 1
} }
ip, err := ip2long(ipStr) ip, err := ip2long(ipStr)
if err != nil { if err != nil {
return ipInfo, err return ipInfo, err
} }
var (
h := this.totalBlocks h = this.totalBlocks
var dataPtr, l int64; dataPtr, l int64
for (l <= h) { )
for l <= h {
m := (l + h) >> 1 m := (l + h) >> 1
p := this.firstIndexPtr + m * INDEX_BLOCK_LENGTH p := this.firstIndexPtr + m*INDEX_BLOCK_LENGTH
sip := getLong(this.dbBinStr, p) sip := getLong(this.dbBinStr, p)
if ip < sip { if ip < sip {
h = m - 1 h = m - 1
} else { } else {
eip := getLong(this.dbBinStr, p + 4) eip := getLong(this.dbBinStr, p+4)
if ip > eip { if ip > eip {
l = m + 1 l = m + 1
} else { } else {
dataPtr = getLong(this.dbBinStr, p + 8) dataPtr = getLong(this.dbBinStr, p+8)
break; break
} }
} }
} }
if dataPtr == 0 { if dataPtr == 0 {
return ipInfo, errors.New("not found") return ipInfo, errors.New("not found")
} }
dataLen := (dataPtr >> 24) & 0xFF
dataLen := ((dataPtr >> 24) & 0xFF) dataPtr = dataPtr & 0x00FFFFFF
dataPtr = (dataPtr & 0x00FFFFFF); ipInfo = getIpInfo(getLong(this.dbBinStr, dataPtr), this.dbBinStr[(dataPtr)+4:dataPtr+dataLen])
ipInfo = getIpInfo(getLong(this.dbBinStr, dataPtr), this.dbBinStr[(dataPtr) + 4:dataPtr + dataLen])
return ipInfo, nil return ipInfo, nil
} }
func (this *Ip2Region)BinarySearch(ipStr string) (ipInfo IpInfo, err error) { func (this *Ip2Region) BinarySearch(ipStr string) (ipInfo IpInfo, err error) {
ipInfo = IpInfo{} ipInfo = IpInfo{}
if this.totalBlocks == 0 { if this.totalBlocks == 0 {
this.dbFileHandler.Seek(0, 0) this.dbFileHandler.Seek(0, 0)
@ -148,34 +134,27 @@ func (this *Ip2Region)BinarySearch(ipStr string) (ipInfo IpInfo, err error) {
this.dbFileHandler.Read(superBlock) this.dbFileHandler.Read(superBlock)
this.firstIndexPtr = getLong(superBlock, 0) this.firstIndexPtr = getLong(superBlock, 0)
this.lastIndexPtr = getLong(superBlock, 4) this.lastIndexPtr = getLong(superBlock, 4)
this.totalBlocks = (this.lastIndexPtr - this.firstIndexPtr) / INDEX_BLOCK_LENGTH + 1 this.totalBlocks = (this.lastIndexPtr-this.firstIndexPtr)/INDEX_BLOCK_LENGTH + 1
} }
var (
var l, dataPtr, p int64 h = this.totalBlocks
l, dataPtr, p int64
h := this.totalBlocks )
ip, err := ip2long(ipStr) ip, err := ip2long(ipStr)
if err != nil { if err != nil {
return return
} }
for l <= h {
for (l <= h) {
m := (l + h) >> 1 m := (l + h) >> 1
p = m * INDEX_BLOCK_LENGTH p = m * INDEX_BLOCK_LENGTH
_, err = this.dbFileHandler.Seek(this.firstIndexPtr+p, 0)
_, err = this.dbFileHandler.Seek(this.firstIndexPtr + p, 0)
if err != nil { if err != nil {
return return
} }
buffer := make([]byte, INDEX_BLOCK_LENGTH) buffer := make([]byte, INDEX_BLOCK_LENGTH)
_, err = this.dbFileHandler.Read(buffer) _, err = this.dbFileHandler.Read(buffer)
if err != nil { if err != nil {
return
} }
sip := getLong(buffer, 0) sip := getLong(buffer, 0)
if ip < sip { if ip < sip {
@ -186,129 +165,119 @@ func (this *Ip2Region)BinarySearch(ipStr string) (ipInfo IpInfo, err error) {
l = m + 1 l = m + 1
} else { } else {
dataPtr = getLong(buffer, 8) dataPtr = getLong(buffer, 8)
break; break
} }
} }
} }
if dataPtr == 0 { if dataPtr == 0 {
err = errors.New("not found") return ipInfo, errors.New("not found")
return
} }
dataLen := (dataPtr >> 24) & 0xFF
dataLen := ((dataPtr >> 24) & 0xFF) dataPtr = dataPtr & 0x00FFFFFF
dataPtr = (dataPtr & 0x00FFFFFF);
this.dbFileHandler.Seek(dataPtr, 0) this.dbFileHandler.Seek(dataPtr, 0)
data := make([]byte, dataLen) data := make([]byte, dataLen)
this.dbFileHandler.Read(data) this.dbFileHandler.Read(data)
ipInfo = getIpInfo(getLong(data, 0), data[4:dataLen]) ipInfo = getIpInfo(getLong(data, 0), data[4:dataLen])
err = nil
return return
} }
func (this *Ip2Region) BtreeSearch(ipStr string) (ipInfo IpInfo, err error) { func (this *Ip2Region) BtreeSearch(ipStr string) (ipInfo IpInfo, err error) {
ipInfo = IpInfo{}
ip, err := ip2long(ipStr) ip, err := ip2long(ipStr)
if err != nil {
return
}
ipInfo = IpInfo{}
if this.headerLen == 0 { if this.headerLen == 0 {
this.dbFileHandler.Seek(8, 0) this.dbFileHandler.Seek(8, 0)
buffer := make([]byte, TOTAL_HEADER_LENGTH) buffer := make([]byte, TOTAL_HEADER_LENGTH)
this.dbFileHandler.Read(buffer) this.dbFileHandler.Read(buffer)
var idx int64; var idx int64
for i := 0; i < TOTAL_HEADER_LENGTH; i += 8 { for i := 0; i < TOTAL_HEADER_LENGTH; i += 8 {
startIp := getLong(buffer, int64(i)) startIp := getLong(buffer, int64(i))
dataPar := getLong(buffer, int64(i + 4)) dataPar := getLong(buffer, int64(i+4))
if dataPar == 0 { if dataPar == 0 {
break break
} }
this.headerSip = append(this.headerSip, startIp) this.headerSip = append(this.headerSip, startIp)
this.headerPtr = append(this.headerPtr, dataPar) this.headerPtr = append(this.headerPtr, dataPar)
idx ++; idx++
} }
this.headerLen = idx this.headerLen = idx
} }
var l, sptr, eptr int64 var (
h := this.headerLen l, sptr, eptr int64
h = this.headerLen
)
for l <= h { for l <= h {
m := int64(l + h) >> 1 m := (l + h) >> 1
if m < this.headerLen { if m < this.headerLen {
if ip == this.headerSip[m] { if ip == this.headerSip[m] {
if m > 0 { if m > 0 {
sptr = this.headerPtr[m - 1] sptr = this.headerPtr[m-1]
eptr = this.headerPtr[m] eptr = this.headerPtr[m]
} else { } else {
sptr = this.headerPtr[m] sptr = this.headerPtr[m]
eptr = this.headerPtr[m + 1] eptr = this.headerPtr[m+1]
} }
break break
} }
if ip < this.headerSip[m] { if ip < this.headerSip[m] {
if m == 0 { if m == 0 {
sptr = this.headerPtr[m] sptr = this.headerPtr[m]
eptr = this.headerPtr[m + 1] eptr = this.headerPtr[m+1]
break break
} else if ip > this.headerSip[m - 1] { } else if ip > this.headerSip[m-1] {
sptr = this.headerPtr[m - 1] sptr = this.headerPtr[m-1]
eptr = this.headerPtr[m] eptr = this.headerPtr[m]
break break
} }
h = m - 1 h = m - 1
} else { } else {
if m == this.headerLen - 1 { if m == this.headerLen-1 {
sptr = this.headerPtr[m - 1] sptr = this.headerPtr[m-1]
eptr = this.headerPtr[m] eptr = this.headerPtr[m]
break break
} else if ip <= this.headerSip[m + 1] { } else if ip <= this.headerSip[m+1] {
sptr = this.headerPtr[m ] sptr = this.headerPtr[m]
eptr = this.headerPtr[m + 1] eptr = this.headerPtr[m+1]
break break
} }
l = m + 1 l = m + 1
} }
} }
} }
if sptr == 0 { if sptr == 0 {
err = errors.New("not found") return ipInfo, errors.New("not found")
return
} }
blockLen := eptr - sptr blockLen := eptr - sptr
this.dbFileHandler.Seek(sptr, 0) this.dbFileHandler.Seek(sptr, 0)
index := make([]byte, blockLen + INDEX_BLOCK_LENGTH) index := make([]byte, blockLen+INDEX_BLOCK_LENGTH)
this.dbFileHandler.Read(index) this.dbFileHandler.Read(index)
var dataptr int64 var dataptr int64
h = blockLen / INDEX_BLOCK_LENGTH h = blockLen / INDEX_BLOCK_LENGTH
l = 0 l = 0
for l <= h { for l <= h {
m := int64(l + h) >> 1 m := (l + h) >> 1
p := m * INDEX_BLOCK_LENGTH p := m * INDEX_BLOCK_LENGTH
sip := getLong(index, p) sip := getLong(index, p)
if ip < sip { if ip < sip {
h = m - 1; h = m - 1
} else { } else {
eip := getLong(index, p + 4) eip := getLong(index, p+4)
if ip > eip { if ip > eip {
l = m + 1 l = m + 1
} else { } else {
dataptr = getLong(index, p + 8) dataptr = getLong(index, p+8)
break break
} }
} }
} }
if dataptr == 0 { if dataptr == 0 {
err = errors.New("not found") return ipInfo, errors.New("not found")
return
} }
dataLen := (dataptr >> 24) & 0xFF dataLen := (dataptr >> 24) & 0xFF
@ -322,28 +291,13 @@ func (this *Ip2Region) BtreeSearch(ipStr string) (ipInfo IpInfo, err error) {
} }
func getLong(b []byte, offset int64) int64 { func getLong(b []byte, offset int64) int64 {
return int64(b[offset]) | int64(b[offset+1])<<8 | int64(b[offset+2])<<16 | int64(b[offset+3])<<24
val := (int64(b[offset ]) |
int64(b[offset + 1]) << 8 |
int64(b[offset + 2]) << 16 |
int64(b[offset + 3]) << 24)
return val
} }
func ip2long(IpStr string) (int64, error) { func ip2long(ipStr string) (int64, error) {
bits := strings.Split(IpStr, ".") ipv4 := net.ParseIP(ipStr)
if len(bits) != 4 { if ipv4 == nil {
return 0, errors.New("ip format error") return 0, errors.New("ip format error")
} }
return int64(binary.BigEndian.Uint32(ipv4[12:])), nil
var sum int64
for i, n := range bits {
bit, _ := strconv.ParseInt(n, 10, 64)
sum += bit << uint(24 - 8 * i)
}
return sum, nil
} }

View File

@ -5,7 +5,7 @@ import (
) )
func BenchmarkBtreeSearch(B *testing.B) { func BenchmarkBtreeSearch(B *testing.B) {
region, err := New("../../data/ip2region.db ") region, err := New("../../../data/ip2region.db")
if err != nil { if err != nil {
B.Error(err) B.Error(err)
} }
@ -16,7 +16,7 @@ func BenchmarkBtreeSearch(B *testing.B) {
} }
func BenchmarkMemorySearch(B *testing.B) { func BenchmarkMemorySearch(B *testing.B) {
region, err := New("../../data/ip2region.db ") region, err := New("../../../data/ip2region.db")
if err != nil { if err != nil {
B.Error(err) B.Error(err)
} }
@ -27,7 +27,7 @@ func BenchmarkMemorySearch(B *testing.B) {
} }
func BenchmarkBinarySearch(B *testing.B) { func BenchmarkBinarySearch(B *testing.B) {
region, err := New("../../data/ip2region.db ") region, err := New("../../../data/ip2region.db")
if err != nil { if err != nil {
B.Error(err) B.Error(err)
} }