1/*
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}
199