1 | package humanize
|
---|
2 |
|
---|
3 | import (
|
---|
4 | "fmt"
|
---|
5 | "math"
|
---|
6 | "strconv"
|
---|
7 | "strings"
|
---|
8 | "unicode"
|
---|
9 | )
|
---|
10 |
|
---|
11 | // IEC Sizes.
|
---|
12 | // kibis of bits
|
---|
13 | const (
|
---|
14 | Byte = 1 << (iota * 10)
|
---|
15 | KiByte
|
---|
16 | MiByte
|
---|
17 | GiByte
|
---|
18 | TiByte
|
---|
19 | PiByte
|
---|
20 | EiByte
|
---|
21 | )
|
---|
22 |
|
---|
23 | // SI Sizes.
|
---|
24 | const (
|
---|
25 | IByte = 1
|
---|
26 | KByte = IByte * 1000
|
---|
27 | MByte = KByte * 1000
|
---|
28 | GByte = MByte * 1000
|
---|
29 | TByte = GByte * 1000
|
---|
30 | PByte = TByte * 1000
|
---|
31 | EByte = PByte * 1000
|
---|
32 | )
|
---|
33 |
|
---|
34 | var bytesSizeTable = map[string]uint64{
|
---|
35 | "b": Byte,
|
---|
36 | "kib": KiByte,
|
---|
37 | "kb": KByte,
|
---|
38 | "mib": MiByte,
|
---|
39 | "mb": MByte,
|
---|
40 | "gib": GiByte,
|
---|
41 | "gb": GByte,
|
---|
42 | "tib": TiByte,
|
---|
43 | "tb": TByte,
|
---|
44 | "pib": PiByte,
|
---|
45 | "pb": PByte,
|
---|
46 | "eib": EiByte,
|
---|
47 | "eb": EByte,
|
---|
48 | // Without suffix
|
---|
49 | "": Byte,
|
---|
50 | "ki": KiByte,
|
---|
51 | "k": KByte,
|
---|
52 | "mi": MiByte,
|
---|
53 | "m": MByte,
|
---|
54 | "gi": GiByte,
|
---|
55 | "g": GByte,
|
---|
56 | "ti": TiByte,
|
---|
57 | "t": TByte,
|
---|
58 | "pi": PiByte,
|
---|
59 | "p": PByte,
|
---|
60 | "ei": EiByte,
|
---|
61 | "e": EByte,
|
---|
62 | }
|
---|
63 |
|
---|
64 | func logn(n, b float64) float64 {
|
---|
65 | return math.Log(n) / math.Log(b)
|
---|
66 | }
|
---|
67 |
|
---|
68 | func humanateBytes(s uint64, base float64, sizes []string) string {
|
---|
69 | if s < 10 {
|
---|
70 | return fmt.Sprintf("%d B", s)
|
---|
71 | }
|
---|
72 | e := math.Floor(logn(float64(s), base))
|
---|
73 | suffix := sizes[int(e)]
|
---|
74 | val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10
|
---|
75 | f := "%.0f %s"
|
---|
76 | if val < 10 {
|
---|
77 | f = "%.1f %s"
|
---|
78 | }
|
---|
79 |
|
---|
80 | return fmt.Sprintf(f, val, suffix)
|
---|
81 | }
|
---|
82 |
|
---|
83 | // Bytes produces a human readable representation of an SI size.
|
---|
84 | //
|
---|
85 | // See also: ParseBytes.
|
---|
86 | //
|
---|
87 | // Bytes(82854982) -> 83 MB
|
---|
88 | func Bytes(s uint64) string {
|
---|
89 | sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"}
|
---|
90 | return humanateBytes(s, 1000, sizes)
|
---|
91 | }
|
---|
92 |
|
---|
93 | // IBytes produces a human readable representation of an IEC size.
|
---|
94 | //
|
---|
95 | // See also: ParseBytes.
|
---|
96 | //
|
---|
97 | // IBytes(82854982) -> 79 MiB
|
---|
98 | func IBytes(s uint64) string {
|
---|
99 | sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}
|
---|
100 | return humanateBytes(s, 1024, sizes)
|
---|
101 | }
|
---|
102 |
|
---|
103 | // ParseBytes parses a string representation of bytes into the number
|
---|
104 | // of bytes it represents.
|
---|
105 | //
|
---|
106 | // See Also: Bytes, IBytes.
|
---|
107 | //
|
---|
108 | // ParseBytes("42 MB") -> 42000000, nil
|
---|
109 | // ParseBytes("42 mib") -> 44040192, nil
|
---|
110 | func ParseBytes(s string) (uint64, error) {
|
---|
111 | lastDigit := 0
|
---|
112 | hasComma := false
|
---|
113 | for _, r := range s {
|
---|
114 | if !(unicode.IsDigit(r) || r == '.' || r == ',') {
|
---|
115 | break
|
---|
116 | }
|
---|
117 | if r == ',' {
|
---|
118 | hasComma = true
|
---|
119 | }
|
---|
120 | lastDigit++
|
---|
121 | }
|
---|
122 |
|
---|
123 | num := s[:lastDigit]
|
---|
124 | if hasComma {
|
---|
125 | num = strings.Replace(num, ",", "", -1)
|
---|
126 | }
|
---|
127 |
|
---|
128 | f, err := strconv.ParseFloat(num, 64)
|
---|
129 | if err != nil {
|
---|
130 | return 0, err
|
---|
131 | }
|
---|
132 |
|
---|
133 | extra := strings.ToLower(strings.TrimSpace(s[lastDigit:]))
|
---|
134 | if m, ok := bytesSizeTable[extra]; ok {
|
---|
135 | f *= float64(m)
|
---|
136 | if f >= math.MaxUint64 {
|
---|
137 | return 0, fmt.Errorf("too large: %v", s)
|
---|
138 | }
|
---|
139 | return uint64(f), nil
|
---|
140 | }
|
---|
141 |
|
---|
142 | return 0, fmt.Errorf("unhandled size name: %v", extra)
|
---|
143 | }
|
---|