@@ -10,66 +10,151 @@ import (
10
10
type (
11
11
// Bytes struct
12
12
Bytes struct {}
13
+
14
+ // PrefixType is the type of the unit prefix (binary/decimal)
15
+ PrefixType byte
16
+ )
17
+
18
+ const (
19
+ // IEC 60027
20
+ PrefixTypeBinary PrefixType = iota
21
+ // SI international system of units
22
+ PrefixTypeDecimal
13
23
)
14
24
25
+ // binary units (IEC 60027)
15
26
const (
16
27
_ = 1.0 << (10 * iota ) // ignore first value by assigning to blank identifier
17
- KB
18
- MB
19
- GB
20
- TB
21
- PB
22
- EB
28
+ KiB
29
+ MiB
30
+ GiB
31
+ TiB
32
+ PiB
33
+ EiB
34
+ )
35
+
36
+ // decimal units (SI international system of units)
37
+ const (
38
+ KB = 1000
39
+ MB = KB * 1000
40
+ GB = MB * 1000
41
+ TB = GB * 1000
42
+ PB = TB * 1000
43
+ EB = PB * 1000
23
44
)
24
45
25
46
var (
26
- pattern = regexp .MustCompile (`(?i)^(-?\d+(?:\.\d+)?)\s?([KMGTPE]B?|B?)$` )
27
- global = New ()
47
+ patternBinary = regexp .MustCompile (`(?i)^(-?\d+(?:\.\d+)?)\s?([KMGTPE]iB?)$` )
48
+ patternDecimal = regexp .MustCompile (`(?i)^(-?\d+(?:\.\d+)?)\s?([KMGTPE]B?|B?)$` )
49
+ global = New ()
28
50
)
29
51
30
52
// New creates a Bytes instance.
31
53
func New () * Bytes {
32
54
return & Bytes {}
33
55
}
34
56
35
- // Format formats bytes integer to human readable string.
57
+ // Format formats bytes integer to human readable string according to the given prefix type.
58
+ // If prefixType is not passed, binary prefix is used.
59
+ func (b * Bytes ) Format (value int64 , prefixType ... PrefixType ) string {
60
+
61
+ if len (prefixType ) > 0 {
62
+ switch prefixType [0 ] {
63
+ case PrefixTypeBinary :
64
+ return b .FormatBinary (value )
65
+ case PrefixTypeDecimal :
66
+ return b .FormatDecimal (value )
67
+ }
68
+ }
69
+
70
+ return b .FormatBinary (value )
71
+ }
72
+
73
+ // FormatBinary formats bytes integer to human readable string according to IEC 60027.
36
74
// For example, 31323 bytes will return 30.59KB.
37
- func (* Bytes ) Format ( b int64 ) string {
75
+ func (* Bytes ) FormatBinary ( value int64 ) string {
38
76
multiple := ""
39
- value := float64 (b )
77
+ val := float64 (value )
40
78
41
79
switch {
42
- case b >= EB :
43
- value /= EB
80
+ case value >= EiB :
81
+ val /= EiB
82
+ multiple = "EiB"
83
+ case value >= PiB :
84
+ val /= PiB
85
+ multiple = "PiB"
86
+ case value >= TiB :
87
+ val /= TiB
88
+ multiple = "TiB"
89
+ case value >= GiB :
90
+ val /= GiB
91
+ multiple = "GiB"
92
+ case value >= MiB :
93
+ val /= MiB
94
+ multiple = "MiB"
95
+ case value >= KiB :
96
+ val /= KiB
97
+ multiple = "KiB"
98
+ case value == 0 :
99
+ return "0"
100
+ default :
101
+ return strconv .FormatInt (value , 10 ) + "B"
102
+ }
103
+
104
+ return fmt .Sprintf ("%.2f%s" , val , multiple )
105
+ }
106
+
107
+ // FormatDecimal formats bytes integer to human readable string according to SI international system of units.
108
+ // For example, 31323 bytes will return 31.32KB.
109
+ func (* Bytes ) FormatDecimal (value int64 ) string {
110
+ multiple := ""
111
+ val := float64 (value )
112
+
113
+ switch {
114
+ case value >= EB :
115
+ val /= EB
44
116
multiple = "EB"
45
- case b >= PB :
46
- value /= PB
117
+ case value >= PB :
118
+ val /= PB
47
119
multiple = "PB"
48
- case b >= TB :
49
- value /= TB
120
+ case value >= TB :
121
+ val /= TB
50
122
multiple = "TB"
51
- case b >= GB :
52
- value /= GB
123
+ case value >= GB :
124
+ val /= GB
53
125
multiple = "GB"
54
- case b >= MB :
55
- value /= MB
126
+ case value >= MB :
127
+ val /= MB
56
128
multiple = "MB"
57
- case b >= KB :
58
- value /= KB
129
+ case value >= KB :
130
+ val /= KB
59
131
multiple = "KB"
60
- case b == 0 :
132
+ case value == 0 :
61
133
return "0"
62
134
default :
63
- return strconv .FormatInt (b , 10 ) + "B"
135
+ return strconv .FormatInt (value , 10 ) + "B"
64
136
}
65
137
66
- return fmt .Sprintf ("%.2f%s" , value , multiple )
138
+ return fmt .Sprintf ("%.2f%s" , val , multiple )
67
139
}
68
140
69
141
// Parse parses human readable bytes string to bytes integer.
70
- // For example, 6GB (6G is also valid) will return 6442450944.
71
- func (* Bytes ) Parse (value string ) (i int64 , err error ) {
72
- parts := pattern .FindStringSubmatch (value )
142
+ // For example, 6GiB (6Gi is also valid) will return 6442450944, and
143
+ // 6GB (6G is also valid) will return 6000000000.
144
+ func (b * Bytes ) Parse (value string ) (int64 , error ) {
145
+
146
+ i , err := b .ParseBinary (value )
147
+ if err == nil {
148
+ return i , err
149
+ }
150
+
151
+ return b .ParseDecimal (value )
152
+ }
153
+
154
+ // ParseBinary parses human readable bytes string to bytes integer.
155
+ // For example, 6GiB (6Gi is also valid) will return 6442450944.
156
+ func (* Bytes ) ParseBinary (value string ) (i int64 , err error ) {
157
+ parts := patternBinary .FindStringSubmatch (value )
73
158
if len (parts ) < 3 {
74
159
return 0 , fmt .Errorf ("error parsing value=%s" , value )
75
160
}
@@ -81,8 +166,38 @@ func (*Bytes) Parse(value string) (i int64, err error) {
81
166
}
82
167
83
168
switch multiple {
169
+ case "KI" , "KIB" :
170
+ return int64 (bytes * KiB ), nil
171
+ case "MI" , "MIB" :
172
+ return int64 (bytes * MiB ), nil
173
+ case "GI" , "GIB" :
174
+ return int64 (bytes * GiB ), nil
175
+ case "TI" , "TIB" :
176
+ return int64 (bytes * TiB ), nil
177
+ case "PI" , "PIB" :
178
+ return int64 (bytes * PiB ), nil
179
+ case "EI" , "EIB" :
180
+ return int64 (bytes * EiB ), nil
84
181
default :
85
182
return int64 (bytes ), nil
183
+ }
184
+ }
185
+
186
+ // ParseDecimal parses human readable bytes string to bytes integer.
187
+ // For example, 6GB (6G is also valid) will return 6000000000.
188
+ func (* Bytes ) ParseDecimal (value string ) (i int64 , err error ) {
189
+ parts := patternDecimal .FindStringSubmatch (value )
190
+ if len (parts ) < 3 {
191
+ return 0 , fmt .Errorf ("error parsing value=%s" , value )
192
+ }
193
+ bytesString := parts [1 ]
194
+ multiple := strings .ToUpper (parts [2 ])
195
+ bytes , err := strconv .ParseFloat (bytesString , 64 )
196
+ if err != nil {
197
+ return
198
+ }
199
+
200
+ switch multiple {
86
201
case "K" , "KB" :
87
202
return int64 (bytes * KB ), nil
88
203
case "M" , "MB" :
@@ -95,15 +210,27 @@ func (*Bytes) Parse(value string) (i int64, err error) {
95
210
return int64 (bytes * PB ), nil
96
211
case "E" , "EB" :
97
212
return int64 (bytes * EB ), nil
213
+ default :
214
+ return int64 (bytes ), nil
98
215
}
99
216
}
100
217
101
218
// Format wraps global Bytes's Format function.
102
- func Format (b int64 ) string {
103
- return global .Format (b )
219
+ func Format (value int64 , prefixType ... PrefixType ) string {
220
+ return global .Format (value , prefixType ... )
221
+ }
222
+
223
+ // FormatBinary wraps global Bytes's FormatBinary function.
224
+ func FormatBinary (value int64 ) string {
225
+ return global .FormatBinary (value )
226
+ }
227
+
228
+ // FormatDecimal wraps global Bytes's FormatDecimal function.
229
+ func FormatDecimal (value int64 ) string {
230
+ return global .FormatDecimal (value )
104
231
}
105
232
106
233
// Parse wraps global Bytes's Parse function.
107
- func Parse (val string ) (int64 , error ) {
108
- return global .Parse (val )
234
+ func Parse (value string ) (int64 , error ) {
235
+ return global .Parse (value )
109
236
}
0 commit comments