-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgotty.go
114 lines (96 loc) · 3.06 KB
/
gotty.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package gotty
import (
"errors"
"fmt"
"math"
"strconv"
)
var (
byteUnits = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
bibyteUnits = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
bitUnits = []string{"b", "kbit", "Mbit", "Gbit", "Tbit", "Pbit", "Ebit", "Zbit", "Ybit"}
bibitUnits = []string{"b", "kibit", "Mibit", "Gibit", "Tibit", "Pibit", "Eibit", "Zibit", "Yibit"}
)
// Options represents the options for formatting the byte size.
type Options struct {
Bits bool
Binary bool
Space bool
Signed bool
Locale string
MinimumFractionDigits int
MaximumFractionDigits int
}
// Format formats the given byte size as a human-readable string.
// If the options parameter is nil, default options will be used.
func Format(byteSize float64, options *Options) (string, error) {
if options == nil {
options = &Options{}
}
if !isFinite(byteSize) {
return "", errors.New(fmt.Sprintf("expected a finite number, got %T: %v", byteSize, byteSize))
}
units := byteUnits
if options.Bits {
if options.Binary {
units = bibitUnits
} else {
units = bitUnits
}
} else if options.Binary {
units = bibyteUnits
}
separator := ""
if options.Space {
separator = " "
}
if options.Signed && byteSize == 0 {
return "0" + separator + units[0], nil
}
isNegative := byteSize < 0
prefix := ""
if isNegative {
prefix = "-"
byteSize = -byteSize
} else if options.Signed {
prefix = "+"
}
localeOptions := make(map[string]interface{})
if options.MinimumFractionDigits != 0 {
localeOptions["minimumFractionDigits"] = options.MinimumFractionDigits
}
if options.MaximumFractionDigits != 0 {
localeOptions["maximumFractionDigits"] = options.MaximumFractionDigits
}
if byteSize < 1 {
numberString := formatNumber(byteSize, options.Locale, localeOptions)
return prefix + numberString + separator + units[0], nil
}
var exponent int
if options.Binary {
exponent = int(math.Min(math.Floor(math.Log(byteSize)/math.Log(1024)), float64(len(units)-1)))
byteSize /= math.Pow(1024, float64(exponent))
} else {
exponent = int(math.Min(math.Floor(math.Log10(byteSize)/3), float64(len(units)-1)))
byteSize /= math.Pow(1000, float64(exponent))
}
if len(localeOptions) == 0 {
byteSize = roundToPrecision(byteSize, 3)
}
numberString := formatNumber(byteSize, options.Locale, localeOptions)
unit := units[exponent]
return prefix + numberString + separator + unit, nil
}
// isFinite checks if a given number is finite.
func isFinite(number float64) bool {
return !math.IsInf(number, 0) && !math.IsNaN(number)
}
// roundToPrecision rounds a given number to a specified precision.
func roundToPrecision(number float64, precision int) float64 {
scale := math.Pow(10, float64(precision))
return math.Round(number*scale) / scale
}
// formatNumber formats a given number according to the specified locale and options.
func formatNumber(number float64, locale string, options map[string]interface{}) string {
return strconv.FormatFloat(number, 'f', -1, 64)
}