diff options
Diffstat (limited to 'vendor/github.com/dustin')
-rw-r--r-- | vendor/github.com/dustin/go-humanize/.travis.yml | 21 | ||||
-rw-r--r-- | vendor/github.com/dustin/go-humanize/LICENSE | 21 | ||||
-rw-r--r-- | vendor/github.com/dustin/go-humanize/README.markdown | 124 | ||||
-rw-r--r-- | vendor/github.com/dustin/go-humanize/big.go | 31 | ||||
-rw-r--r-- | vendor/github.com/dustin/go-humanize/bigbytes.go | 189 | ||||
-rw-r--r-- | vendor/github.com/dustin/go-humanize/bytes.go | 143 | ||||
-rw-r--r-- | vendor/github.com/dustin/go-humanize/comma.go | 116 | ||||
-rw-r--r-- | vendor/github.com/dustin/go-humanize/commaf.go | 41 | ||||
-rw-r--r-- | vendor/github.com/dustin/go-humanize/ftoa.go | 49 | ||||
-rw-r--r-- | vendor/github.com/dustin/go-humanize/humanize.go | 8 | ||||
-rw-r--r-- | vendor/github.com/dustin/go-humanize/number.go | 192 | ||||
-rw-r--r-- | vendor/github.com/dustin/go-humanize/ordinals.go | 25 | ||||
-rw-r--r-- | vendor/github.com/dustin/go-humanize/si.go | 127 | ||||
-rw-r--r-- | vendor/github.com/dustin/go-humanize/times.go | 117 |
14 files changed, 1204 insertions, 0 deletions
diff --git a/vendor/github.com/dustin/go-humanize/.travis.yml b/vendor/github.com/dustin/go-humanize/.travis.yml new file mode 100644 index 0000000..ac12e48 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/.travis.yml | |||
@@ -0,0 +1,21 @@ | |||
1 | sudo: false | ||
2 | language: go | ||
3 | go_import_path: github.com/dustin/go-humanize | ||
4 | go: | ||
5 | - 1.13.x | ||
6 | - 1.14.x | ||
7 | - 1.15.x | ||
8 | - 1.16.x | ||
9 | - stable | ||
10 | - master | ||
11 | matrix: | ||
12 | allow_failures: | ||
13 | - go: master | ||
14 | fast_finish: true | ||
15 | install: | ||
16 | - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step). | ||
17 | script: | ||
18 | - diff -u <(echo -n) <(gofmt -d -s .) | ||
19 | - go vet . | ||
20 | - go install -v -race ./... | ||
21 | - go test -v -race ./... | ||
diff --git a/vendor/github.com/dustin/go-humanize/LICENSE b/vendor/github.com/dustin/go-humanize/LICENSE new file mode 100644 index 0000000..8d9a94a --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/LICENSE | |||
@@ -0,0 +1,21 @@ | |||
1 | Copyright (c) 2005-2008 Dustin Sallings <[email protected]> | ||
2 | |||
3 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
4 | of this software and associated documentation files (the "Software"), to deal | ||
5 | in the Software without restriction, including without limitation the rights | ||
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
7 | copies of the Software, and to permit persons to whom the Software is | ||
8 | furnished to do so, subject to the following conditions: | ||
9 | |||
10 | The above copyright notice and this permission notice shall be included in | ||
11 | all copies or substantial portions of the Software. | ||
12 | |||
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
19 | SOFTWARE. | ||
20 | |||
21 | <http://www.opensource.org/licenses/mit-license.php> | ||
diff --git a/vendor/github.com/dustin/go-humanize/README.markdown b/vendor/github.com/dustin/go-humanize/README.markdown new file mode 100644 index 0000000..7d0b16b --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/README.markdown | |||
@@ -0,0 +1,124 @@ | |||
1 | # Humane Units [![Build Status](https://travis-ci.org/dustin/go-humanize.svg?branch=master)](https://travis-ci.org/dustin/go-humanize) [![GoDoc](https://godoc.org/github.com/dustin/go-humanize?status.svg)](https://godoc.org/github.com/dustin/go-humanize) | ||
2 | |||
3 | Just a few functions for helping humanize times and sizes. | ||
4 | |||
5 | `go get` it as `github.com/dustin/go-humanize`, import it as | ||
6 | `"github.com/dustin/go-humanize"`, use it as `humanize`. | ||
7 | |||
8 | See [godoc](https://pkg.go.dev/github.com/dustin/go-humanize) for | ||
9 | complete documentation. | ||
10 | |||
11 | ## Sizes | ||
12 | |||
13 | This lets you take numbers like `82854982` and convert them to useful | ||
14 | strings like, `83 MB` or `79 MiB` (whichever you prefer). | ||
15 | |||
16 | Example: | ||
17 | |||
18 | ```go | ||
19 | fmt.Printf("That file is %s.", humanize.Bytes(82854982)) // That file is 83 MB. | ||
20 | ``` | ||
21 | |||
22 | ## Times | ||
23 | |||
24 | This lets you take a `time.Time` and spit it out in relative terms. | ||
25 | For example, `12 seconds ago` or `3 days from now`. | ||
26 | |||
27 | Example: | ||
28 | |||
29 | ```go | ||
30 | fmt.Printf("This was touched %s.", humanize.Time(someTimeInstance)) // This was touched 7 hours ago. | ||
31 | ``` | ||
32 | |||
33 | Thanks to Kyle Lemons for the time implementation from an IRC | ||
34 | conversation one day. It's pretty neat. | ||
35 | |||
36 | ## Ordinals | ||
37 | |||
38 | From a [mailing list discussion][odisc] where a user wanted to be able | ||
39 | to label ordinals. | ||
40 | |||
41 | 0 -> 0th | ||
42 | 1 -> 1st | ||
43 | 2 -> 2nd | ||
44 | 3 -> 3rd | ||
45 | 4 -> 4th | ||
46 | [...] | ||
47 | |||
48 | Example: | ||
49 | |||
50 | ```go | ||
51 | fmt.Printf("You're my %s best friend.", humanize.Ordinal(193)) // You are my 193rd best friend. | ||
52 | ``` | ||
53 | |||
54 | ## Commas | ||
55 | |||
56 | Want to shove commas into numbers? Be my guest. | ||
57 | |||
58 | 0 -> 0 | ||
59 | 100 -> 100 | ||
60 | 1000 -> 1,000 | ||
61 | 1000000000 -> 1,000,000,000 | ||
62 | -100000 -> -100,000 | ||
63 | |||
64 | Example: | ||
65 | |||
66 | ```go | ||
67 | fmt.Printf("You owe $%s.\n", humanize.Comma(6582491)) // You owe $6,582,491. | ||
68 | ``` | ||
69 | |||
70 | ## Ftoa | ||
71 | |||
72 | Nicer float64 formatter that removes trailing zeros. | ||
73 | |||
74 | ```go | ||
75 | fmt.Printf("%f", 2.24) // 2.240000 | ||
76 | fmt.Printf("%s", humanize.Ftoa(2.24)) // 2.24 | ||
77 | fmt.Printf("%f", 2.0) // 2.000000 | ||
78 | fmt.Printf("%s", humanize.Ftoa(2.0)) // 2 | ||
79 | ``` | ||
80 | |||
81 | ## SI notation | ||
82 | |||
83 | Format numbers with [SI notation][sinotation]. | ||
84 | |||
85 | Example: | ||
86 | |||
87 | ```go | ||
88 | humanize.SI(0.00000000223, "M") // 2.23 nM | ||
89 | ``` | ||
90 | |||
91 | ## English-specific functions | ||
92 | |||
93 | The following functions are in the `humanize/english` subpackage. | ||
94 | |||
95 | ### Plurals | ||
96 | |||
97 | Simple English pluralization | ||
98 | |||
99 | ```go | ||
100 | english.PluralWord(1, "object", "") // object | ||
101 | english.PluralWord(42, "object", "") // objects | ||
102 | english.PluralWord(2, "bus", "") // buses | ||
103 | english.PluralWord(99, "locus", "loci") // loci | ||
104 | |||
105 | english.Plural(1, "object", "") // 1 object | ||
106 | english.Plural(42, "object", "") // 42 objects | ||
107 | english.Plural(2, "bus", "") // 2 buses | ||
108 | english.Plural(99, "locus", "loci") // 99 loci | ||
109 | ``` | ||
110 | |||
111 | ### Word series | ||
112 | |||
113 | Format comma-separated words lists with conjuctions: | ||
114 | |||
115 | ```go | ||
116 | english.WordSeries([]string{"foo"}, "and") // foo | ||
117 | english.WordSeries([]string{"foo", "bar"}, "and") // foo and bar | ||
118 | english.WordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar and baz | ||
119 | |||
120 | english.OxfordWordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar, and baz | ||
121 | ``` | ||
122 | |||
123 | [odisc]: https://groups.google.com/d/topic/golang-nuts/l8NhI74jl-4/discussion | ||
124 | [sinotation]: http://en.wikipedia.org/wiki/Metric_prefix | ||
diff --git a/vendor/github.com/dustin/go-humanize/big.go b/vendor/github.com/dustin/go-humanize/big.go new file mode 100644 index 0000000..f49dc33 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/big.go | |||
@@ -0,0 +1,31 @@ | |||
1 | package humanize | ||
2 | |||
3 | import ( | ||
4 | "math/big" | ||
5 | ) | ||
6 | |||
7 | // order of magnitude (to a max order) | ||
8 | func oomm(n, b *big.Int, maxmag int) (float64, int) { | ||
9 | mag := 0 | ||
10 | m := &big.Int{} | ||
11 | for n.Cmp(b) >= 0 { | ||
12 | n.DivMod(n, b, m) | ||
13 | mag++ | ||
14 | if mag == maxmag && maxmag >= 0 { | ||
15 | break | ||
16 | } | ||
17 | } | ||
18 | return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag | ||
19 | } | ||
20 | |||
21 | // total order of magnitude | ||
22 | // (same as above, but with no upper limit) | ||
23 | func oom(n, b *big.Int) (float64, int) { | ||
24 | mag := 0 | ||
25 | m := &big.Int{} | ||
26 | for n.Cmp(b) >= 0 { | ||
27 | n.DivMod(n, b, m) | ||
28 | mag++ | ||
29 | } | ||
30 | return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag | ||
31 | } | ||
diff --git a/vendor/github.com/dustin/go-humanize/bigbytes.go b/vendor/github.com/dustin/go-humanize/bigbytes.go new file mode 100644 index 0000000..3b015fd --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/bigbytes.go | |||
@@ -0,0 +1,189 @@ | |||
1 | package humanize | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "math/big" | ||
6 | "strings" | ||
7 | "unicode" | ||
8 | ) | ||
9 | |||
10 | var ( | ||
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 | // BigRiByte is 1,024 y bytes in bit.Ints | ||
32 | BigRiByte = (&big.Int{}).Mul(BigYiByte, bigIECExp) | ||
33 | // BigQiByte is 1,024 r bytes in bit.Ints | ||
34 | BigQiByte = (&big.Int{}).Mul(BigRiByte, bigIECExp) | ||
35 | ) | ||
36 | |||
37 | var ( | ||
38 | bigSIExp = big.NewInt(1000) | ||
39 | |||
40 | // BigSIByte is one SI byte in big.Ints | ||
41 | BigSIByte = big.NewInt(1) | ||
42 | // BigKByte is 1,000 SI bytes in big.Ints | ||
43 | BigKByte = (&big.Int{}).Mul(BigSIByte, bigSIExp) | ||
44 | // BigMByte is 1,000 SI k bytes in big.Ints | ||
45 | BigMByte = (&big.Int{}).Mul(BigKByte, bigSIExp) | ||
46 | // BigGByte is 1,000 SI m bytes in big.Ints | ||
47 | BigGByte = (&big.Int{}).Mul(BigMByte, bigSIExp) | ||
48 | // BigTByte is 1,000 SI g bytes in big.Ints | ||
49 | BigTByte = (&big.Int{}).Mul(BigGByte, bigSIExp) | ||
50 | // BigPByte is 1,000 SI t bytes in big.Ints | ||
51 | BigPByte = (&big.Int{}).Mul(BigTByte, bigSIExp) | ||
52 | // BigEByte is 1,000 SI p bytes in big.Ints | ||
53 | BigEByte = (&big.Int{}).Mul(BigPByte, bigSIExp) | ||
54 | // BigZByte is 1,000 SI e bytes in big.Ints | ||
55 | BigZByte = (&big.Int{}).Mul(BigEByte, bigSIExp) | ||
56 | // BigYByte is 1,000 SI z bytes in big.Ints | ||
57 | BigYByte = (&big.Int{}).Mul(BigZByte, bigSIExp) | ||
58 | // BigRByte is 1,000 SI y bytes in big.Ints | ||
59 | BigRByte = (&big.Int{}).Mul(BigYByte, bigSIExp) | ||
60 | // BigQByte is 1,000 SI r bytes in big.Ints | ||
61 | BigQByte = (&big.Int{}).Mul(BigRByte, bigSIExp) | ||
62 | ) | ||
63 | |||
64 | var bigBytesSizeTable = map[string]*big.Int{ | ||
65 | "b": BigByte, | ||
66 | "kib": BigKiByte, | ||
67 | "kb": BigKByte, | ||
68 | "mib": BigMiByte, | ||
69 | "mb": BigMByte, | ||
70 | "gib": BigGiByte, | ||
71 | "gb": BigGByte, | ||
72 | "tib": BigTiByte, | ||
73 | "tb": BigTByte, | ||
74 | "pib": BigPiByte, | ||
75 | "pb": BigPByte, | ||
76 | "eib": BigEiByte, | ||
77 | "eb": BigEByte, | ||
78 | "zib": BigZiByte, | ||
79 | "zb": BigZByte, | ||
80 | "yib": BigYiByte, | ||
81 | "yb": BigYByte, | ||
82 | "rib": BigRiByte, | ||
83 | "rb": BigRByte, | ||
84 | "qib": BigQiByte, | ||
85 | "qb": BigQByte, | ||
86 | // Without suffix | ||
87 | "": BigByte, | ||
88 | "ki": BigKiByte, | ||
89 | "k": BigKByte, | ||
90 | "mi": BigMiByte, | ||
91 | "m": BigMByte, | ||
92 | "gi": BigGiByte, | ||
93 | "g": BigGByte, | ||
94 | "ti": BigTiByte, | ||
95 | "t": BigTByte, | ||
96 | "pi": BigPiByte, | ||
97 | "p": BigPByte, | ||
98 | "ei": BigEiByte, | ||
99 | "e": BigEByte, | ||
100 | "z": BigZByte, | ||
101 | "zi": BigZiByte, | ||
102 | "y": BigYByte, | ||
103 | "yi": BigYiByte, | ||
104 | "r": BigRByte, | ||
105 | "ri": BigRiByte, | ||
106 | "q": BigQByte, | ||
107 | "qi": BigQiByte, | ||
108 | } | ||
109 | |||
110 | var ten = big.NewInt(10) | ||
111 | |||
112 | func humanateBigBytes(s, base *big.Int, sizes []string) string { | ||
113 | if s.Cmp(ten) < 0 { | ||
114 | return fmt.Sprintf("%d B", s) | ||
115 | } | ||
116 | c := (&big.Int{}).Set(s) | ||
117 | val, mag := oomm(c, base, len(sizes)-1) | ||
118 | suffix := sizes[mag] | ||
119 | f := "%.0f %s" | ||
120 | if val < 10 { | ||
121 | f = "%.1f %s" | ||
122 | } | ||
123 | |||
124 | return fmt.Sprintf(f, val, suffix) | ||
125 | |||
126 | } | ||
127 | |||
128 | // BigBytes produces a human readable representation of an SI size. | ||
129 | // | ||
130 | // See also: ParseBigBytes. | ||
131 | // | ||
132 | // BigBytes(82854982) -> 83 MB | ||
133 | func BigBytes(s *big.Int) string { | ||
134 | sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", "RB", "QB"} | ||
135 | return humanateBigBytes(s, bigSIExp, sizes) | ||
136 | } | ||
137 | |||
138 | // BigIBytes produces a human readable representation of an IEC size. | ||
139 | // | ||
140 | // See also: ParseBigBytes. | ||
141 | // | ||
142 | // BigIBytes(82854982) -> 79 MiB | ||
143 | func BigIBytes(s *big.Int) string { | ||
144 | sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "RiB", "QiB"} | ||
145 | return humanateBigBytes(s, bigIECExp, sizes) | ||
146 | } | ||
147 | |||
148 | // ParseBigBytes parses a string representation of bytes into the number | ||
149 | // of bytes it represents. | ||
150 | // | ||
151 | // See also: BigBytes, BigIBytes. | ||
152 | // | ||
153 | // ParseBigBytes("42 MB") -> 42000000, nil | ||
154 | // ParseBigBytes("42 mib") -> 44040192, nil | ||
155 | func ParseBigBytes(s string) (*big.Int, error) { | ||
156 | lastDigit := 0 | ||
157 | hasComma := false | ||
158 | for _, r := range s { | ||
159 | if !(unicode.IsDigit(r) || r == '.' || r == ',') { | ||
160 | break | ||
161 | } | ||
162 | if r == ',' { | ||
163 | hasComma = true | ||
164 | } | ||
165 | lastDigit++ | ||
166 | } | ||
167 | |||
168 | num := s[:lastDigit] | ||
169 | if hasComma { | ||
170 | num = strings.Replace(num, ",", "", -1) | ||
171 | } | ||
172 | |||
173 | val := &big.Rat{} | ||
174 | _, err := fmt.Sscanf(num, "%f", val) | ||
175 | if err != nil { | ||
176 | return nil, err | ||
177 | } | ||
178 | |||
179 | extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) | ||
180 | if m, ok := bigBytesSizeTable[extra]; ok { | ||
181 | mv := (&big.Rat{}).SetInt(m) | ||
182 | val.Mul(val, mv) | ||
183 | rv := &big.Int{} | ||
184 | rv.Div(val.Num(), val.Denom()) | ||
185 | return rv, nil | ||
186 | } | ||
187 | |||
188 | return nil, fmt.Errorf("unhandled size name: %v", extra) | ||
189 | } | ||
diff --git a/vendor/github.com/dustin/go-humanize/bytes.go b/vendor/github.com/dustin/go-humanize/bytes.go new file mode 100644 index 0000000..0b498f4 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/bytes.go | |||
@@ -0,0 +1,143 @@ | |||
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 | } | ||
diff --git a/vendor/github.com/dustin/go-humanize/comma.go b/vendor/github.com/dustin/go-humanize/comma.go new file mode 100644 index 0000000..520ae3e --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/comma.go | |||
@@ -0,0 +1,116 @@ | |||
1 | package humanize | ||
2 | |||
3 | import ( | ||
4 | "bytes" | ||
5 | "math" | ||
6 | "math/big" | ||
7 | "strconv" | ||
8 | "strings" | ||
9 | ) | ||
10 | |||
11 | // Comma produces a string form of the given number in base 10 with | ||
12 | // commas after every three orders of magnitude. | ||
13 | // | ||
14 | // e.g. Comma(834142) -> 834,142 | ||
15 | func Comma(v int64) string { | ||
16 | sign := "" | ||
17 | |||
18 | // Min int64 can't be negated to a usable value, so it has to be special cased. | ||
19 | if v == math.MinInt64 { | ||
20 | return "-9,223,372,036,854,775,808" | ||
21 | } | ||
22 | |||
23 | if v < 0 { | ||
24 | sign = "-" | ||
25 | v = 0 - v | ||
26 | } | ||
27 | |||
28 | parts := []string{"", "", "", "", "", "", ""} | ||
29 | j := len(parts) - 1 | ||
30 | |||
31 | for v > 999 { | ||
32 | parts[j] = strconv.FormatInt(v%1000, 10) | ||
33 | switch len(parts[j]) { | ||
34 | case 2: | ||
35 | parts[j] = "0" + parts[j] | ||
36 | case 1: | ||
37 | parts[j] = "00" + parts[j] | ||
38 | } | ||
39 | v = v / 1000 | ||
40 | j-- | ||
41 | } | ||
42 | parts[j] = strconv.Itoa(int(v)) | ||
43 | return sign + strings.Join(parts[j:], ",") | ||
44 | } | ||
45 | |||
46 | // Commaf produces a string form of the given number in base 10 with | ||
47 | // commas after every three orders of magnitude. | ||
48 | // | ||
49 | // e.g. Commaf(834142.32) -> 834,142.32 | ||
50 | func Commaf(v float64) string { | ||
51 | buf := &bytes.Buffer{} | ||
52 | if v < 0 { | ||
53 | buf.Write([]byte{'-'}) | ||
54 | v = 0 - v | ||
55 | } | ||
56 | |||
57 | comma := []byte{','} | ||
58 | |||
59 | parts := strings.Split(strconv.FormatFloat(v, 'f', -1, 64), ".") | ||
60 | pos := 0 | ||
61 | if len(parts[0])%3 != 0 { | ||
62 | pos += len(parts[0]) % 3 | ||
63 | buf.WriteString(parts[0][:pos]) | ||
64 | buf.Write(comma) | ||
65 | } | ||
66 | for ; pos < len(parts[0]); pos += 3 { | ||
67 | buf.WriteString(parts[0][pos : pos+3]) | ||
68 | buf.Write(comma) | ||
69 | } | ||
70 | buf.Truncate(buf.Len() - 1) | ||
71 | |||
72 | if len(parts) > 1 { | ||
73 | buf.Write([]byte{'.'}) | ||
74 | buf.WriteString(parts[1]) | ||
75 | } | ||
76 | return buf.String() | ||
77 | } | ||
78 | |||
79 | // CommafWithDigits works like the Commaf but limits the resulting | ||
80 | // string to the given number of decimal places. | ||
81 | // | ||
82 | // e.g. CommafWithDigits(834142.32, 1) -> 834,142.3 | ||
83 | func CommafWithDigits(f float64, decimals int) string { | ||
84 | return stripTrailingDigits(Commaf(f), decimals) | ||
85 | } | ||
86 | |||
87 | // BigComma produces a string form of the given big.Int in base 10 | ||
88 | // with commas after every three orders of magnitude. | ||
89 | func BigComma(b *big.Int) string { | ||
90 | sign := "" | ||
91 | if b.Sign() < 0 { | ||
92 | sign = "-" | ||
93 | b.Abs(b) | ||
94 | } | ||
95 | |||
96 | athousand := big.NewInt(1000) | ||
97 | c := (&big.Int{}).Set(b) | ||
98 | _, m := oom(c, athousand) | ||
99 | parts := make([]string, m+1) | ||
100 | j := len(parts) - 1 | ||
101 | |||
102 | mod := &big.Int{} | ||
103 | for b.Cmp(athousand) >= 0 { | ||
104 | b.DivMod(b, athousand, mod) | ||
105 | parts[j] = strconv.FormatInt(mod.Int64(), 10) | ||
106 | switch len(parts[j]) { | ||
107 | case 2: | ||
108 | parts[j] = "0" + parts[j] | ||
109 | case 1: | ||
110 | parts[j] = "00" + parts[j] | ||
111 | } | ||
112 | j-- | ||
113 | } | ||
114 | parts[j] = strconv.Itoa(int(b.Int64())) | ||
115 | return sign + strings.Join(parts[j:], ",") | ||
116 | } | ||
diff --git a/vendor/github.com/dustin/go-humanize/commaf.go b/vendor/github.com/dustin/go-humanize/commaf.go new file mode 100644 index 0000000..2bc83a0 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/commaf.go | |||
@@ -0,0 +1,41 @@ | |||
1 | //go:build go1.6 | ||
2 | // +build go1.6 | ||
3 | |||
4 | package humanize | ||
5 | |||
6 | import ( | ||
7 | "bytes" | ||
8 | "math/big" | ||
9 | "strings" | ||
10 | ) | ||
11 | |||
12 | // BigCommaf produces a string form of the given big.Float in base 10 | ||
13 | // with commas after every three orders of magnitude. | ||
14 | func BigCommaf(v *big.Float) string { | ||
15 | buf := &bytes.Buffer{} | ||
16 | if v.Sign() < 0 { | ||
17 | buf.Write([]byte{'-'}) | ||
18 | v.Abs(v) | ||
19 | } | ||
20 | |||
21 | comma := []byte{','} | ||
22 | |||
23 | parts := strings.Split(v.Text('f', -1), ".") | ||
24 | pos := 0 | ||
25 | if len(parts[0])%3 != 0 { | ||
26 | pos += len(parts[0]) % 3 | ||
27 | buf.WriteString(parts[0][:pos]) | ||
28 | buf.Write(comma) | ||
29 | } | ||
30 | for ; pos < len(parts[0]); pos += 3 { | ||
31 | buf.WriteString(parts[0][pos : pos+3]) | ||
32 | buf.Write(comma) | ||
33 | } | ||
34 | buf.Truncate(buf.Len() - 1) | ||
35 | |||
36 | if len(parts) > 1 { | ||
37 | buf.Write([]byte{'.'}) | ||
38 | buf.WriteString(parts[1]) | ||
39 | } | ||
40 | return buf.String() | ||
41 | } | ||
diff --git a/vendor/github.com/dustin/go-humanize/ftoa.go b/vendor/github.com/dustin/go-humanize/ftoa.go new file mode 100644 index 0000000..bce923f --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/ftoa.go | |||
@@ -0,0 +1,49 @@ | |||
1 | package humanize | ||
2 | |||
3 | import ( | ||
4 | "strconv" | ||
5 | "strings" | ||
6 | ) | ||
7 | |||
8 | func stripTrailingZeros(s string) string { | ||
9 | if !strings.ContainsRune(s, '.') { | ||
10 | return s | ||
11 | } | ||
12 | offset := len(s) - 1 | ||
13 | for offset > 0 { | ||
14 | if s[offset] == '.' { | ||
15 | offset-- | ||
16 | break | ||
17 | } | ||
18 | if s[offset] != '0' { | ||
19 | break | ||
20 | } | ||
21 | offset-- | ||
22 | } | ||
23 | return s[:offset+1] | ||
24 | } | ||
25 | |||
26 | func stripTrailingDigits(s string, digits int) string { | ||
27 | if i := strings.Index(s, "."); i >= 0 { | ||
28 | if digits <= 0 { | ||
29 | return s[:i] | ||
30 | } | ||
31 | i++ | ||
32 | if i+digits >= len(s) { | ||
33 | return s | ||
34 | } | ||
35 | return s[:i+digits] | ||
36 | } | ||
37 | return s | ||
38 | } | ||
39 | |||
40 | // Ftoa converts a float to a string with no trailing zeros. | ||
41 | func Ftoa(num float64) string { | ||
42 | return stripTrailingZeros(strconv.FormatFloat(num, 'f', 6, 64)) | ||
43 | } | ||
44 | |||
45 | // FtoaWithDigits converts a float to a string but limits the resulting string | ||
46 | // to the given number of decimal places, and no trailing zeros. | ||
47 | func FtoaWithDigits(num float64, digits int) string { | ||
48 | return stripTrailingZeros(stripTrailingDigits(strconv.FormatFloat(num, 'f', 6, 64), digits)) | ||
49 | } | ||
diff --git a/vendor/github.com/dustin/go-humanize/humanize.go b/vendor/github.com/dustin/go-humanize/humanize.go new file mode 100644 index 0000000..a2c2da3 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/humanize.go | |||
@@ -0,0 +1,8 @@ | |||
1 | /* | ||
2 | Package humanize converts boring ugly numbers to human-friendly strings and back. | ||
3 | |||
4 | Durations can be turned into strings such as "3 days ago", numbers | ||
5 | representing sizes like 82854982 into useful strings like, "83 MB" or | ||
6 | "79 MiB" (whichever you prefer). | ||
7 | */ | ||
8 | package humanize | ||
diff --git a/vendor/github.com/dustin/go-humanize/number.go b/vendor/github.com/dustin/go-humanize/number.go new file mode 100644 index 0000000..6470d0d --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/number.go | |||
@@ -0,0 +1,192 @@ | |||
1 | package humanize | ||
2 | |||
3 | /* | ||
4 | Slightly adapted from the source to fit go-humanize. | ||
5 | |||
6 | Author: https://github.com/gorhill | ||
7 | Source: https://gist.github.com/gorhill/5285193 | ||
8 | |||
9 | */ | ||
10 | |||
11 | import ( | ||
12 | "math" | ||
13 | "strconv" | ||
14 | ) | ||
15 | |||
16 | var ( | ||
17 | renderFloatPrecisionMultipliers = [...]float64{ | ||
18 | 1, | ||
19 | 10, | ||
20 | 100, | ||
21 | 1000, | ||
22 | 10000, | ||
23 | 100000, | ||
24 | 1000000, | ||
25 | 10000000, | ||
26 | 100000000, | ||
27 | 1000000000, | ||
28 | } | ||
29 | |||
30 | renderFloatPrecisionRounders = [...]float64{ | ||
31 | 0.5, | ||
32 | 0.05, | ||
33 | 0.005, | ||
34 | 0.0005, | ||
35 | 0.00005, | ||
36 | 0.000005, | ||
37 | 0.0000005, | ||
38 | 0.00000005, | ||
39 | 0.000000005, | ||
40 | 0.0000000005, | ||
41 | } | ||
42 | ) | ||
43 | |||
44 | // FormatFloat produces a formatted number as string based on the following user-specified criteria: | ||
45 | // * thousands separator | ||
46 | // * decimal separator | ||
47 | // * decimal precision | ||
48 | // | ||
49 | // Usage: s := RenderFloat(format, n) | ||
50 | // The format parameter tells how to render the number n. | ||
51 | // | ||
52 | // See examples: http://play.golang.org/p/LXc1Ddm1lJ | ||
53 | // | ||
54 | // Examples of format strings, given n = 12345.6789: | ||
55 | // "#,###.##" => "12,345.67" | ||
56 | // "#,###." => "12,345" | ||
57 | // "#,###" => "12345,678" | ||
58 | // "#\u202F###,##" => "12โฏ345,68" | ||
59 | // "#.###,###### => 12.345,678900 | ||
60 | // "" (aka default format) => 12,345.67 | ||
61 | // | ||
62 | // The highest precision allowed is 9 digits after the decimal symbol. | ||
63 | // There is also a version for integer number, FormatInteger(), | ||
64 | // which is convenient for calls within template. | ||
65 | func FormatFloat(format string, n float64) string { | ||
66 | // Special cases: | ||
67 | // NaN = "NaN" | ||
68 | // +Inf = "+Infinity" | ||
69 | // -Inf = "-Infinity" | ||
70 | if math.IsNaN(n) { | ||
71 | return "NaN" | ||
72 | } | ||
73 | if n > math.MaxFloat64 { | ||
74 | return "Infinity" | ||
75 | } | ||
76 | if n < (0.0 - math.MaxFloat64) { | ||
77 | return "-Infinity" | ||
78 | } | ||
79 | |||
80 | // default format | ||
81 | precision := 2 | ||
82 | decimalStr := "." | ||
83 | thousandStr := "," | ||
84 | positiveStr := "" | ||
85 | negativeStr := "-" | ||
86 | |||
87 | if len(format) > 0 { | ||
88 | format := []rune(format) | ||
89 | |||
90 | // If there is an explicit format directive, | ||
91 | // then default values are these: | ||
92 | precision = 9 | ||
93 | thousandStr = "" | ||
94 | |||
95 | // collect indices of meaningful formatting directives | ||
96 | formatIndx := []int{} | ||
97 | for i, char := range format { | ||
98 | if char != '#' && char != '0' { | ||
99 | formatIndx = append(formatIndx, i) | ||
100 | } | ||
101 | } | ||
102 | |||
103 | if len(formatIndx) > 0 { | ||
104 | // Directive at index 0: | ||
105 | // Must be a '+' | ||
106 | // Raise an error if not the case | ||
107 | // index: 0123456789 | ||
108 | // +0.000,000 | ||
109 | // +000,000.0 | ||
110 | // +0000.00 | ||
111 | // +0000 | ||
112 | if formatIndx[0] == 0 { | ||
113 | if format[formatIndx[0]] != '+' { | ||
114 | panic("RenderFloat(): invalid positive sign directive") | ||
115 | } | ||
116 | positiveStr = "+" | ||
117 | formatIndx = formatIndx[1:] | ||
118 | } | ||
119 | |||
120 | // Two directives: | ||
121 | // First is thousands separator | ||
122 | // Raise an error if not followed by 3-digit | ||
123 | // 0123456789 | ||
124 | // 0.000,000 | ||
125 | // 000,000.00 | ||
126 | if len(formatIndx) == 2 { | ||
127 | if (formatIndx[1] - formatIndx[0]) != 4 { | ||
128 | panic("RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers") | ||
129 | } | ||
130 | thousandStr = string(format[formatIndx[0]]) | ||
131 | formatIndx = formatIndx[1:] | ||
132 | } | ||
133 | |||
134 | // One directive: | ||
135 | // Directive is decimal separator | ||
136 | // The number of digit-specifier following the separator indicates wanted precision | ||
137 | // 0123456789 | ||
138 | // 0.00 | ||
139 | // 000,0000 | ||
140 | if len(formatIndx) == 1 { | ||
141 | decimalStr = string(format[formatIndx[0]]) | ||
142 | precision = len(format) - formatIndx[0] - 1 | ||
143 | } | ||
144 | } | ||
145 | } | ||
146 | |||
147 | // generate sign part | ||
148 | var signStr string | ||
149 | if n >= 0.000000001 { | ||
150 | signStr = positiveStr | ||
151 | } else if n <= -0.000000001 { | ||
152 | signStr = negativeStr | ||
153 | n = -n | ||
154 | } else { | ||
155 | signStr = "" | ||
156 | n = 0.0 | ||
157 | } | ||
158 | |||
159 | // split number into integer and fractional parts | ||
160 | intf, fracf := math.Modf(n + renderFloatPrecisionRounders[precision]) | ||
161 | |||
162 | // generate integer part string | ||
163 | intStr := strconv.FormatInt(int64(intf), 10) | ||
164 | |||
165 | // add thousand separator if required | ||
166 | if len(thousandStr) > 0 { | ||
167 | for i := len(intStr); i > 3; { | ||
168 | i -= 3 | ||
169 | intStr = intStr[:i] + thousandStr + intStr[i:] | ||
170 | } | ||
171 | } | ||
172 | |||
173 | // no fractional part, we can leave now | ||
174 | if precision == 0 { | ||
175 | return signStr + intStr | ||
176 | } | ||
177 | |||
178 | // generate fractional part | ||
179 | fracStr := strconv.Itoa(int(fracf * renderFloatPrecisionMultipliers[precision])) | ||
180 | // may need padding | ||
181 | if len(fracStr) < precision { | ||
182 | fracStr = "000000000000000"[:precision-len(fracStr)] + fracStr | ||
183 | } | ||
184 | |||
185 | return signStr + intStr + decimalStr + fracStr | ||
186 | } | ||
187 | |||
188 | // FormatInteger produces a formatted number as string. | ||
189 | // See FormatFloat. | ||
190 | func FormatInteger(format string, n int) string { | ||
191 | return FormatFloat(format, float64(n)) | ||
192 | } | ||
diff --git a/vendor/github.com/dustin/go-humanize/ordinals.go b/vendor/github.com/dustin/go-humanize/ordinals.go new file mode 100644 index 0000000..43d88a8 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/ordinals.go | |||
@@ -0,0 +1,25 @@ | |||
1 | package humanize | ||
2 | |||
3 | import "strconv" | ||
4 | |||
5 | // Ordinal gives you the input number in a rank/ordinal format. | ||
6 | // | ||
7 | // Ordinal(3) -> 3rd | ||
8 | func Ordinal(x int) string { | ||
9 | suffix := "th" | ||
10 | switch x % 10 { | ||
11 | case 1: | ||
12 | if x%100 != 11 { | ||
13 | suffix = "st" | ||
14 | } | ||
15 | case 2: | ||
16 | if x%100 != 12 { | ||
17 | suffix = "nd" | ||
18 | } | ||
19 | case 3: | ||
20 | if x%100 != 13 { | ||
21 | suffix = "rd" | ||
22 | } | ||
23 | } | ||
24 | return strconv.Itoa(x) + suffix | ||
25 | } | ||
diff --git a/vendor/github.com/dustin/go-humanize/si.go b/vendor/github.com/dustin/go-humanize/si.go new file mode 100644 index 0000000..8b85019 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/si.go | |||
@@ -0,0 +1,127 @@ | |||
1 | package humanize | ||
2 | |||
3 | import ( | ||
4 | "errors" | ||
5 | "math" | ||
6 | "regexp" | ||
7 | "strconv" | ||
8 | ) | ||
9 | |||
10 | var siPrefixTable = map[float64]string{ | ||
11 | -30: "q", // quecto | ||
12 | -27: "r", // ronto | ||
13 | -24: "y", // yocto | ||
14 | -21: "z", // zepto | ||
15 | -18: "a", // atto | ||
16 | -15: "f", // femto | ||
17 | -12: "p", // pico | ||
18 | -9: "n", // nano | ||
19 | -6: "ยต", // micro | ||
20 | -3: "m", // milli | ||
21 | 0: "", | ||
22 | 3: "k", // kilo | ||
23 | 6: "M", // mega | ||
24 | 9: "G", // giga | ||
25 | 12: "T", // tera | ||
26 | 15: "P", // peta | ||
27 | 18: "E", // exa | ||
28 | 21: "Z", // zetta | ||
29 | 24: "Y", // yotta | ||
30 | 27: "R", // ronna | ||
31 | 30: "Q", // quetta | ||
32 | } | ||
33 | |||
34 | var revSIPrefixTable = revfmap(siPrefixTable) | ||
35 | |||
36 | // revfmap reverses the map and precomputes the power multiplier | ||
37 | func revfmap(in map[float64]string) map[string]float64 { | ||
38 | rv := map[string]float64{} | ||
39 | for k, v := range in { | ||
40 | rv[v] = math.Pow(10, k) | ||
41 | } | ||
42 | return rv | ||
43 | } | ||
44 | |||
45 | var riParseRegex *regexp.Regexp | ||
46 | |||
47 | func init() { | ||
48 | ri := `^([\-0-9.]+)\s?([` | ||
49 | for _, v := range siPrefixTable { | ||
50 | ri += v | ||
51 | } | ||
52 | ri += `]?)(.*)` | ||
53 | |||
54 | riParseRegex = regexp.MustCompile(ri) | ||
55 | } | ||
56 | |||
57 | // ComputeSI finds the most appropriate SI prefix for the given number | ||
58 | // and returns the prefix along with the value adjusted to be within | ||
59 | // that prefix. | ||
60 | // | ||
61 | // See also: SI, ParseSI. | ||
62 | // | ||
63 | // e.g. ComputeSI(2.2345e-12) -> (2.2345, "p") | ||
64 | func ComputeSI(input float64) (float64, string) { | ||
65 | if input == 0 { | ||
66 | return 0, "" | ||
67 | } | ||
68 | mag := math.Abs(input) | ||
69 | exponent := math.Floor(logn(mag, 10)) | ||
70 | exponent = math.Floor(exponent/3) * 3 | ||
71 | |||
72 | value := mag / math.Pow(10, exponent) | ||
73 | |||
74 | // Handle special case where value is exactly 1000.0 | ||
75 | // Should return 1 M instead of 1000 k | ||
76 | if value == 1000.0 { | ||
77 | exponent += 3 | ||
78 | value = mag / math.Pow(10, exponent) | ||
79 | } | ||
80 | |||
81 | value = math.Copysign(value, input) | ||
82 | |||
83 | prefix := siPrefixTable[exponent] | ||
84 | return value, prefix | ||
85 | } | ||
86 | |||
87 | // SI returns a string with default formatting. | ||
88 | // | ||
89 | // SI uses Ftoa to format float value, removing trailing zeros. | ||
90 | // | ||
91 | // See also: ComputeSI, ParseSI. | ||
92 | // | ||
93 | // e.g. SI(1000000, "B") -> 1 MB | ||
94 | // e.g. SI(2.2345e-12, "F") -> 2.2345 pF | ||
95 | func SI(input float64, unit string) string { | ||
96 | value, prefix := ComputeSI(input) | ||
97 | return Ftoa(value) + " " + prefix + unit | ||
98 | } | ||
99 | |||
100 | // SIWithDigits works like SI but limits the resulting string to the | ||
101 | // given number of decimal places. | ||
102 | // | ||
103 | // e.g. SIWithDigits(1000000, 0, "B") -> 1 MB | ||
104 | // e.g. SIWithDigits(2.2345e-12, 2, "F") -> 2.23 pF | ||
105 | func SIWithDigits(input float64, decimals int, unit string) string { | ||
106 | value, prefix := ComputeSI(input) | ||
107 | return FtoaWithDigits(value, decimals) + " " + prefix + unit | ||
108 | } | ||
109 | |||
110 | var errInvalid = errors.New("invalid input") | ||
111 | |||
112 | // ParseSI parses an SI string back into the number and unit. | ||
113 | // | ||
114 | // See also: SI, ComputeSI. | ||
115 | // | ||
116 | // e.g. ParseSI("2.2345 pF") -> (2.2345e-12, "F", nil) | ||
117 | func ParseSI(input string) (float64, string, error) { | ||
118 | found := riParseRegex.FindStringSubmatch(input) | ||
119 | if len(found) != 4 { | ||
120 | return 0, "", errInvalid | ||
121 | } | ||
122 | mag := revSIPrefixTable[found[2]] | ||
123 | unit := found[3] | ||
124 | |||
125 | base, err := strconv.ParseFloat(found[1], 64) | ||
126 | return base * mag, unit, err | ||
127 | } | ||
diff --git a/vendor/github.com/dustin/go-humanize/times.go b/vendor/github.com/dustin/go-humanize/times.go new file mode 100644 index 0000000..dd3fbf5 --- /dev/null +++ b/vendor/github.com/dustin/go-humanize/times.go | |||
@@ -0,0 +1,117 @@ | |||
1 | package humanize | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "math" | ||
6 | "sort" | ||
7 | "time" | ||
8 | ) | ||
9 | |||
10 | // Seconds-based time units | ||
11 | const ( | ||
12 | Day = 24 * time.Hour | ||
13 | Week = 7 * Day | ||
14 | Month = 30 * Day | ||
15 | Year = 12 * Month | ||
16 | LongTime = 37 * Year | ||
17 | ) | ||
18 | |||
19 | // Time formats a time into a relative string. | ||
20 | // | ||
21 | // Time(someT) -> "3 weeks ago" | ||
22 | func Time(then time.Time) string { | ||
23 | return RelTime(then, time.Now(), "ago", "from now") | ||
24 | } | ||
25 | |||
26 | // A RelTimeMagnitude struct contains a relative time point at which | ||
27 | // the relative format of time will switch to a new format string. A | ||
28 | // slice of these in ascending order by their "D" field is passed to | ||
29 | // CustomRelTime to format durations. | ||
30 | // | ||
31 | // The Format field is a string that may contain a "%s" which will be | ||
32 | // replaced with the appropriate signed label (e.g. "ago" or "from | ||
33 | // now") and a "%d" that will be replaced by the quantity. | ||
34 | // | ||
35 | // The DivBy field is the amount of time the time difference must be | ||
36 | // divided by in order to display correctly. | ||
37 | // | ||
38 | // e.g. if D is 2*time.Minute and you want to display "%d minutes %s" | ||
39 | // DivBy should be time.Minute so whatever the duration is will be | ||
40 | // expressed in minutes. | ||
41 | type RelTimeMagnitude struct { | ||
42 | D time.Duration | ||
43 | Format string | ||
44 | DivBy time.Duration | ||
45 | } | ||
46 | |||
47 | var defaultMagnitudes = []RelTimeMagnitude{ | ||
48 | {time.Second, "now", time.Second}, | ||
49 | {2 * time.Second, "1 second %s", 1}, | ||
50 | {time.Minute, "%d seconds %s", time.Second}, | ||
51 | {2 * time.Minute, "1 minute %s", 1}, | ||
52 | {time.Hour, "%d minutes %s", time.Minute}, | ||
53 | {2 * time.Hour, "1 hour %s", 1}, | ||
54 | {Day, "%d hours %s", time.Hour}, | ||
55 | {2 * Day, "1 day %s", 1}, | ||
56 | {Week, "%d days %s", Day}, | ||
57 | {2 * Week, "1 week %s", 1}, | ||
58 | {Month, "%d weeks %s", Week}, | ||
59 | {2 * Month, "1 month %s", 1}, | ||
60 | {Year, "%d months %s", Month}, | ||
61 | {18 * Month, "1 year %s", 1}, | ||
62 | {2 * Year, "2 years %s", 1}, | ||
63 | {LongTime, "%d years %s", Year}, | ||
64 | {math.MaxInt64, "a long while %s", 1}, | ||
65 | } | ||
66 | |||
67 | // RelTime formats a time into a relative string. | ||
68 | // | ||
69 | // It takes two times and two labels. In addition to the generic time | ||
70 | // delta string (e.g. 5 minutes), the labels are used applied so that | ||
71 | // the label corresponding to the smaller time is applied. | ||
72 | // | ||
73 | // RelTime(timeInPast, timeInFuture, "earlier", "later") -> "3 weeks earlier" | ||
74 | func RelTime(a, b time.Time, albl, blbl string) string { | ||
75 | return CustomRelTime(a, b, albl, blbl, defaultMagnitudes) | ||
76 | } | ||
77 | |||
78 | // CustomRelTime formats a time into a relative string. | ||
79 | // | ||
80 | // It takes two times two labels and a table of relative time formats. | ||
81 | // In addition to the generic time delta string (e.g. 5 minutes), the | ||
82 | // labels are used applied so that the label corresponding to the | ||
83 | // smaller time is applied. | ||
84 | func CustomRelTime(a, b time.Time, albl, blbl string, magnitudes []RelTimeMagnitude) string { | ||
85 | lbl := albl | ||
86 | diff := b.Sub(a) | ||
87 | |||
88 | if a.After(b) { | ||
89 | lbl = blbl | ||
90 | diff = a.Sub(b) | ||
91 | } | ||
92 | |||
93 | n := sort.Search(len(magnitudes), func(i int) bool { | ||
94 | return magnitudes[i].D > diff | ||
95 | }) | ||
96 | |||
97 | if n >= len(magnitudes) { | ||
98 | n = len(magnitudes) - 1 | ||
99 | } | ||
100 | mag := magnitudes[n] | ||
101 | args := []interface{}{} | ||
102 | escaped := false | ||
103 | for _, ch := range mag.Format { | ||
104 | if escaped { | ||
105 | switch ch { | ||
106 | case 's': | ||
107 | args = append(args, lbl) | ||
108 | case 'd': | ||
109 | args = append(args, diff/mag.DivBy) | ||
110 | } | ||
111 | escaped = false | ||
112 | } else { | ||
113 | escaped = ch == '%' | ||
114 | } | ||
115 | } | ||
116 | return fmt.Sprintf(mag.Format, args...) | ||
117 | } | ||