mirror of
https://github.com/lionsoul2014/ip2region.git
synced 2025-12-08 19:25:22 +00:00
146 lines
3.0 KiB
Go
146 lines
3.0 KiB
Go
// Copyright 2022 The Ip2Region Authors. All rights reserved.
|
|
// Use of this source code is governed by a Apache2.0-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package xdb
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// Util function
|
|
|
|
var shiftIndex = []int{24, 16, 8, 0}
|
|
|
|
func CheckIP(ip string) (uint32, error) {
|
|
var ps = strings.Split(ip, ".")
|
|
if len(ps) != 4 {
|
|
return 0, fmt.Errorf("invalid ip address `%s`", ip)
|
|
}
|
|
|
|
var val uint32
|
|
for i, s := range ps {
|
|
d, err := strconv.Atoi(s)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("the %dth part `%s` is not an integer", i, s)
|
|
}
|
|
|
|
if d < 0 || d > 255 {
|
|
return 0, fmt.Errorf("the %dth part `%s` should be an integer bettween 0 and 255", i, s)
|
|
}
|
|
|
|
val |= uint32(d) << shiftIndex[i]
|
|
}
|
|
|
|
return val, nil
|
|
}
|
|
|
|
func Long2IP(ip uint32) string {
|
|
return fmt.Sprintf("%d.%d.%d.%d", (ip>>24)&0xFF, (ip>>16)&0xFF, (ip>>8)&0xFF, (ip>>0)&0xFF)
|
|
}
|
|
|
|
func MidIP(sip uint32, eip uint32) uint32 {
|
|
return uint32((uint64(sip) + uint64(eip)) >> 1)
|
|
}
|
|
|
|
func IterateSegments(handle *os.File, before func(l string), cb func(seg *Segment) error) error {
|
|
var last *Segment = nil
|
|
var scanner = bufio.NewScanner(handle)
|
|
scanner.Split(bufio.ScanLines)
|
|
for scanner.Scan() {
|
|
var l = strings.TrimSpace(strings.TrimSuffix(scanner.Text(), "\n"))
|
|
if len(l) < 1 { // ignore empty line
|
|
continue
|
|
}
|
|
|
|
if l[0] == '#' { // ignore the comment line
|
|
continue
|
|
}
|
|
|
|
if before != nil {
|
|
before(l)
|
|
}
|
|
|
|
var ps = strings.SplitN(l, "|", 3)
|
|
if len(ps) != 3 {
|
|
return fmt.Errorf("invalid ip segment line `%s`", l)
|
|
}
|
|
|
|
sip, err := CheckIP(ps[0])
|
|
if err != nil {
|
|
return fmt.Errorf("check start ip `%s`: %s", ps[0], err)
|
|
}
|
|
|
|
eip, err := CheckIP(ps[1])
|
|
if err != nil {
|
|
return fmt.Errorf("check end ip `%s`: %s", ps[1], err)
|
|
}
|
|
|
|
if sip > eip {
|
|
return fmt.Errorf("start ip(%s) should not be greater than end ip(%s)", ps[0], ps[1])
|
|
}
|
|
|
|
if len(ps[2]) < 1 {
|
|
return fmt.Errorf("empty region info in segment line `%s`", l)
|
|
}
|
|
|
|
var seg = &Segment{
|
|
StartIP: sip,
|
|
EndIP: eip,
|
|
Region: ps[2],
|
|
}
|
|
|
|
// check and automatic merging the Consecutive Segments which means:
|
|
// 1, region info is the same
|
|
// 2, last.eip+1 = cur.sip
|
|
if last == nil {
|
|
last = seg
|
|
continue
|
|
} else if last.Region == seg.Region {
|
|
if err = seg.AfterCheck(last); err == nil {
|
|
last.EndIP = seg.EndIP
|
|
continue
|
|
}
|
|
}
|
|
|
|
if err = cb(last); err != nil {
|
|
return err
|
|
}
|
|
|
|
// reset the last
|
|
last = seg
|
|
}
|
|
|
|
// process the last segment
|
|
if last != nil {
|
|
return cb(last)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func CheckSegments(segList []*Segment) error {
|
|
var last *Segment
|
|
for _, seg := range segList {
|
|
// sip must <= eip
|
|
if seg.StartIP > seg.EndIP {
|
|
return fmt.Errorf("segment `%s`: start ip should not be greater than end ip", seg.String())
|
|
}
|
|
|
|
// check the continuity of the data segment
|
|
if last != nil {
|
|
if last.EndIP+1 != seg.StartIP {
|
|
return fmt.Errorf("discontinuous segment `%s`: last.eip+1 != cur.sip", seg.String())
|
|
}
|
|
}
|
|
|
|
last = seg
|
|
}
|
|
|
|
return nil
|
|
}
|