@@ -10,66 +10,151 @@ import (
1010type (
1111 // Bytes struct
1212 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
1323)
1424
25+ // binary units (IEC 60027)
1526const (
1627 _ = 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
2344)
2445
2546var (
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 ()
2850)
2951
3052// New creates a Bytes instance.
3153func New () * Bytes {
3254 return & Bytes {}
3355}
3456
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.
3674// For example, 31323 bytes will return 30.59KB.
37- func (* Bytes ) Format ( b int64 ) string {
75+ func (* Bytes ) FormatBinary ( value int64 ) string {
3876 multiple := ""
39- value := float64 (b )
77+ val := float64 (value )
4078
4179 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
44116 multiple = "EB"
45- case b >= PB :
46- value /= PB
117+ case value >= PB :
118+ val /= PB
47119 multiple = "PB"
48- case b >= TB :
49- value /= TB
120+ case value >= TB :
121+ val /= TB
50122 multiple = "TB"
51- case b >= GB :
52- value /= GB
123+ case value >= GB :
124+ val /= GB
53125 multiple = "GB"
54- case b >= MB :
55- value /= MB
126+ case value >= MB :
127+ val /= MB
56128 multiple = "MB"
57- case b >= KB :
58- value /= KB
129+ case value >= KB :
130+ val /= KB
59131 multiple = "KB"
60- case b == 0 :
132+ case value == 0 :
61133 return "0"
62134 default :
63- return strconv .FormatInt (b , 10 ) + "B"
135+ return strconv .FormatInt (value , 10 ) + "B"
64136 }
65137
66- return fmt .Sprintf ("%.2f%s" , value , multiple )
138+ return fmt .Sprintf ("%.2f%s" , val , multiple )
67139}
68140
69141// 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 )
73158 if len (parts ) < 3 {
74159 return 0 , fmt .Errorf ("error parsing value=%s" , value )
75160 }
@@ -81,8 +166,38 @@ func (*Bytes) Parse(value string) (i int64, err error) {
81166 }
82167
83168 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
84181 default :
85182 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 {
86201 case "K" , "KB" :
87202 return int64 (bytes * KB ), nil
88203 case "M" , "MB" :
@@ -95,15 +210,27 @@ func (*Bytes) Parse(value string) (i int64, err error) {
95210 return int64 (bytes * PB ), nil
96211 case "E" , "EB" :
97212 return int64 (bytes * EB ), nil
213+ default :
214+ return int64 (bytes ), nil
98215 }
99216}
100217
101218// 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 )
104231}
105232
106233// 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 )
109236}
0 commit comments