blob: 5ed7abe66510d6102bee8b8f715b633b25554542 [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001/*
2Copyright 2014 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package resource
18
19import (
20 "strconv"
21)
22
23type suffix string
24
25// suffixer can interpret and construct suffixes.
26type suffixer interface {
27 interpret(suffix) (base, exponent int32, fmt Format, ok bool)
28 construct(base, exponent int32, fmt Format) (s suffix, ok bool)
29 constructBytes(base, exponent int32, fmt Format) (s []byte, ok bool)
30}
31
32// quantitySuffixer handles suffixes for all three formats that quantity
33// can handle.
34var quantitySuffixer = newSuffixer()
35
36type bePair struct {
37 base, exponent int32
38}
39
40type listSuffixer struct {
41 suffixToBE map[suffix]bePair
42 beToSuffix map[bePair]suffix
43 beToSuffixBytes map[bePair][]byte
44}
45
46func (ls *listSuffixer) addSuffix(s suffix, pair bePair) {
47 if ls.suffixToBE == nil {
48 ls.suffixToBE = map[suffix]bePair{}
49 }
50 if ls.beToSuffix == nil {
51 ls.beToSuffix = map[bePair]suffix{}
52 }
53 if ls.beToSuffixBytes == nil {
54 ls.beToSuffixBytes = map[bePair][]byte{}
55 }
56 ls.suffixToBE[s] = pair
57 ls.beToSuffix[pair] = s
58 ls.beToSuffixBytes[pair] = []byte(s)
59}
60
61func (ls *listSuffixer) lookup(s suffix) (base, exponent int32, ok bool) {
62 pair, ok := ls.suffixToBE[s]
63 if !ok {
64 return 0, 0, false
65 }
66 return pair.base, pair.exponent, true
67}
68
69func (ls *listSuffixer) construct(base, exponent int32) (s suffix, ok bool) {
70 s, ok = ls.beToSuffix[bePair{base, exponent}]
71 return
72}
73
74func (ls *listSuffixer) constructBytes(base, exponent int32) (s []byte, ok bool) {
75 s, ok = ls.beToSuffixBytes[bePair{base, exponent}]
76 return
77}
78
79type suffixHandler struct {
80 decSuffixes listSuffixer
81 binSuffixes listSuffixer
82}
83
84type fastLookup struct {
85 *suffixHandler
86}
87
88func (l fastLookup) interpret(s suffix) (base, exponent int32, format Format, ok bool) {
89 switch s {
90 case "":
91 return 10, 0, DecimalSI, true
92 case "n":
93 return 10, -9, DecimalSI, true
94 case "u":
95 return 10, -6, DecimalSI, true
96 case "m":
97 return 10, -3, DecimalSI, true
98 case "k":
99 return 10, 3, DecimalSI, true
100 case "M":
101 return 10, 6, DecimalSI, true
102 case "G":
103 return 10, 9, DecimalSI, true
104 }
105 return l.suffixHandler.interpret(s)
106}
107
108func newSuffixer() suffixer {
109 sh := &suffixHandler{}
110
111 // IMPORTANT: if you change this section you must change fastLookup
112
113 sh.binSuffixes.addSuffix("Ki", bePair{2, 10})
114 sh.binSuffixes.addSuffix("Mi", bePair{2, 20})
115 sh.binSuffixes.addSuffix("Gi", bePair{2, 30})
116 sh.binSuffixes.addSuffix("Ti", bePair{2, 40})
117 sh.binSuffixes.addSuffix("Pi", bePair{2, 50})
118 sh.binSuffixes.addSuffix("Ei", bePair{2, 60})
119 // Don't emit an error when trying to produce
120 // a suffix for 2^0.
121 sh.decSuffixes.addSuffix("", bePair{2, 0})
122
123 sh.decSuffixes.addSuffix("n", bePair{10, -9})
124 sh.decSuffixes.addSuffix("u", bePair{10, -6})
125 sh.decSuffixes.addSuffix("m", bePair{10, -3})
126 sh.decSuffixes.addSuffix("", bePair{10, 0})
127 sh.decSuffixes.addSuffix("k", bePair{10, 3})
128 sh.decSuffixes.addSuffix("M", bePair{10, 6})
129 sh.decSuffixes.addSuffix("G", bePair{10, 9})
130 sh.decSuffixes.addSuffix("T", bePair{10, 12})
131 sh.decSuffixes.addSuffix("P", bePair{10, 15})
132 sh.decSuffixes.addSuffix("E", bePair{10, 18})
133
134 return fastLookup{sh}
135}
136
137func (sh *suffixHandler) construct(base, exponent int32, fmt Format) (s suffix, ok bool) {
138 switch fmt {
139 case DecimalSI:
140 return sh.decSuffixes.construct(base, exponent)
141 case BinarySI:
142 return sh.binSuffixes.construct(base, exponent)
143 case DecimalExponent:
144 if base != 10 {
145 return "", false
146 }
147 if exponent == 0 {
148 return "", true
149 }
150 return suffix("e" + strconv.FormatInt(int64(exponent), 10)), true
151 }
152 return "", false
153}
154
155func (sh *suffixHandler) constructBytes(base, exponent int32, format Format) (s []byte, ok bool) {
156 switch format {
157 case DecimalSI:
158 return sh.decSuffixes.constructBytes(base, exponent)
159 case BinarySI:
160 return sh.binSuffixes.constructBytes(base, exponent)
161 case DecimalExponent:
162 if base != 10 {
163 return nil, false
164 }
165 if exponent == 0 {
166 return nil, true
167 }
168 result := make([]byte, 8, 8)
169 result[0] = 'e'
170 number := strconv.AppendInt(result[1:1], int64(exponent), 10)
171 if &result[1] == &number[0] {
172 return result[:1+len(number)], true
173 }
174 result = append(result[:1], number...)
175 return result, true
176 }
177 return nil, false
178}
179
180func (sh *suffixHandler) interpret(suffix suffix) (base, exponent int32, fmt Format, ok bool) {
181 // Try lookup tables first
182 if b, e, ok := sh.decSuffixes.lookup(suffix); ok {
183 return b, e, DecimalSI, true
184 }
185 if b, e, ok := sh.binSuffixes.lookup(suffix); ok {
186 return b, e, BinarySI, true
187 }
188
189 if len(suffix) > 1 && (suffix[0] == 'E' || suffix[0] == 'e') {
190 parsed, err := strconv.ParseInt(string(suffix[1:]), 10, 64)
191 if err != nil {
192 return 0, 0, DecimalExponent, false
193 }
194 return 10, int32(parsed), DecimalExponent, true
195 }
196
197 return 0, 0, DecimalExponent, false
198}