You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

570 lines
12 KiB
Go

package gtid
import (
"errors"
"fmt"
"sort"
"strconv"
"strings"
)
func Parse(gtidDesc string) (*Gtid, error) {
return parse(gtidDesc)
}
func parse(gtidDesc string) (*Gtid, error) {
var gtid Gtid
gtid.hashMap = make(map[string]int)
gtidDesc = strings.TrimSpace(gtidDesc)
if "" == gtidDesc {
return &gtid, nil
}
return &gtid, gtid.uniform(gtidDesc)
}
func (g *Gtid) uniform(gtidDesc string) error {
hashMap := make(map[string][]string)
gtidlists := strings.Split(gtidDesc, ",")
uuidLists := make([]string, 0, len(gtidlists))
for _, v := range gtidlists {
if v == "" {
continue
}
numbers := strings.Split(v, ":")
if len(numbers) < 2 {
return fmt.Errorf("invalid format:%v", v)
}
uuid, err := g.isValidUUID(numbers[0])
if err != nil {
return err
}
numbers = numbers[1:]
existsNums, ok := hashMap[uuid]
if ok {
for _, v := range existsNums {
numbers = append(numbers, v)
}
hashMap[uuid] = numbers
continue
}
hashMap[uuid] = numbers
uuidLists = append(uuidLists, uuid)
}
sort.Strings(uuidLists)
g.uuidNumbers = make([]UUIDNumber, 0, len(uuidLists))
for k, v := range uuidLists {
number, err := g.uniformNumber(hashMap[v])
if err != nil {
return err
}
number, err = g.uniformRange(number)
if err != nil {
return err
}
g.mu.Lock()
g.hashMap[v] = k
g.mu.Unlock()
g.uuidNumbers = append(g.uuidNumbers, UUIDNumber{
uuid: v,
intervals: number,
})
}
return nil
}
func (g *Gtid) reform() {
sort.Sort(SortUUID(g.uuidNumbers))
g.hashMap = make(map[string]int, len(g.uuidNumbers))
for k, v := range g.uuidNumbers {
g.uuidNumbers[k].intervals, _ = g.uniformRange(v.intervals)
g.hashMap[v.uuid] = k
}
}
func (g *Gtid) uniformRange(rg []uuidRange) ([]uuidRange, error) {
newRg := make([]uuidRange, 0, len(rg))
sort.Sort(SortRange(rg))
var last *uuidRange = nil
for _, v := range rg {
if last != nil && v.min <= last.max+1 {
if last.max <= v.max {
last.max = v.max
}
continue
}
newRg = append(newRg, v)
last = &newRg[len(newRg)-1]
}
return newRg, nil
}
func subRange(origin, rg []uuidRange) []uuidRange {
newRg := make([]uuidRange, 0)
sort.Sort(SortRange(rg))
sort.Sort(SortRange(origin))
for i := 0; i < len(origin); i++ {
ori := origin[i]
res := make([]uuidRange, 0)
shouldAdd := true
for _, v := range rg {
if v.min <= ori.min && v.max >= ori.max {
shouldAdd = false
break
}
if v.max < ori.min {
continue
}
if v.min > ori.max {
break
}
ur1 := uuidRange{
min: ori.min,
max: v.min - 1,
}
ur2 := uuidRange{
min: v.max + 1,
max: ori.max,
}
if ur1.max >= ur1.min {
res = append(res, ur1)
}
if ur2.max >= ur2.min {
res = append(res, ur2)
}
}
if len(res) == 0 && shouldAdd {
res = append(res, ori)
}
newRg = append(newRg, res...)
}
return newRg
}
func containRange(origin, rg []uuidRange) bool {
sort.Sort(SortRange(rg))
sort.Sort(SortRange(origin))
for i := 0; i < len(rg); i++ {
ft := rg[i]
found := false
for _, v := range origin {
if v.min <= ft.min && v.max >= ft.max {
found = true
}
}
if !found {
return false
}
}
return true
}
func overlapRange(origin, rg []uuidRange) bool {
sort.Sort(SortRange(rg))
sort.Sort(SortRange(origin))
for i := 0; i < len(origin); i++ {
ori := origin[i]
for _, v := range rg {
if v.min <= ori.min && v.max >= ori.max {
return true
}
if v.max < ori.min {
continue
}
if v.min > ori.max {
break
}
ur1 := uuidRange{
min: ori.min,
max: v.min - 1,
}
ur2 := uuidRange{
min: v.max + 1,
max: ori.max,
}
if ur1.max >= ur1.min {
return true
}
if ur2.max >= ur2.min {
return true
}
}
}
return false
}
func (g *Gtid) uniformNumber(num []string) ([]uuidRange, error) {
rg := make([]uuidRange, 0, len(num))
for _, v := range num {
ret := uuidRange{}
if splitPos := strings.Index(v, "-"); -1 != splitPos {
firstPart := string(v[0:splitPos])
if i64, err := strconv.ParseUint(firstPart, 10, 64); nil == err {
ret.min = i64
} else {
return rg, fmt.Errorf("invalid number %v", firstPart)
}
secondPart := string(v[splitPos+1:])
if i64, err := strconv.ParseUint(secondPart, 10, 64); nil == err {
ret.max = i64
} else {
return rg, fmt.Errorf("invalid number %v", secondPart)
}
} else {
if i64, err := strconv.ParseUint(v, 10, 64); nil == err {
ret.min = i64
ret.max = i64
} else {
return rg, fmt.Errorf("invalid number %v", v)
}
}
rg = append(rg, ret)
}
return rg, nil
}
func (g *Gtid) isValidUUID(uuid string) (string, error) {
uuid = strings.TrimSpace(strings.ToLower(strings.Replace(uuid, "-", "", -1)))
if 32 != len(uuid) {
return "", errors.New("invalid uuid" + uuid)
}
return uuid[0:8] + "-" + uuid[8:12] + "-" + uuid[12:16] + "-" + uuid[16:20] + "-" + uuid[20:], nil
}
func (g *Gtid) String() (ret string) {
if g.changed {
g.mu.Lock()
g.reform()
g.mu.Unlock()
g.changed = false
}
for _, uuidNumber := range g.uuidNumbers {
s := uuidNumber.uuid
for _, interval := range uuidNumber.intervals {
if interval.min == interval.max {
s = s + ":" + strconv.FormatUint(interval.min, 10)
} else {
s = s + ":" + strconv.FormatUint(interval.min, 10) + "-" + strconv.FormatUint(interval.max, 10)
}
}
if "" != ret {
ret = ret + ","
}
ret = ret + s
}
return ret
}
func (g *Gtid) AddGtid(uuid string, number uint64) error {
if strings.TrimSpace(uuid) == "" {
return nil
}
uuid, err := g.isValidUUID(uuid)
if err != nil {
return err
}
g.mu.RLock()
id, ok := g.hashMap[uuid]
g.mu.RUnlock()
g.mu.Lock()
defer g.mu.Unlock()
if ok {
tmp := g.uuidNumbers[id].intervals
if len(tmp) > 0 && tmp[len(tmp)-1].max+1 == number {
g.uuidNumbers[id].intervals[len(tmp)-1].max = number
} else {
g.uuidNumbers[id].intervals = append(g.uuidNumbers[id].intervals, uuidRange{
min: number,
max: number,
})
}
} else {
g.uuidNumbers = append(g.uuidNumbers, UUIDNumber{
uuid: uuid,
intervals: []uuidRange{uuidRange{
min: number,
max: number,
}},
})
g.hashMap[uuid] = len(g.uuidNumbers) - 1
}
if len(g.uuidNumbers[id].intervals) > 5000 {
g.reform()
}
g.changed = true
return nil
}
func (g *Gtid) Add(gtidDesc string) error {
return g.add(gtidDesc)
}
func (g *Gtid) add(gtidDesc string) error {
tmpSlice := make([]UUIDNumber, 0)
gtidlists := strings.Split(gtidDesc, ",")
for _, v := range gtidlists {
numbers := strings.Split(v, ":")
if len(numbers) < 2 {
return fmt.Errorf("invalid format:%v", v)
}
uuid, err := g.isValidUUID(numbers[0])
if err != nil {
return err
}
num, err := g.uniformNumber(numbers[1:])
if err != nil {
return err
}
tmpSlice = append(tmpSlice, UUIDNumber{
uuid: uuid,
intervals: num,
})
}
g.changed = true
g.mu.Lock()
defer g.mu.Unlock()
for _, v := range tmpSlice {
id, ok := g.hashMap[v.uuid]
if ok {
n := g.uuidNumbers[id].intervals
n = append(n, v.intervals...)
g.uuidNumbers[id].intervals = n
} else {
g.uuidNumbers = append(g.uuidNumbers, v)
g.hashMap[v.uuid] = len(g.uuidNumbers) - 1
}
if len(g.uuidNumbers[id].intervals) > 5000 {
g.reform()
}
}
return nil
}
func (g *Gtid) Sub(gtidDesc string) error {
return g.sub(gtidDesc)
}
func (g *Gtid) sub(gtidDesc string) error {
if strings.TrimSpace(gtidDesc) == "" {
return nil
}
tmpSlice := make([]UUIDNumber, 0)
gtidlists := strings.Split(gtidDesc, ",")
for _, v := range gtidlists {
numbers := strings.Split(v, ":")
if len(numbers) < 2 {
return fmt.Errorf("invalid format:%v", v)
}
uuid, err := g.isValidUUID(numbers[0])
if err != nil {
return err
}
num, err := g.uniformNumber(numbers[1:])
if err != nil {
return err
}
tmpSlice = append(tmpSlice, UUIDNumber{
uuid: uuid,
intervals: num,
})
}
g.changed = true
g.mu.Lock()
defer g.mu.Unlock()
for _, v := range tmpSlice {
id, ok := g.hashMap[v.uuid]
if ok {
n := subRange(g.uuidNumbers[id].intervals, v.intervals)
n, _ = g.uniformRange(n)
if len(n) == 0 || (len(n) == 1 && n[0].max == 0) {
delete(g.hashMap, v.uuid)
g.uuidNumbers = append(g.uuidNumbers[:id], g.uuidNumbers[id+1:]...)
for k, v := range g.uuidNumbers {
g.hashMap[v.uuid] = k
}
continue
} else {
g.uuidNumbers[id].intervals = n
}
} else {
continue
}
if len(g.uuidNumbers[id].intervals) > 5000 {
g.reform()
}
}
return nil
}
func (g *Gtid) ContainGtid(uuid string, number uint64) (bool, error) {
if strings.TrimSpace(uuid) == "" {
return true, nil
}
uuid, err := g.isValidUUID(uuid)
if err != nil {
return false, err
}
g.mu.Lock()
defer g.mu.Unlock()
id, ok := g.hashMap[uuid]
if ok {
if g.changed {
g.reform()
}
for _, v := range g.uuidNumbers[id].intervals {
if v.min <= number && v.max >= number {
return true, nil
}
}
}
return false, nil
}
func (g *Gtid) Contain(gtidDesc string) (bool, error) {
return g.contain(gtidDesc)
}
func (g *Gtid) contain(gtidDesc string) (bool, error) {
if strings.TrimSpace(gtidDesc) == "" {
return true, nil
}
tmpSlice := make([]UUIDNumber, 0)
gtidlists := strings.Split(gtidDesc, ",")
for _, v := range gtidlists {
numbers := strings.Split(v, ":")
if len(numbers) < 2 {
return false, fmt.Errorf("invalid format:%v", v)
}
uuid, err := g.isValidUUID(numbers[0])
if err != nil {
return false, err
}
num, err := g.uniformNumber(numbers[1:])
if err != nil {
return false, err
}
tmpSlice = append(tmpSlice, UUIDNumber{
uuid: uuid,
intervals: num,
})
}
g.mu.Lock()
defer g.mu.Unlock()
for _, v := range tmpSlice {
id, ok := g.hashMap[v.uuid]
if ok {
if !containRange(g.uuidNumbers[id].intervals, v.intervals) {
return false, nil
}
} else {
return false, nil
}
}
return true, nil
}
func (g *Gtid) Overlap(gtidDesc string) (bool, error) {
return g.overlap(gtidDesc)
}
func (g *Gtid) overlap(gtidDesc string) (bool, error) {
if strings.TrimSpace(gtidDesc) == "" {
if g.String() == "" {
return true, nil
}
return false, nil
}
tmpSlice := make([]UUIDNumber, 0)
gtidlists := strings.Split(gtidDesc, ",")
for _, v := range gtidlists {
numbers := strings.Split(v, ":")
if len(numbers) < 2 {
return false, fmt.Errorf("invalid format:%v", v)
}
uuid, err := g.isValidUUID(numbers[0])
if err != nil {
return false, err
}
num, err := g.uniformNumber(numbers[1:])
if err != nil {
return false, err
}
tmpSlice = append(tmpSlice, UUIDNumber{
uuid: uuid,
intervals: num,
})
}
g.mu.Lock()
defer g.mu.Unlock()
for _, v := range tmpSlice {
id, ok := g.hashMap[v.uuid]
if ok {
if overlapRange(g.uuidNumbers[id].intervals, v.intervals) {
return true, nil
}
} else {
continue
}
}
return false, nil
}
func (g *Gtid) Equal(gtidDesc string) (bool, error) {
if g.String() == "" && strings.TrimSpace(gtidDesc) == "" {
return true, nil
}
equ, err := parse(gtidDesc)
if err != nil {
return false, err
}
return g.String() == equ.String(), nil
}
func (g *Gtid) Clone() *Gtid {
g.mu.Lock()
defer g.mu.Unlock()
var newGtid = &Gtid{}
newGtid.uuidNumbers = make([]UUIDNumber, len(g.uuidNumbers))
copy(newGtid.uuidNumbers, g.uuidNumbers)
newGtid.hashMap = make(map[string]int, len(g.hashMap))
for k, v := range g.hashMap {
newGtid.hashMap[k] = v
}
if g.changed {
newGtid.reform()
}
return newGtid
}
func (g *Gtid) EventCount() uint64 {
g.mu.Lock()
defer g.mu.Unlock()
if g.changed {
g.reform()
}
var ret uint64 = 0
for _, uuidNumber := range g.uuidNumbers {
for _, interval := range uuidNumber.intervals {
ret += interval.max - interval.min + 1
}
}
return ret
}
func (g *Gtid) EventList() []string {
g.mu.Lock()
defer g.mu.Unlock()
if g.changed {
g.reform()
}
eventList := []string{}
for _, uuidNumber := range g.uuidNumbers {
for _, interval := range uuidNumber.intervals {
for i := interval.min; i <= interval.max; i++ {
eventList = append(eventList, fmt.Sprintf("%s:%d", uuidNumber.uuid, i))
}
}
}
return eventList
}