From 90e827633a99b703177ee1a76a01906f875ef2e8 Mon Sep 17 00:00:00 2001 From: lionsoul2014 Date: Wed, 29 Apr 2026 12:23:51 +0800 Subject: [PATCH 1/5] Use the Region struct to manager the region info instead of old string --- maker/golang/cmd/bench.go | 2 +- maker/golang/xdb/editor.go | 8 +-- maker/golang/xdb/maker.go | 10 ++-- maker/golang/xdb/region.go | 100 ++++++++++++++++++++++++++++++++++ maker/golang/xdb/segment.go | 4 +- maker/golang/xdb/util.go | 6 +- maker/golang/xdb/util_test.go | 2 +- 7 files changed, 116 insertions(+), 16 deletions(-) create mode 100644 maker/golang/xdb/region.go diff --git a/maker/golang/cmd/bench.go b/maker/golang/cmd/bench.go index 9bf763f..8500a9f 100644 --- a/maker/golang/cmd/bench.go +++ b/maker/golang/cmd/bench.go @@ -109,7 +109,7 @@ func Bench(sCmd string) { // check the region info count++ - if r != seg.Region { + if r != seg.Region.Str { errCount++ slog.Error(" --[Failed] region not match", "src", r, "dst", seg.Region) if !ignoreError { diff --git a/maker/golang/xdb/editor.go b/maker/golang/xdb/editor.go index c179048..7b15ee3 100644 --- a/maker/golang/xdb/editor.go +++ b/maker/golang/xdb/editor.go @@ -108,7 +108,7 @@ func (e *Editor) loadSegments() error { e.segments.PushBack(&Segment{ StartIP: e.verison.Min, EndIP: IPSubOne(seg.StartIP), - Region: "", + Region: REmpty(), }) } } else if err := seg.RightBehind(last); err == nil { @@ -121,7 +121,7 @@ func (e *Editor) loadSegments() error { e.segments.PushBack(&Segment{ StartIP: IPAddOne(last.EndIP), EndIP: IPSubOne(seg.StartIP), - Region: "", + Region: REmpty(), }) } @@ -138,7 +138,7 @@ func (e *Editor) loadSegments() error { e.segments.PushBack(&Segment{ StartIP: IPAddOne(back.Value.(*Segment).EndIP), EndIP: e.verison.Max, - Region: "", + Region: REmpty(), }) } } @@ -395,7 +395,7 @@ func (e *Editor) SaveToFile(dstFile string) error { } // ignore the padded or empty segment - if s.Region == "" { + if s.Region.IsEmpty() { continue } diff --git a/maker/golang/xdb/maker.go b/maker/golang/xdb/maker.go index bd4f56c..3aee6a3 100644 --- a/maker/golang/xdb/maker.go +++ b/maker/golang/xdb/maker.go @@ -276,13 +276,13 @@ func (m *Maker) Start() error { slog.Info("try to write the data block ... ") for _, seg := range m.segments { slog.Debug("try to write", "region", seg.Region) - ptr, has := m.regionPool[seg.Region] + ptr, has := m.regionPool[seg.Region.Str] if has { slog.Debug(" --[Cached]", "ptr=", ptr) continue } - var region = []byte(seg.Region) + var region = []byte(seg.Region.Str) if len(region) > 0xFFFF { return fmt.Errorf("too long region info `%s`: should be less than %d bytes", seg.Region, 0xFFFF) } @@ -303,7 +303,7 @@ func (m *Maker) Start() error { return fmt.Errorf("write region '%s': %w", seg.Region, err) } - m.regionPool[seg.Region] = uint32(pos) + m.regionPool[seg.Region.Str] = uint32(pos) slog.Debug(" --[Added] with", "ptr", pos) } @@ -312,14 +312,14 @@ func (m *Maker) Start() error { var indexBuff = make([]byte, m.version.SegmentIndexSize) var counter, startIndexPtr, endIndexPtr = 0, int64(-1), int64(-1) for _, seg := range m.segments { - dataPtr, has := m.regionPool[seg.Region] + dataPtr, has := m.regionPool[seg.Region.Str] if !has { return fmt.Errorf("missing ptr cache for region `%s`", seg.Region) } // @Note: data length should be the length of bytes. // this works fine because of the string feature (byte sequence) of golang. - var dataLen = len(seg.Region) + var dataLen = len(seg.Region.Str) if dataLen < 1 { // @TODO: could this even be a case ? // return fmt.Errorf("empty region info for segment '%s'", seg) diff --git a/maker/golang/xdb/region.go b/maker/golang/xdb/region.go new file mode 100644 index 0000000..874a26f --- /dev/null +++ b/maker/golang/xdb/region.go @@ -0,0 +1,100 @@ +package xdb + +import ( + "fmt" + "strings" +) + +// region manager with: +// 1, content cache. +// 2, util functions + +type Region struct { + Str string // region string + fields []string // region fields +} + +// global cache map +var regionCache = map[string]*Region{} + +func RNew(str string) *Region { + return NewRegion(str) +} + +func REmpty() *Region { + return NewRegion("") +} + +func NewRegion(str string) *Region { + // check the cache and return it directly + // if there is a cache available + region, ok := regionCache[str] + if ok { + return region + } + + // cache the new region + region = &Region{ + Str: str, + fields: nil, + } + + regionCache[str] = region + return region +} + +func (r *Region) Fields() []string { + if r.fields == nil { + r.fields = strings.Split(r.Str, "|") + } + + return r.fields +} + +func (r *Region) JoinBy(sep string) string { + if sep == "|" { + return r.Str + } + + return strings.Join(r.Fields(), sep) +} + +func (r *Region) Filtering(fields []int) (*Region, error) { + if len(fields) == 0 { + return r, nil + } + + fs := r.Fields() + var sb []string + for _, idx := range fields { + if idx < 0 { + return r, fmt.Errorf("negative filter index %d", idx) + } + + if idx >= len(fs) { + return r, fmt.Errorf("field index %d exceeded the max length of %d", idx, len(fs)) + } + + sb = append(sb, fs[idx]) + } + + new := RNew(strings.Join(sb, "|")) + if new.fields == nil { + new.fields = sb + } + + return new, nil +} + +// Equal check ptr (share the same region cache) or the Str is the same. +func (r *Region) Equal(dst *Region) bool { + return (r == dst || r.Str == dst.Str) +} + +func (r *Region) IsEmpty() bool { + return r.Str == "" +} + +func (r *Region) String() string { + return r.Str +} diff --git a/maker/golang/xdb/segment.go b/maker/golang/xdb/segment.go index 6a5a995..e123035 100644 --- a/maker/golang/xdb/segment.go +++ b/maker/golang/xdb/segment.go @@ -12,7 +12,7 @@ import ( type Segment struct { StartIP []byte EndIP []byte - Region string + Region *Region } func SegmentFrom(seg string) (*Segment, error) { @@ -38,7 +38,7 @@ func SegmentFrom(seg string) (*Segment, error) { return &Segment{ StartIP: sip, EndIP: eip, - Region: ps[2], + Region: RNew(ps[2]), }, nil } diff --git a/maker/golang/xdb/util.go b/maker/golang/xdb/util.go index c28359a..9c3eaf4 100644 --- a/maker/golang/xdb/util.go +++ b/maker/golang/xdb/util.go @@ -200,7 +200,7 @@ func IterateSegments(handle *os.File, autoMerge bool, before func(l string), fil var seg = &Segment{ StartIP: sip, EndIP: eip, - Region: region, + Region: RNew(region), } // check and automatic merging the Consecutive Segments, which means: @@ -209,7 +209,7 @@ func IterateSegments(handle *os.File, autoMerge bool, before func(l string), fil if last == nil { last = seg continue - } else if autoMerge && last.Region == seg.Region { + } else if autoMerge && last.Region.Equal(seg.Region) { if err = seg.RightBehind(last); err == nil { mergeCount++ last.EndIP = seg.EndIP @@ -266,7 +266,7 @@ func MergeSegments(segList []*Segment) []*Segment { if last == nil { last = seg continue - } else if last.Region == seg.Region { + } else if last.Region.Equal(seg.Region) { if err = seg.RightBehind(last); err == nil { last.EndIP = seg.EndIP continue diff --git a/maker/golang/xdb/util_test.go b/maker/golang/xdb/util_test.go index f428974..e7a151a 100644 --- a/maker/golang/xdb/util_test.go +++ b/maker/golang/xdb/util_test.go @@ -225,7 +225,7 @@ func TestRegionFiltering(t *testing.T) { t.Fatalf("failed to parse segment '%s': %s", line, err) } - fReg, err := RegionFiltering(seg.Region, []int{1, 2, 4, 6}) + fReg, err := seg.Region.Filtering([]int{1, 2, 4, 6}) if err != nil { t.Fatalf("failed to filter region '%s': %s", seg.Region, err) } From 5b10989ac21087bf6c319f719b85af5b7522293f Mon Sep 17 00:00:00 2001 From: lionsoul2014 Date: Wed, 29 Apr 2026 13:44:14 +0800 Subject: [PATCH 2/5] Thread-safe impl for CacheRegion --- maker/golang/xdb/editor.go | 6 +++--- maker/golang/xdb/region.go | 35 ++++++++++++++++++++++------------- maker/golang/xdb/segment.go | 2 +- maker/golang/xdb/util.go | 2 +- 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/maker/golang/xdb/editor.go b/maker/golang/xdb/editor.go index 7b15ee3..4ffbbb2 100644 --- a/maker/golang/xdb/editor.go +++ b/maker/golang/xdb/editor.go @@ -108,7 +108,7 @@ func (e *Editor) loadSegments() error { e.segments.PushBack(&Segment{ StartIP: e.verison.Min, EndIP: IPSubOne(seg.StartIP), - Region: REmpty(), + Region: EmptyRegion, }) } } else if err := seg.RightBehind(last); err == nil { @@ -121,7 +121,7 @@ func (e *Editor) loadSegments() error { e.segments.PushBack(&Segment{ StartIP: IPAddOne(last.EndIP), EndIP: IPSubOne(seg.StartIP), - Region: REmpty(), + Region: EmptyRegion, }) } @@ -138,7 +138,7 @@ func (e *Editor) loadSegments() error { e.segments.PushBack(&Segment{ StartIP: IPAddOne(back.Value.(*Segment).EndIP), EndIP: e.verison.Max, - Region: REmpty(), + Region: EmptyRegion, }) } } diff --git a/maker/golang/xdb/region.go b/maker/golang/xdb/region.go index 874a26f..e011ea4 100644 --- a/maker/golang/xdb/region.go +++ b/maker/golang/xdb/region.go @@ -3,31 +3,32 @@ package xdb import ( "fmt" "strings" + "sync" ) // region manager with: // 1, content cache. // 2, util functions +// global cache map +var rcLock sync.Mutex +var regionCache = map[string]*Region{} + type Region struct { Str string // region string fields []string // region fields } -// global cache map -var regionCache = map[string]*Region{} +var EmptyRegion = CacheRegion("") -func RNew(str string) *Region { - return NewRegion(str) -} - -func REmpty() *Region { - return NewRegion("") -} - -func NewRegion(str string) *Region { +// Create or get the region from the global cache. +// And it is a thread-safe implementation. +func CacheRegion(str string) *Region { // check the cache and return it directly // if there is a cache available + rcLock.Lock() + defer rcLock.Unlock() + region, ok := regionCache[str] if ok { return region @@ -43,6 +44,14 @@ func NewRegion(str string) *Region { return region } +// Create a new region without checking cache info +func NewRegion(str string) *Region { + return &Region{ + Str: str, + fields: nil, + } +} + func (r *Region) Fields() []string { if r.fields == nil { r.fields = strings.Split(r.Str, "|") @@ -51,7 +60,7 @@ func (r *Region) Fields() []string { return r.fields } -func (r *Region) JoinBy(sep string) string { +func (r *Region) Join(sep string) string { if sep == "|" { return r.Str } @@ -78,7 +87,7 @@ func (r *Region) Filtering(fields []int) (*Region, error) { sb = append(sb, fs[idx]) } - new := RNew(strings.Join(sb, "|")) + new := CacheRegion(strings.Join(sb, "|")) if new.fields == nil { new.fields = sb } diff --git a/maker/golang/xdb/segment.go b/maker/golang/xdb/segment.go index e123035..179cc88 100644 --- a/maker/golang/xdb/segment.go +++ b/maker/golang/xdb/segment.go @@ -38,7 +38,7 @@ func SegmentFrom(seg string) (*Segment, error) { return &Segment{ StartIP: sip, EndIP: eip, - Region: RNew(ps[2]), + Region: CacheRegion(ps[2]), }, nil } diff --git a/maker/golang/xdb/util.go b/maker/golang/xdb/util.go index 9c3eaf4..6e8fb58 100644 --- a/maker/golang/xdb/util.go +++ b/maker/golang/xdb/util.go @@ -200,7 +200,7 @@ func IterateSegments(handle *os.File, autoMerge bool, before func(l string), fil var seg = &Segment{ StartIP: sip, EndIP: eip, - Region: RNew(region), + Region: CacheRegion(region), } // check and automatic merging the Consecutive Segments, which means: From 787bd7ae070e9759b9b26ee35d2939fffcec4d08 Mon Sep 17 00:00:00 2001 From: lionsoul2014 Date: Wed, 29 Apr 2026 15:52:40 +0800 Subject: [PATCH 3/5] use the Region cache --- maker/golang/cmd/bench.go | 6 ++- maker/golang/xdb/editor.go | 13 +++--- maker/golang/xdb/maker.go | 4 +- maker/golang/xdb/processor.go | 6 ++- maker/golang/xdb/region.go | 86 +++++++++++++++++++++-------------- maker/golang/xdb/segment.go | 4 +- maker/golang/xdb/util.go | 6 +-- maker/golang/xdb/util_test.go | 8 ++-- 8 files changed, 81 insertions(+), 52 deletions(-) diff --git a/maker/golang/cmd/bench.go b/maker/golang/cmd/bench.go index 8500a9f..a008a72 100644 --- a/maker/golang/cmd/bench.go +++ b/maker/golang/cmd/bench.go @@ -90,12 +90,14 @@ func Bench(sCmd string) { fmt.Printf("failed to open source text file: %s\n", err) return } - defer handle.Close() + var rgCache = xdb.NewRegionCache() var count, errCount, tStart = 0, 0, time.Now() slog.Info("Bench start", "xdbPath", dbFile, "srcPath", srcFile) - _, _, iErr := xdb.IterateSegments(handle, false, nil, nil, func(seg *xdb.Segment) error { + _, _, iErr := xdb.IterateSegments(handle, false, func(l string) { + // do thing here + }, nil, rgCache.Region, func(seg *xdb.Segment) error { var l = fmt.Sprintf("%d|%d|%s", seg.StartIP, seg.EndIP, seg.Region) slog.Debug("try to bench", "segment", l) // mip := xdb.IPMiddle(seg.StartIP, seg.EndIP) diff --git a/maker/golang/xdb/editor.go b/maker/golang/xdb/editor.go index 4ffbbb2..e1bc7d7 100644 --- a/maker/golang/xdb/editor.go +++ b/maker/golang/xdb/editor.go @@ -24,6 +24,9 @@ type Editor struct { // segments list segments *list.List + + // region cache + rgCache *RegionCache } func NewEditor(version *Version, srcFile string) (*Editor, error) { @@ -44,6 +47,7 @@ func NewEditor(version *Version, srcFile string) (*Editor, error) { srcHandle: srcHandle, toSave: false, segments: list.New(), + rgCache: NewRegionCache(), } // load the segments @@ -62,7 +66,7 @@ func (e *Editor) loadSegments() error { _, _, iErr := IterateSegments(e.srcHandle, true, func(l string) { // do nothing here - }, nil, func(seg *Segment) error { + }, nil, e.rgCache.Region, func(seg *Segment) error { // version check if len(seg.StartIP) != e.verison.Bytes { return fmt.Errorf("invalid ip segment(%s expected)", e.verison.Name) @@ -100,9 +104,6 @@ func (e *Editor) loadSegments() error { // to Keep the entire data continuous. last = nil for _, seg := range segments { - if err := seg.After(last); err != nil { - } - if last == nil { if IPCompare(seg.StartIP, e.verison.Min) > 0 { e.segments.PushBack(&Segment{ @@ -182,7 +183,7 @@ func (e *Editor) Slice(offset int, size int) []*Segment { } func (e *Editor) Put(ip string, cb func(newSeg *Segment, oldList []*Segment) []*Segment) (int, int, error) { - seg, err := SegmentFrom(ip) + seg, err := SegmentFrom(ip, e.rgCache.Region) if err != nil { return 0, 0, err } @@ -350,7 +351,7 @@ func (e *Editor) PutFile(src string, cb func(newSeg *Segment, oldList []*Segment var oldRows, newRows = 0, 0 _, _, iErr := IterateSegments(handle, true, func(l string) { // do nothing here - }, nil, func(seg *Segment) error { + }, nil, NewRegion, func(seg *Segment) error { o, n, err := e.PutSegment(seg, cb) if err == nil { oldRows += o diff --git a/maker/golang/xdb/maker.go b/maker/golang/xdb/maker.go index 3aee6a3..a9aea1c 100644 --- a/maker/golang/xdb/maker.go +++ b/maker/golang/xdb/maker.go @@ -84,6 +84,7 @@ type Maker struct { indexPolicy IndexPolicy segments []*Segment regionPool map[string]uint32 + regionCache *RegionCache vectorIndex []byte } @@ -118,6 +119,7 @@ func NewMaker(version *Version, policy IndexPolicy, srcFile string, dstFile stri indexPolicy: policy, segments: []*Segment{}, regionPool: map[string]uint32{}, + regionCache: NewRegionCache(), vectorIndex: make([]byte, VectorIndexLength), }, nil } @@ -173,7 +175,7 @@ func (m *Maker) loadSegments() error { }, func(region string) (string, error) { // apply the field filter return RegionFiltering(region, m.fields) - }, func(seg *Segment) error { + }, m.regionCache.Region, 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) diff --git a/maker/golang/xdb/processor.go b/maker/golang/xdb/processor.go index e791d0f..af2f20e 100644 --- a/maker/golang/xdb/processor.go +++ b/maker/golang/xdb/processor.go @@ -26,6 +26,9 @@ type Processor struct { fields []int segments []*Segment + + // region cache + rgCache *RegionCache } func NewProcessor(srcFile string, dstFile string, fields []int, @@ -55,6 +58,7 @@ func NewProcessor(srcFile string, dstFile string, fields []int, fields: fields, segments: []*Segment{}, + rgCache: NewRegionCache(), }, nil } @@ -94,7 +98,7 @@ func (p *Processor) loadSegments() error { } return RegionFiltering(region, p.fields) - }, func(seg *Segment) error { + }, p.rgCache.Region, func(seg *Segment) error { // check the continuity of the data segment // if err := seg.AfterCheck(last); err != nil { // return err diff --git a/maker/golang/xdb/region.go b/maker/golang/xdb/region.go index e011ea4..88e6579 100644 --- a/maker/golang/xdb/region.go +++ b/maker/golang/xdb/region.go @@ -10,39 +10,14 @@ import ( // 1, content cache. // 2, util functions -// global cache map -var rcLock sync.Mutex -var regionCache = map[string]*Region{} +// --- region type Region struct { Str string // region string fields []string // region fields } -var EmptyRegion = CacheRegion("") - -// Create or get the region from the global cache. -// And it is a thread-safe implementation. -func CacheRegion(str string) *Region { - // check the cache and return it directly - // if there is a cache available - rcLock.Lock() - defer rcLock.Unlock() - - region, ok := regionCache[str] - if ok { - return region - } - - // cache the new region - region = &Region{ - Str: str, - fields: nil, - } - - regionCache[str] = region - return region -} +var EmptyRegion = NewRegion("") // Create a new region without checking cache info func NewRegion(str string) *Region { @@ -87,12 +62,10 @@ func (r *Region) Filtering(fields []int) (*Region, error) { sb = append(sb, fs[idx]) } - new := CacheRegion(strings.Join(sb, "|")) - if new.fields == nil { - new.fields = sb - } - - return new, nil + return &Region{ + Str: strings.Join(sb, "|"), + fields: sb, + }, nil } // Equal check ptr (share the same region cache) or the Str is the same. @@ -107,3 +80,50 @@ func (r *Region) IsEmpty() bool { func (r *Region) String() string { return r.Str } + +// --- +// --- region cache + +type RegionCache struct { + lock sync.Mutex + cache map[string]*Region +} + +func NewRegionCache() *RegionCache { + return &RegionCache{ + lock: sync.Mutex{}, + cache: make(map[string]*Region), + } +} + +func (rc *RegionCache) Region(str string) *Region { + rc.lock.Lock() + defer rc.lock.Unlock() + + region, ok := rc.cache[str] + if ok { + return region + } + + // cache the new region + region = &Region{ + Str: str, + fields: nil, + } + + rc.cache[str] = region + return region +} + +func (rc *RegionCache) Swap(r *Region) *Region { + return rc.Region(r.Str) +} + +func (rc *RegionCache) Clean() { + rc.lock.Lock() + defer rc.lock.Unlock() + + for k := range rc.cache { + delete(rc.cache, k) + } +} diff --git a/maker/golang/xdb/segment.go b/maker/golang/xdb/segment.go index 179cc88..831bdf9 100644 --- a/maker/golang/xdb/segment.go +++ b/maker/golang/xdb/segment.go @@ -15,7 +15,7 @@ type Segment struct { Region *Region } -func SegmentFrom(seg string) (*Segment, error) { +func SegmentFrom(seg string, cRegion func(string) *Region) (*Segment, error) { var ps = strings.SplitN(strings.TrimSpace(seg), "|", 3) if len(ps) != 3 { return nil, fmt.Errorf("invalid ip segment `%s`", seg) @@ -38,7 +38,7 @@ func SegmentFrom(seg string) (*Segment, error) { return &Segment{ StartIP: sip, EndIP: eip, - Region: CacheRegion(ps[2]), + Region: cRegion(ps[2]), }, nil } diff --git a/maker/golang/xdb/util.go b/maker/golang/xdb/util.go index 6e8fb58..4d394ea 100644 --- a/maker/golang/xdb/util.go +++ b/maker/golang/xdb/util.go @@ -140,7 +140,7 @@ func IPMiddle(sip, eip []byte) ([]byte, error) { return IPHalf(buf), nil } -func IterateSegments(handle *os.File, autoMerge bool, before func(l string), filter func(region string) (string, error), done func(seg *Segment) error) (int, int, error) { +func IterateSegments(handle *os.File, autoMerge bool, before func(l string), filter func(region string) (string, error), cRegion func(string) *Region, done func(seg *Segment) error) (int, int, error) { var last *Segment = nil var totalCount, mergeCount = 0, 0 var scanner = bufio.NewScanner(handle) @@ -193,14 +193,14 @@ func IterateSegments(handle *os.File, autoMerge bool, before func(l string), fil if filter != nil { region, err = filter(ps[2]) if err != nil { - return totalCount, mergeCount, fmt.Errorf("failed to filter region `%s`: %s", ps[2], err) + return totalCount, mergeCount, fmt.Errorf("failed to filter region `%s`: %s", region, err) } } var seg = &Segment{ StartIP: sip, EndIP: eip, - Region: CacheRegion(region), + Region: cRegion(region), } // check and automatic merging the Consecutive Segments, which means: diff --git a/maker/golang/xdb/util_test.go b/maker/golang/xdb/util_test.go index e7a151a..7f5b8cb 100644 --- a/maker/golang/xdb/util_test.go +++ b/maker/golang/xdb/util_test.go @@ -201,7 +201,7 @@ func TestSplitSegmentV4(t *testing.T) { // var str = "0.0.0.0|1.255.225.254|0|0|0|内网IP|内网IP" // var str = "29.0.0.0|29.34.191.255|美国|0|0|0|0" var str = "28.201.224.0|29.34.191.255|美国|0|0|0|0" - seg, err := SegmentFrom(str) + seg, err := SegmentFrom(str, NewRegion) if err != nil { t.Fatalf("failed to parser segment '%s': %s", str, err) } @@ -220,7 +220,7 @@ func TestSplitSegmentV4(t *testing.T) { func TestRegionFiltering(t *testing.T) { var line = "2001:1203:31:8000::|2001:1203:31:bfff:ffff:ffff:ffff:ffff||墨西哥|瓜纳华托州||||专线用户|" - seg, err := SegmentFrom(line) + seg, err := SegmentFrom(line, NewRegion) if err != nil { t.Fatalf("failed to parse segment '%s': %s", line, err) } @@ -235,7 +235,7 @@ func TestRegionFiltering(t *testing.T) { func TestSplitSegmentV6(t *testing.T) { var str = "fec0::|ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff||瑞士|弗里堡州||||专线用户|IANA" - seg, err := SegmentFrom(str) + seg, err := SegmentFrom(str, NewRegion) if err != nil { t.Fatalf("failed to parser segment '%s': %s", str, err) } @@ -260,7 +260,7 @@ func TestIterateSegments(t *testing.T) { _, _, _ = IterateSegments(handle, true, func(l string) { // fmt.Printf("load segment: `%s`\n", l) - }, nil, func(seg *Segment) error { + }, nil, NewRegion, func(seg *Segment) error { fmt.Printf("get segment: `%s`\n", seg) return nil }) From 19a8816827a0d7f43ae93bf98a67476a0f844eb3 Mon Sep 17 00:00:00 2001 From: lionsoul2014 Date: Wed, 29 Apr 2026 16:04:20 +0800 Subject: [PATCH 4/5] clean the region cache --- maker/golang/cmd/bench.go | 2 ++ maker/golang/xdb/editor.go | 1 + maker/golang/xdb/maker.go | 2 ++ maker/golang/xdb/processor.go | 2 ++ 4 files changed, 7 insertions(+) diff --git a/maker/golang/cmd/bench.go b/maker/golang/cmd/bench.go index a008a72..851bfd5 100644 --- a/maker/golang/cmd/bench.go +++ b/maker/golang/cmd/bench.go @@ -93,6 +93,8 @@ func Bench(sCmd string) { defer handle.Close() var rgCache = xdb.NewRegionCache() + defer rgCache.Clean() + var count, errCount, tStart = 0, 0, time.Now() slog.Info("Bench start", "xdbPath", dbFile, "srcPath", srcFile) _, _, iErr := xdb.IterateSegments(handle, false, func(l string) { diff --git a/maker/golang/xdb/editor.go b/maker/golang/xdb/editor.go index e1bc7d7..959a807 100644 --- a/maker/golang/xdb/editor.go +++ b/maker/golang/xdb/editor.go @@ -416,4 +416,5 @@ func (e *Editor) SaveToFile(dstFile string) error { func (e *Editor) Close() { _ = e.srcHandle.Close() + e.rgCache.Clean() } diff --git a/maker/golang/xdb/maker.go b/maker/golang/xdb/maker.go index a9aea1c..5772aa2 100644 --- a/maker/golang/xdb/maker.go +++ b/maker/golang/xdb/maker.go @@ -414,5 +414,7 @@ func (m *Maker) End() error { return err } + m.regionCache.Clean() + return nil } diff --git a/maker/golang/xdb/processor.go b/maker/golang/xdb/processor.go index af2f20e..386b04b 100644 --- a/maker/golang/xdb/processor.go +++ b/maker/golang/xdb/processor.go @@ -156,5 +156,7 @@ func (p *Processor) End() error { return err } + p.rgCache.Clean() + return nil } From 72c657c3782bd408408541bc4a5aeae57cf99257 Mon Sep 17 00:00:00 2001 From: lionsoul2014 Date: Wed, 29 Apr 2026 17:36:30 +0800 Subject: [PATCH 5/5] export the Region API for callbacks --- maker/golang/xdb/editor.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/maker/golang/xdb/editor.go b/maker/golang/xdb/editor.go index 959a807..fc5de39 100644 --- a/maker/golang/xdb/editor.go +++ b/maker/golang/xdb/editor.go @@ -58,6 +58,10 @@ func NewEditor(version *Version, srcFile string) (*Editor, error) { return e, nil } +func (e *Editor) Region(str string) *Region { + return e.rgCache.Region(str) +} + // Load all the segments from the source file func (e *Editor) loadSegments() error { var last *Segment = nil @@ -66,7 +70,7 @@ func (e *Editor) loadSegments() error { _, _, iErr := IterateSegments(e.srcHandle, true, func(l string) { // do nothing here - }, nil, e.rgCache.Region, func(seg *Segment) error { + }, nil, e.Region, func(seg *Segment) error { // version check if len(seg.StartIP) != e.verison.Bytes { return fmt.Errorf("invalid ip segment(%s expected)", e.verison.Name)