Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / gopkg.in / yaml.v2 / resolve.go
1 package yaml
2
3 import (
4         "encoding/base64"
5         "math"
6         "regexp"
7         "strconv"
8         "strings"
9         "time"
10 )
11
12 type resolveMapItem struct {
13         value interface{}
14         tag   string
15 }
16
17 var resolveTable = make([]byte, 256)
18 var resolveMap = make(map[string]resolveMapItem)
19
20 func init() {
21         t := resolveTable
22         t[int('+')] = 'S' // Sign
23         t[int('-')] = 'S'
24         for _, c := range "0123456789" {
25                 t[int(c)] = 'D' // Digit
26         }
27         for _, c := range "yYnNtTfFoO~" {
28                 t[int(c)] = 'M' // In map
29         }
30         t[int('.')] = '.' // Float (potentially in map)
31
32         var resolveMapList = []struct {
33                 v   interface{}
34                 tag string
35                 l   []string
36         }{
37                 {true, yaml_BOOL_TAG, []string{"y", "Y", "yes", "Yes", "YES"}},
38                 {true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}},
39                 {true, yaml_BOOL_TAG, []string{"on", "On", "ON"}},
40                 {false, yaml_BOOL_TAG, []string{"n", "N", "no", "No", "NO"}},
41                 {false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}},
42                 {false, yaml_BOOL_TAG, []string{"off", "Off", "OFF"}},
43                 {nil, yaml_NULL_TAG, []string{"", "~", "null", "Null", "NULL"}},
44                 {math.NaN(), yaml_FLOAT_TAG, []string{".nan", ".NaN", ".NAN"}},
45                 {math.Inf(+1), yaml_FLOAT_TAG, []string{".inf", ".Inf", ".INF"}},
46                 {math.Inf(+1), yaml_FLOAT_TAG, []string{"+.inf", "+.Inf", "+.INF"}},
47                 {math.Inf(-1), yaml_FLOAT_TAG, []string{"-.inf", "-.Inf", "-.INF"}},
48                 {"<<", yaml_MERGE_TAG, []string{"<<"}},
49         }
50
51         m := resolveMap
52         for _, item := range resolveMapList {
53                 for _, s := range item.l {
54                         m[s] = resolveMapItem{item.v, item.tag}
55                 }
56         }
57 }
58
59 const longTagPrefix = "tag:yaml.org,2002:"
60
61 func shortTag(tag string) string {
62         // TODO This can easily be made faster and produce less garbage.
63         if strings.HasPrefix(tag, longTagPrefix) {
64                 return "!!" + tag[len(longTagPrefix):]
65         }
66         return tag
67 }
68
69 func longTag(tag string) string {
70         if strings.HasPrefix(tag, "!!") {
71                 return longTagPrefix + tag[2:]
72         }
73         return tag
74 }
75
76 func resolvableTag(tag string) bool {
77         switch tag {
78         case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG, yaml_TIMESTAMP_TAG:
79                 return true
80         }
81         return false
82 }
83
84 var yamlStyleFloat = regexp.MustCompile(`^[-+]?[0-9]*\.?[0-9]+([eE][-+][0-9]+)?$`)
85
86 func resolve(tag string, in string) (rtag string, out interface{}) {
87         if !resolvableTag(tag) {
88                 return tag, in
89         }
90
91         defer func() {
92                 switch tag {
93                 case "", rtag, yaml_STR_TAG, yaml_BINARY_TAG:
94                         return
95                 case yaml_FLOAT_TAG:
96                         if rtag == yaml_INT_TAG {
97                                 switch v := out.(type) {
98                                 case int64:
99                                         rtag = yaml_FLOAT_TAG
100                                         out = float64(v)
101                                         return
102                                 case int:
103                                         rtag = yaml_FLOAT_TAG
104                                         out = float64(v)
105                                         return
106                                 }
107                         }
108                 }
109                 failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag))
110         }()
111
112         // Any data is accepted as a !!str or !!binary.
113         // Otherwise, the prefix is enough of a hint about what it might be.
114         hint := byte('N')
115         if in != "" {
116                 hint = resolveTable[in[0]]
117         }
118         if hint != 0 && tag != yaml_STR_TAG && tag != yaml_BINARY_TAG {
119                 // Handle things we can lookup in a map.
120                 if item, ok := resolveMap[in]; ok {
121                         return item.tag, item.value
122                 }
123
124                 // Base 60 floats are a bad idea, were dropped in YAML 1.2, and
125                 // are purposefully unsupported here. They're still quoted on
126                 // the way out for compatibility with other parser, though.
127
128                 switch hint {
129                 case 'M':
130                         // We've already checked the map above.
131
132                 case '.':
133                         // Not in the map, so maybe a normal float.
134                         floatv, err := strconv.ParseFloat(in, 64)
135                         if err == nil {
136                                 return yaml_FLOAT_TAG, floatv
137                         }
138
139                 case 'D', 'S':
140                         // Int, float, or timestamp.
141                         // Only try values as a timestamp if the value is unquoted or there's an explicit
142                         // !!timestamp tag.
143                         if tag == "" || tag == yaml_TIMESTAMP_TAG {
144                                 t, ok := parseTimestamp(in)
145                                 if ok {
146                                         return yaml_TIMESTAMP_TAG, t
147                                 }
148                         }
149
150                         plain := strings.Replace(in, "_", "", -1)
151                         intv, err := strconv.ParseInt(plain, 0, 64)
152                         if err == nil {
153                                 if intv == int64(int(intv)) {
154                                         return yaml_INT_TAG, int(intv)
155                                 } else {
156                                         return yaml_INT_TAG, intv
157                                 }
158                         }
159                         uintv, err := strconv.ParseUint(plain, 0, 64)
160                         if err == nil {
161                                 return yaml_INT_TAG, uintv
162                         }
163                         if yamlStyleFloat.MatchString(plain) {
164                                 floatv, err := strconv.ParseFloat(plain, 64)
165                                 if err == nil {
166                                         return yaml_FLOAT_TAG, floatv
167                                 }
168                         }
169                         if strings.HasPrefix(plain, "0b") {
170                                 intv, err := strconv.ParseInt(plain[2:], 2, 64)
171                                 if err == nil {
172                                         if intv == int64(int(intv)) {
173                                                 return yaml_INT_TAG, int(intv)
174                                         } else {
175                                                 return yaml_INT_TAG, intv
176                                         }
177                                 }
178                                 uintv, err := strconv.ParseUint(plain[2:], 2, 64)
179                                 if err == nil {
180                                         return yaml_INT_TAG, uintv
181                                 }
182                         } else if strings.HasPrefix(plain, "-0b") {
183                                 intv, err := strconv.ParseInt("-" + plain[3:], 2, 64)
184                                 if err == nil {
185                                         if true || intv == int64(int(intv)) {
186                                                 return yaml_INT_TAG, int(intv)
187                                         } else {
188                                                 return yaml_INT_TAG, intv
189                                         }
190                                 }
191                         }
192                 default:
193                         panic("resolveTable item not yet handled: " + string(rune(hint)) + " (with " + in + ")")
194                 }
195         }
196         return yaml_STR_TAG, in
197 }
198
199 // encodeBase64 encodes s as base64 that is broken up into multiple lines
200 // as appropriate for the resulting length.
201 func encodeBase64(s string) string {
202         const lineLen = 70
203         encLen := base64.StdEncoding.EncodedLen(len(s))
204         lines := encLen/lineLen + 1
205         buf := make([]byte, encLen*2+lines)
206         in := buf[0:encLen]
207         out := buf[encLen:]
208         base64.StdEncoding.Encode(in, []byte(s))
209         k := 0
210         for i := 0; i < len(in); i += lineLen {
211                 j := i + lineLen
212                 if j > len(in) {
213                         j = len(in)
214                 }
215                 k += copy(out[k:], in[i:j])
216                 if lines > 1 {
217                         out[k] = '\n'
218                         k++
219                 }
220         }
221         return string(out[:k])
222 }
223
224 // This is a subset of the formats allowed by the regular expression
225 // defined at http://yaml.org/type/timestamp.html.
226 var allowedTimestampFormats = []string{
227         "2006-1-2T15:4:5.999999999Z07:00", // RCF3339Nano with short date fields.
228         "2006-1-2t15:4:5.999999999Z07:00", // RFC3339Nano with short date fields and lower-case "t".
229         "2006-1-2 15:4:5.999999999",       // space separated with no time zone
230         "2006-1-2",                        // date only
231         // Notable exception: time.Parse cannot handle: "2001-12-14 21:59:43.10 -5"
232         // from the set of examples.
233 }
234
235 // parseTimestamp parses s as a timestamp string and
236 // returns the timestamp and reports whether it succeeded.
237 // Timestamp formats are defined at http://yaml.org/type/timestamp.html
238 func parseTimestamp(s string) (time.Time, bool) {
239         // TODO write code to check all the formats supported by
240         // http://yaml.org/type/timestamp.html instead of using time.Parse.
241
242         // Quick check: all date formats start with YYYY-.
243         i := 0
244         for ; i < len(s); i++ {
245                 if c := s[i]; c < '0' || c > '9' {
246                         break
247                 }
248         }
249         if i != 4 || i == len(s) || s[i] != '-' {
250                 return time.Time{}, false
251         }
252         for _, format := range allowedTimestampFormats {
253                 if t, err := time.Parse(format, s); err == nil {
254                         return t, true
255                 }
256         }
257         return time.Time{}, false
258 }