forked from pingcap/tidb
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathset.go
132 lines (109 loc) · 3.11 KB
/
set.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"strconv"
"strings"
"github.com/pingcap/errors"
"github.com/pingcap/tidb/util/collate"
"github.com/pingcap/tidb/util/stringutil"
)
var zeroSet = Set{Name: "", Value: 0}
// Set is for MySQL Set type.
type Set struct {
Name string
Value uint64
}
// String implements fmt.Stringer interface.
func (e Set) String() string {
return e.Name
}
// ToNumber changes Set to float64 for numeric operation.
func (e Set) ToNumber() float64 {
return float64(e.Value)
}
// Copy deep copy a Set.
func (e Set) Copy() Set {
return Set{
Name: stringutil.Copy(e.Name),
Value: e.Value,
}
}
// ParseSet creates a Set with name or value.
func ParseSet(elems []string, name string, collation string) (Set, error) {
if setName, err := ParseSetName(elems, name, collation); err == nil {
return setName, nil
}
// name doesn't exist, maybe an integer?
if num, err := strconv.ParseUint(name, 0, 64); err == nil {
return ParseSetValue(elems, num)
}
return Set{}, errors.Errorf("item %s is not in Set %v", name, elems)
}
// ParseSetName creates a Set with name.
func ParseSetName(elems []string, name string, collation string) (Set, error) {
if len(name) == 0 {
return zeroSet, nil
}
ctor := collate.GetCollator(collation)
seps := strings.Split(name, ",")
marked := make(map[string]struct{}, len(seps))
for _, s := range seps {
marked[string(ctor.Key(s))] = struct{}{}
}
items := make([]string, 0, len(seps))
value := uint64(0)
for i, n := range elems {
key := string(ctor.Key(n))
if _, ok := marked[key]; ok {
value |= 1 << uint64(i)
delete(marked, key)
items = append(items, n)
}
}
if len(marked) == 0 {
return Set{Name: strings.Join(items, ","), Value: value}, nil
}
return Set{}, errors.Errorf("item %s is not in Set %v", name, elems)
}
var (
setIndexValue []uint64
setIndexInvertValue []uint64
)
func init() {
setIndexValue = make([]uint64, 64)
setIndexInvertValue = make([]uint64, 64)
for i := 0; i < 64; i++ {
setIndexValue[i] = 1 << uint64(i)
setIndexInvertValue[i] = ^setIndexValue[i]
}
}
// ParseSetValue creates a Set with special number.
func ParseSetValue(elems []string, number uint64) (Set, error) {
if number == 0 {
return zeroSet, nil
}
value := number
var items []string
for i := 0; i < len(elems); i++ {
if number&setIndexValue[i] > 0 {
items = append(items, elems[i])
number &= setIndexInvertValue[i]
}
}
if number != 0 {
return Set{}, errors.Errorf("invalid number %d for Set %v", number, elems)
}
return Set{Name: strings.Join(items, ","), Value: value}, nil
}