Scott Baker | e7144bc | 2019-10-01 14:16:47 -0700 | [diff] [blame] | 1 | /* |
| 2 | Copyright 2014 The Kubernetes Authors. |
| 3 | |
| 4 | Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | you may not use this file except in compliance with the License. |
| 6 | You may obtain a copy of the License at |
| 7 | |
| 8 | http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | |
| 10 | Unless required by applicable law or agreed to in writing, software |
| 11 | distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | See the License for the specific language governing permissions and |
| 14 | limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package resource |
| 18 | |
| 19 | import ( |
| 20 | "strconv" |
| 21 | ) |
| 22 | |
| 23 | type suffix string |
| 24 | |
| 25 | // suffixer can interpret and construct suffixes. |
| 26 | type 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. |
| 34 | var quantitySuffixer = newSuffixer() |
| 35 | |
| 36 | type bePair struct { |
| 37 | base, exponent int32 |
| 38 | } |
| 39 | |
| 40 | type listSuffixer struct { |
| 41 | suffixToBE map[suffix]bePair |
| 42 | beToSuffix map[bePair]suffix |
| 43 | beToSuffixBytes map[bePair][]byte |
| 44 | } |
| 45 | |
| 46 | func (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 | |
| 61 | func (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 | |
| 69 | func (ls *listSuffixer) construct(base, exponent int32) (s suffix, ok bool) { |
| 70 | s, ok = ls.beToSuffix[bePair{base, exponent}] |
| 71 | return |
| 72 | } |
| 73 | |
| 74 | func (ls *listSuffixer) constructBytes(base, exponent int32) (s []byte, ok bool) { |
| 75 | s, ok = ls.beToSuffixBytes[bePair{base, exponent}] |
| 76 | return |
| 77 | } |
| 78 | |
| 79 | type suffixHandler struct { |
| 80 | decSuffixes listSuffixer |
| 81 | binSuffixes listSuffixer |
| 82 | } |
| 83 | |
| 84 | type fastLookup struct { |
| 85 | *suffixHandler |
| 86 | } |
| 87 | |
| 88 | func (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 | |
| 108 | func 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 | |
| 137 | func (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 | |
| 155 | func (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 | |
| 180 | func (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 | } |