mirror of
https://github.com/lionsoul2014/ip2region.git
synced 2025-12-08 19:25:22 +00:00
version expected for xdb gen and auto version detect for search test
This commit is contained in:
parent
b830361c52
commit
c0058781c9
@ -6,6 +6,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
@ -23,7 +24,7 @@ func printHelp() {
|
|||||||
fmt.Printf("ip2region xdb maker\n")
|
fmt.Printf("ip2region xdb maker\n")
|
||||||
fmt.Printf("%s [command] [command options]\n", os.Args[0])
|
fmt.Printf("%s [command] [command options]\n", os.Args[0])
|
||||||
fmt.Printf("Command: \n")
|
fmt.Printf("Command: \n")
|
||||||
fmt.Printf(" gen generate the binary db file\n")
|
fmt.Printf(" gen generate the binary xdb file\n")
|
||||||
fmt.Printf(" search binary xdb search test\n")
|
fmt.Printf(" search binary xdb search test\n")
|
||||||
fmt.Printf(" bench binary xdb bench test\n")
|
fmt.Printf(" bench binary xdb bench test\n")
|
||||||
fmt.Printf(" edit edit the source ip data\n")
|
fmt.Printf(" edit edit the source ip data\n")
|
||||||
@ -152,7 +153,7 @@ func getFilterFields(fieldList string) ([]int, error) {
|
|||||||
func genDb() {
|
func genDb() {
|
||||||
var err error
|
var err error
|
||||||
var srcFile, dstFile = "", ""
|
var srcFile, dstFile = "", ""
|
||||||
var ipVersion, fieldList, logLevel = "ipv4", "", "info"
|
var ipVersion, fieldList, logLevel = "", "", "info"
|
||||||
var indexPolicy = xdb.VectorIndexPolicy
|
var indexPolicy = xdb.VectorIndexPolicy
|
||||||
var fErr = iterateFlags(func(key string, val string) error {
|
var fErr = iterateFlags(func(key string, val string) error {
|
||||||
switch key {
|
switch key {
|
||||||
@ -187,12 +188,23 @@ func genDb() {
|
|||||||
fmt.Printf("options:\n")
|
fmt.Printf("options:\n")
|
||||||
fmt.Printf(" --src string source ip text file path\n")
|
fmt.Printf(" --src string source ip text file path\n")
|
||||||
fmt.Printf(" --dst string destination binary xdb file path\n")
|
fmt.Printf(" --dst string destination binary xdb file path\n")
|
||||||
fmt.Printf(" --version string IP version, options: ipv4/ipv6, default to ipv4\n")
|
fmt.Printf(" --version string IP version, options: ipv4/ipv6, specify this flag so you don't get confused \n")
|
||||||
fmt.Printf(" --field-list string field index list imploded with ',' eg: 0,1,2,3-6,7\n")
|
fmt.Printf(" --field-list string field index list imploded with ',' eg: 0,1,2,3-6,7\n")
|
||||||
fmt.Printf(" --log-level string set the log level, options: debug/info/warn/error\n")
|
fmt.Printf(" --log-level string set the log level, options: debug/info/warn/error\n")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check and define the IP version
|
||||||
|
var version *xdb.Version = nil
|
||||||
|
if len(ipVersion) < 2 {
|
||||||
|
slog.Error("please specify the ip version with flag --version, ipv4 or ipv6 ?")
|
||||||
|
return
|
||||||
|
} else if v, err := xdb.VersionFromName(ipVersion); err != nil {
|
||||||
|
slog.Error("failed to parse version name", "error", err)
|
||||||
|
} else {
|
||||||
|
version = v
|
||||||
|
}
|
||||||
|
|
||||||
// check and apply the log level
|
// check and apply the log level
|
||||||
err = applyLogLevel(logLevel)
|
err = applyLogLevel(logLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -206,14 +218,6 @@ func genDb() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// check and define the IP version
|
|
||||||
var version *xdb.Version = nil
|
|
||||||
if v, err := xdb.VersionFromName(ipVersion); err != nil {
|
|
||||||
slog.Error("failed to parse version name", "error", err)
|
|
||||||
} else {
|
|
||||||
version = v
|
|
||||||
}
|
|
||||||
|
|
||||||
// make the binary file
|
// make the binary file
|
||||||
tStart := time.Now()
|
tStart := time.Now()
|
||||||
maker, err := xdb.NewMaker(version, indexPolicy, srcFile, dstFile, fields)
|
maker, err := xdb.NewMaker(version, indexPolicy, srcFile, dstFile, fields)
|
||||||
@ -245,12 +249,10 @@ func genDb() {
|
|||||||
|
|
||||||
func testSearch() {
|
func testSearch() {
|
||||||
var err error
|
var err error
|
||||||
var dbFile, ipVersion = "", "v4"
|
var dbFile = ""
|
||||||
var fErr = iterateFlags(func(key string, val string) error {
|
var fErr = iterateFlags(func(key string, val string) error {
|
||||||
if key == "db" {
|
if key == "db" {
|
||||||
dbFile = val
|
dbFile = val
|
||||||
} else if key == "version" {
|
|
||||||
ipVersion = val
|
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("undefined option '%s=%s'\n", key, val)
|
return fmt.Errorf("undefined option '%s=%s'\n", key, val)
|
||||||
}
|
}
|
||||||
@ -265,16 +267,34 @@ func testSearch() {
|
|||||||
fmt.Printf("%s search [command options]\n", os.Args[0])
|
fmt.Printf("%s search [command options]\n", os.Args[0])
|
||||||
fmt.Printf("options:\n")
|
fmt.Printf("options:\n")
|
||||||
fmt.Printf(" --db string ip2region binary xdb file path\n")
|
fmt.Printf(" --db string ip2region binary xdb file path\n")
|
||||||
fmt.Printf(" --version string IP version, options: ipv4/ipv6\n")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// check and parse the IP version
|
// detect the version from the xdb header
|
||||||
|
header, err := xdb.LoadXdbHeaderFromFile(dbFile)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("failed to load xdb header", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var version *xdb.Version = nil
|
var version *xdb.Version = nil
|
||||||
if v, err := xdb.VersionFromName(ipVersion); err != nil {
|
versionNo := binary.LittleEndian.Uint16(header[0:])
|
||||||
slog.Error("failed to parse version name", "error", err)
|
if versionNo == 2 {
|
||||||
|
// old xdb file
|
||||||
|
version = xdb.V4
|
||||||
|
} else if versionNo == 3 {
|
||||||
|
ipNo := int(binary.LittleEndian.Uint16(header[16:]))
|
||||||
|
if ipNo == xdb.V4.Id {
|
||||||
|
version = xdb.V4
|
||||||
|
} else if ipNo == xdb.V6.Id {
|
||||||
|
version = xdb.V6
|
||||||
|
} else {
|
||||||
|
slog.Error("invalid ip version", "id", ipNo)
|
||||||
|
return
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
version = v
|
slog.Error("invalid xdb version", "versionNo", versionNo, "xdbFile", dbFile)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
searcher, err := xdb.NewSearcher(version, dbFile)
|
searcher, err := xdb.NewSearcher(version, dbFile)
|
||||||
@ -287,10 +307,13 @@ func testSearch() {
|
|||||||
fmt.Printf("test program exited, thanks for trying\n")
|
fmt.Printf("test program exited, thanks for trying\n")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
fmt.Println(`ip2region xdb search test program, commands:
|
fmt.Printf(`ip2region xdb search test program,
|
||||||
loadIndex : load the vector index for search speedup.
|
source xdb: %s (%s)
|
||||||
clearIndex: clear the vector index.
|
commands:
|
||||||
quit : exit the test program`)
|
loadIndex : load the vector index for search speedup.
|
||||||
|
clearIndex: clear the vector index.
|
||||||
|
quit : exit the test program
|
||||||
|
`, dbFile, version.Name)
|
||||||
reader := bufio.NewReader(os.Stdin)
|
reader := bufio.NewReader(os.Stdin)
|
||||||
for {
|
for {
|
||||||
fmt.Print("ip2region>> ")
|
fmt.Print("ip2region>> ")
|
||||||
|
|||||||
@ -60,7 +60,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
VersionNo = 3
|
VersionNo = 3 // since 2025/09/01 (IPv6 supporting)
|
||||||
HeaderInfoLength = 256
|
HeaderInfoLength = 256
|
||||||
VectorIndexRows = 256
|
VectorIndexRows = 256
|
||||||
VectorIndexCols = 256
|
VectorIndexCols = 256
|
||||||
@ -183,6 +183,11 @@ func (m *Maker) loadSegments() error {
|
|||||||
var iErr = IterateSegments(m.srcHandle, func(l string) {
|
var iErr = IterateSegments(m.srcHandle, func(l string) {
|
||||||
slog.Debug("loaded", "segment", l)
|
slog.Debug("loaded", "segment", l)
|
||||||
}, func(seg *Segment) error {
|
}, func(seg *Segment) error {
|
||||||
|
// ip version check
|
||||||
|
if len(seg.StartIP) != m.version.Bytes {
|
||||||
|
return fmt.Errorf("invalid ip segment(%s expected)", m.version.Name)
|
||||||
|
}
|
||||||
|
|
||||||
// check the continuity of the data segment
|
// check the continuity of the data segment
|
||||||
if err := seg.AfterCheck(last); err != nil {
|
if err := seg.AfterCheck(last); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@ -90,6 +90,11 @@ func (s *Searcher) ClearVectorIndex() {
|
|||||||
|
|
||||||
// Search find the region for the specified ip address
|
// Search find the region for the specified ip address
|
||||||
func (s *Searcher) Search(ip []byte) (string, int, error) {
|
func (s *Searcher) Search(ip []byte) (string, int, error) {
|
||||||
|
// version check
|
||||||
|
if len(ip) != s.version.Bytes {
|
||||||
|
return "", 0, fmt.Errorf("invalid ip address(%s expected)", s.version.Name)
|
||||||
|
}
|
||||||
|
|
||||||
// locate the segment index block based on the vector index
|
// locate the segment index block based on the vector index
|
||||||
var ioCount = 0
|
var ioCount = 0
|
||||||
var il0, il1, bytes, tBytes = int(ip[0]), int(ip[1]), len(ip), len(ip) << 1
|
var il0, il1, bytes, tBytes = int(ip[0]), int(ip[1]), len(ip), len(ip) << 1
|
||||||
@ -180,3 +185,42 @@ func (s *Searcher) Search(ip []byte) (string, int, error) {
|
|||||||
|
|
||||||
return string(regionBuff), ioCount, nil
|
return string(regionBuff), ioCount, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadXdbHeader load the header info from the specified handle
|
||||||
|
func LoadXdbHeader(handle *os.File) ([]byte, error) {
|
||||||
|
_, err := handle.Seek(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("seek to the header: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var buff = make([]byte, HeaderInfoLength)
|
||||||
|
rLen, err := handle.Read(buff)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if rLen != len(buff) {
|
||||||
|
return nil, fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
|
||||||
|
}
|
||||||
|
|
||||||
|
return buff, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadXdbHeaderFromFile load header info from the specified db file path
|
||||||
|
func LoadXdbHeaderFromFile(dbFile string) ([]byte, error) {
|
||||||
|
handle, err := os.OpenFile(dbFile, os.O_RDONLY, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("open xdb file `%s`: %w", dbFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func(handle *os.File) {
|
||||||
|
_ = handle.Close()
|
||||||
|
}(handle)
|
||||||
|
|
||||||
|
header, err := LoadXdbHeader(handle)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return header, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -132,6 +132,10 @@ func IterateSegments(handle *os.File, before func(l string), cb func(seg *Segmen
|
|||||||
return fmt.Errorf("check end ip `%s`: %s", ps[1], err)
|
return fmt.Errorf("check end ip `%s`: %s", ps[1], err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(sip) != len(eip) {
|
||||||
|
return fmt.Errorf("invalid ip segment line `%s`, sip/eip version not match", l)
|
||||||
|
}
|
||||||
|
|
||||||
if IPCompare(sip, eip) > 0 {
|
if IPCompare(sip, eip) > 0 {
|
||||||
return fmt.Errorf("start ip(%s) should not be greater than end ip(%s)", ps[0], ps[1])
|
return fmt.Errorf("start ip(%s) should not be greater than end ip(%s)", ps[0], ps[1])
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,8 @@ import (
|
|||||||
|
|
||||||
type Version struct {
|
type Version struct {
|
||||||
Id int
|
Id int
|
||||||
|
Name string
|
||||||
|
Bytes int
|
||||||
SegmentIndexSize int
|
SegmentIndexSize int
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,15 +20,19 @@ var (
|
|||||||
VX = &Version{}
|
VX = &Version{}
|
||||||
V4 = &Version{
|
V4 = &Version{
|
||||||
Id: 4,
|
Id: 4,
|
||||||
|
Name: "IPv4",
|
||||||
|
Bytes: 4,
|
||||||
SegmentIndexSize: 14, // 4 + 4 + 2 + 4
|
SegmentIndexSize: 14, // 4 + 4 + 2 + 4
|
||||||
}
|
}
|
||||||
V6 = &Version{
|
V6 = &Version{
|
||||||
Id: 6,
|
Id: 6,
|
||||||
|
Name: "IPv6",
|
||||||
|
Bytes: 16,
|
||||||
SegmentIndexSize: 38, // 16 + 16 + 2 + 4
|
SegmentIndexSize: 38, // 16 + 16 + 2 + 4
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func VersionFromData(ip string) (*Version, error) {
|
func VersionFromIP(ip string) (*Version, error) {
|
||||||
bytes, err := ParseIP(ip)
|
bytes, err := ParseIP(ip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return VX, fmt.Errorf("parse ip fail: %w", err)
|
return VX, fmt.Errorf("parse ip fail: %w", err)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user