1package humanize
2
3import (
4	"fmt"
5	"math/big"
6	"strings"
7	"unicode"
8)
9
10var (
11	bigIECExp = big.NewInt(1024)
12
13	// BigByte is one byte in bit.Ints
14	BigByte = big.NewInt(1)
15	// BigKiByte is 1,024 bytes in bit.Ints
16	BigKiByte = (&big.Int{}).Mul(BigByte, bigIECExp)
17	// BigMiByte is 1,024 k bytes in bit.Ints
18	BigMiByte = (&big.Int{}).Mul(BigKiByte, bigIECExp)
19	// BigGiByte is 1,024 m bytes in bit.Ints
20	BigGiByte = (&big.Int{}).Mul(BigMiByte, bigIECExp)
21	// BigTiByte is 1,024 g bytes in bit.Ints
22	BigTiByte = (&big.Int{}).Mul(BigGiByte, bigIECExp)
23	// BigPiByte is 1,024 t bytes in bit.Ints
24	BigPiByte = (&big.Int{}).Mul(BigTiByte, bigIECExp)
25	// BigEiByte is 1,024 p bytes in bit.Ints
26	BigEiByte = (&big.Int{}).Mul(BigPiByte, bigIECExp)
27	// BigZiByte is 1,024 e bytes in bit.Ints
28	BigZiByte = (&big.Int{}).Mul(BigEiByte, bigIECExp)
29	// BigYiByte is 1,024 z bytes in bit.Ints
30	BigYiByte = (&big.Int{}).Mul(BigZiByte, bigIECExp)
31)
32
33var (
34	bigSIExp = big.NewInt(1000)
35
36	// BigSIByte is one SI byte in big.Ints
37	BigSIByte = big.NewInt(1)
38	// BigKByte is 1,000 SI bytes in big.Ints
39	BigKByte = (&big.Int{}).Mul(BigSIByte, bigSIExp)
40	// BigMByte is 1,000 SI k bytes in big.Ints
41	BigMByte = (&big.Int{}).Mul(BigKByte, bigSIExp)
42	// BigGByte is 1,000 SI m bytes in big.Ints
43	BigGByte = (&big.Int{}).Mul(BigMByte, bigSIExp)
44	// BigTByte is 1,000 SI g bytes in big.Ints
45	BigTByte = (&big.Int{}).Mul(BigGByte, bigSIExp)
46	// BigPByte is 1,000 SI t bytes in big.Ints
47	BigPByte = (&big.Int{}).Mul(BigTByte, bigSIExp)
48	// BigEByte is 1,000 SI p bytes in big.Ints
49	BigEByte = (&big.Int{}).Mul(BigPByte, bigSIExp)
50	// BigZByte is 1,000 SI e bytes in big.Ints
51	BigZByte = (&big.Int{}).Mul(BigEByte, bigSIExp)
52	// BigYByte is 1,000 SI z bytes in big.Ints
53	BigYByte = (&big.Int{}).Mul(BigZByte, bigSIExp)
54)
55
56var bigBytesSizeTable = map[string]*big.Int{
57	"b":   BigByte,
58	"kib": BigKiByte,
59	"kb":  BigKByte,
60	"mib": BigMiByte,
61	"mb":  BigMByte,
62	"gib": BigGiByte,
63	"gb":  BigGByte,
64	"tib": BigTiByte,
65	"tb":  BigTByte,
66	"pib": BigPiByte,
67	"pb":  BigPByte,
68	"eib": BigEiByte,
69	"eb":  BigEByte,
70	"zib": BigZiByte,
71	"zb":  BigZByte,
72	"yib": BigYiByte,
73	"yb":  BigYByte,
74	// Without suffix
75	"":   BigByte,
76	"ki": BigKiByte,
77	"k":  BigKByte,
78	"mi": BigMiByte,
79	"m":  BigMByte,
80	"gi": BigGiByte,
81	"g":  BigGByte,
82	"ti": BigTiByte,
83	"t":  BigTByte,
84	"pi": BigPiByte,
85	"p":  BigPByte,
86	"ei": BigEiByte,
87	"e":  BigEByte,
88	"z":  BigZByte,
89	"zi": BigZiByte,
90	"y":  BigYByte,
91	"yi": BigYiByte,
92}
93
94var ten = big.NewInt(10)
95
96func humanateBigBytes(s, base *big.Int, sizes []string) string {
97	if s.Cmp(ten) < 0 {
98		return fmt.Sprintf("%d B", s)
99	}
100	c := (&big.Int{}).Set(s)
101	val, mag := oomm(c, base, len(sizes)-1)
102	suffix := sizes[mag]
103	f := "%.0f %s"
104	if val < 10 {
105		f = "%.1f %s"
106	}
107
108	return fmt.Sprintf(f, val, suffix)
109
110}
111
112// BigBytes produces a human readable representation of an SI size.
113//
114// See also: ParseBigBytes.
115//
116// BigBytes(82854982) -> 83 MB
117func BigBytes(s *big.Int) string {
118	sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
119	return humanateBigBytes(s, bigSIExp, sizes)
120}
121
122// BigIBytes produces a human readable representation of an IEC size.
123//
124// See also: ParseBigBytes.
125//
126// BigIBytes(82854982) -> 79 MiB
127func BigIBytes(s *big.Int) string {
128	sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
129	return humanateBigBytes(s, bigIECExp, sizes)
130}
131
132// ParseBigBytes parses a string representation of bytes into the number
133// of bytes it represents.
134//
135// See also: BigBytes, BigIBytes.
136//
137// ParseBigBytes("42 MB") -> 42000000, nil
138// ParseBigBytes("42 mib") -> 44040192, nil
139func ParseBigBytes(s string) (*big.Int, error) {
140	lastDigit := 0
141	hasComma := false
142	for _, r := range s {
143		if !(unicode.IsDigit(r) || r == '.' || r == ',') {
144			break
145		}
146		if r == ',' {
147			hasComma = true
148		}
149		lastDigit++
150	}
151
152	num := s[:lastDigit]
153	if hasComma {
154		num = strings.Replace(num, ",", "", -1)
155	}
156
157	val := &big.Rat{}
158	_, err := fmt.Sscanf(num, "%f", val)
159	if err != nil {
160		return nil, err
161	}
162
163	extra := strings.ToLower(strings.TrimSpace(s[lastDigit:]))
164	if m, ok := bigBytesSizeTable[extra]; ok {
165		mv := (&big.Rat{}).SetInt(m)
166		val.Mul(val, mv)
167		rv := &big.Int{}
168		rv.Div(val.Num(), val.Denom())
169		return rv, nil
170	}
171
172	return nil, fmt.Errorf("unhandled size name: %v", extra)
173}
174