input ip type changed to any to supports string or bytes ip

This commit is contained in:
lionsoul2014 2026-04-04 18:42:12 +08:00
parent e1f0d5126a
commit 5cb17713a7
6 changed files with 53 additions and 40 deletions

View File

@ -39,8 +39,8 @@ if err != nil {
}
// 4. Export the ip2region service for concurrent dual-version IP address queries, for example:
v4Region, err := ip2region.SearchByStr("113.92.157.29") // Perform IPv4 query
v6Region, err := ip2region.SearchByStr("240e:3b7:3272:d8d0:db09:c067:8d59:539e") // Perform IPv6 query
v4Region, err := ip2region.Search("113.92.157.29") // Perform IPv4 query
v6Region, err := ip2region.Search("240e:3b7:3272:d8d0:db09:c067:8d59:539e") // Perform IPv6 query
// 5. When the parent service needs to be closed, close the ip2region query service as well
@ -60,8 +60,7 @@ ip2region.Close()
The location information query API prototypes are:
```go
SearchByStr(string) (string, error)
Search([]byte) (string, error)
Search(string | []byte) (string, error)
```
If a query fails, the `error` will contain specific error details. If successful, it returns the `region` string. If the specified IP cannot be found, it returns an empty string `""`.
@ -121,9 +120,9 @@ func main() {
var ip = "1.2.3.4" // IPv4
// ip = "240e:3b7:3272:d8d0:db09:c067:8d59:539e" // IPv6
var tStart = time.Now()
region, err := searcher.SearchByStr(ip)
region, err := searcher.Search(ip)
if err != nil {
fmt.Printf("failed to SearchIP(%s): %s\n", ip, err)
fmt.Printf("failed to Search(%s): %s\n", ip, err)
return
}

View File

@ -36,8 +36,8 @@ if err != nil {
}
// 4导出 ip2region 服务进行双版本的IP地址的并发查询例如
v4Region, err := ip2region.SearchByStr("113.92.157.29") // 进行 IPv4 查询
v6Region, err := ip2region.SearchByStr("240e:3b7:3272:d8d0:db09:c067:8d59:539e") // 进行 IPv6 查询
v4Region, err := ip2region.Search("113.92.157.29") // 进行 IPv4 查询
v6Region, err := ip2region.Search("240e:3b7:3272:d8d0:db09:c067:8d59:539e") // 进行 IPv6 查询
// 5在服务需要关闭的时候同时关闭 ip2region 查询服务
@ -55,8 +55,7 @@ ip2region.Close()
### 关于查询 API
定位信息查询 API 原型为:
```go
SearchByStr(string) (string, error)
Search([]byte) (string, error)
Search(string | []byte) (string, error)
```
查询出错则 error 会包含具体的错误信息,查询成功会返回字符串的 `region` 信息,如果指定的 IP 查询不到则会返回空字符串 `""`
@ -111,9 +110,9 @@ func main() {
var ip = "1.2.3.4" // IPv4
// ip = "240e:3b7:3272:d8d0:db09:c067:8d59:539e" // IPv6
var tStart = time.Now()
region, err := searcher.SearchByStr(ip)
region, err := searcher.Search(ip)
if err != nil {
fmt.Printf("failed to SearchIP(%s): %s\n", ip, err)
fmt.Printf("failed to Search(%s): %s\n", ip, err)
return
}

View File

@ -239,7 +239,7 @@ type 'quit' to exit
}
tStart := time.Now()
region, err := ip2region.SearchByStr(line)
region, err := ip2region.Search(line)
if err != nil {
fmt.Printf("\x1b[0;31m{err: %s}\x1b[0m\n", err.Error())
} else {
@ -339,7 +339,13 @@ func testBench() {
return
}
for _, ip := range [][]byte{sip, eip} {
mip, err := xdb.IPMiddle(sip, eip)
if err != nil {
fmt.Printf("IPMiddle(%s,%s): %s", xdb.IP2String(sip), xdb.IP2String(eip), err)
return
}
for _, ip := range [][]byte{sip, mip, eip} {
sTime := time.Now()
region, err := searcher.Search(ip)
if err != nil {

View File

@ -118,16 +118,21 @@ func NewIp2RegionWithPath(v4XdbPath string, v6XdbPath string) (*Ip2Region, error
return NewIp2Region(v4Config, v6Config)
}
func (ip2r *Ip2Region) SearchByStr(ipStr string) (string, error) {
ipBytes, err := xdb.ParseIP(ipStr)
if err != nil {
return "", err
func (ip2r *Ip2Region) Search(ip any) (string, error) {
var err error
var ipBytes []byte
switch v := ip.(type) {
case string:
ipBytes, err = xdb.ParseIP(v)
if err != nil {
return "", fmt.Errorf("parse ip %s: %w", v, err)
}
case []byte:
ipBytes = v
default:
return "", fmt.Errorf("invalid ip value type %s", v)
}
return ip2r.Search(ipBytes)
}
func (ip2r *Ip2Region) Search(ipBytes []byte) (string, error) {
if l := len(ipBytes); l == 4 {
return ip2r.v4Search(ipBytes)
} else if l == 16 {

View File

@ -23,7 +23,7 @@ func TestV4SearcherPool(t *testing.T) {
ipString := "219.133.110.197"
for i := 0; i < 20; i++ {
searcher := searcherPool.BorrowSearcher()
region, err := searcher.SearchByStr(ipString)
region, err := searcher.Search(ipString)
if err != nil {
t.Fatalf("failed to search(%s): %s", ipString, err)
}
@ -53,7 +53,7 @@ func TestV6SearcherPool(t *testing.T) {
ipString := "240e:3b7:3275:f090:d2a3:7d1a:dd90:c3b6"
for i := 0; i < 20; i++ {
searcher := searcherPool.BorrowSearcher()
region, err := searcher.SearchByStr(ipString)
region, err := searcher.Search(ipString)
if err != nil {
t.Fatalf("failed to search(%s): %s", ipString, err)
}

View File

@ -89,20 +89,24 @@ func (s *Searcher) GetIOCount() int {
return s.ioCount
}
// SearchByStr find the region for the specified ip string
func (s *Searcher) SearchByStr(str string) (string, error) {
ip, err := ParseIP(str)
if err != nil {
return "", err
// Search the region for the specified string or bytes ip address
func (s *Searcher) Search(ip any) (string, error) {
var err error
var ipBytes []byte
switch v := ip.(type) {
case string:
ipBytes, err = ParseIP(v)
if err != nil {
return "", fmt.Errorf("parse ip %s: %w", v, err)
}
case []byte:
ipBytes = v
default:
return "", fmt.Errorf("invalid ip value type %s", v)
}
return s.Search(ip)
}
// Search find the region for the specified long ip
func (s *Searcher) Search(ip []byte) (string, error) {
// ip version check
if len(ip) != s.version.Bytes {
if len(ipBytes) != s.version.Bytes {
return "", fmt.Errorf("invalid ip address(%s expected)", s.version.Name)
}
@ -110,7 +114,7 @@ func (s *Searcher) Search(ip []byte) (string, error) {
s.ioCount = 0
// locate the segment index block based on the vector index
var il0, il1 = int(ip[0]), int(ip[1])
var il0, il1 = int(ipBytes[0]), int(ipBytes[1])
var idx = il0*VectorIndexCols*VectorIndexSize + il1*VectorIndexSize
var sPtr, ePtr = uint32(0), uint32(0)
if s.vectorIndex != nil {
@ -139,7 +143,7 @@ func (s *Searcher) Search(ip []byte) (string, error) {
}
// binary search the segment index to get the region
var bytes, dBytes = len(ip), len(ip) << 1
var bytes, dBytes = len(ipBytes), len(ipBytes) << 1
var segIndexSize = uint32(s.version.SegmentIndexSize)
var dataLen, dataPtr = 0, uint32(0)
var buff = make([]byte, segIndexSize)
@ -153,9 +157,9 @@ func (s *Searcher) Search(ip []byte) (string, error) {
}
// decode the data step by step to reduce the unnecessary operations
if s.version.IPCompare(ip, buff[0:bytes]) < 0 {
if s.version.IPCompare(ipBytes, buff[0:bytes]) < 0 {
h = m - 1
} else if s.version.IPCompare(ip, buff[bytes:dBytes]) > 0 {
} else if s.version.IPCompare(ipBytes, buff[bytes:dBytes]) > 0 {
l = m + 1
} else {
dataLen = int(binary.LittleEndian.Uint16(buff[dBytes:]))
@ -171,7 +175,7 @@ func (s *Searcher) Search(ip []byte) (string, error) {
// load and return the region data
var regionBuff = make([]byte, dataLen)
err := s.read(int64(dataPtr), regionBuff)
err = s.read(int64(dataPtr), regionBuff)
if err != nil {
return "", fmt.Errorf("read region at %d: %w", dataPtr, err)
}