11package byte
22
33import (
4- "encoding/json"
5- "errors"
64 "fmt"
7- "regexp"
85 "strconv"
9- "strings"
106)
117
12- // ParseErr returned invalid strings are parsed
13- type ParseErr struct {
14- Func string
15- Str string
16- Err error
17- }
18-
19- func (e * ParseErr ) String () string {
20- marshaledStruct , err := json .Marshal (e )
21- if err != nil {
22- return err .Error ()
23- }
24- return string (marshaledStruct )
25- }
26-
27- func (e * ParseErr ) Error () string {
28- return "bytes." + e .Func + ": " + "parsing " + strconv .Quote (e .Str ) + ": " + e .Err .Error ()
29- }
30-
31- // BytesUnit represents a unit of magnitude for Bytes in either SI (base-10) or NIST (base-2)
32- type BytesUnit int64
33-
34- const (
35- // B byte (Unit)
36- B BytesUnit = 1
37- // KB Kilobyte (SI) - 1000 bytes
38- KB BytesUnit = 1000
39- // MB Megabyte (SI) - 1e6 bytes
40- MB BytesUnit = 1e6
41- // GB Gigabyte (SI) - 1e9 bytes
42- GB BytesUnit = 1e9
43- // TB Terabyte (SI) - 1e12 bytes
44- TB BytesUnit = 1e12
45- // PB Petabyte (SI) - 1e15 bytes
46- PB BytesUnit = 1e15
47-
48- // KiB Kibibyte (NIST) - 1024 bytes
49- KiB BytesUnit = 1024
50- // MiB Mebibyte (NIST) - 2^20 bytes
51- MiB = 1024 * KiB
52- // GiB Gibibyte (NIST) - 2^30 bytes
53- GiB = 1024 * MiB
54- // TiB Tebibyte (NIST) - 2^40 bytes
55- TiB = 1024 * GiB
56- // PiB Pebibyte (NIST) - 2^50 bytes
57- PiB = 1024 * TiB
58- )
59-
60- var strToByteUnit = map [string ]BytesUnit {
61- "" : B ,
62- "B" : B ,
63- "KB" : KB ,
64- "KIB" : KiB ,
65- "MB" : MB ,
66- "MIB" : MiB ,
67- "GB" : GB ,
68- "GIB" : GiB ,
69- "TB" : TB ,
70- "TIB" : TiB ,
71- "PB" : PB ,
72- "PIB" : PiB ,
73- }
74-
75- var byteUnitToStr = map [BytesUnit ]string {
76- B : "B" ,
77- KB : "KB" ,
78- KiB : "KiB" ,
79- MB : "MB" ,
80- MiB : "MiB" ,
81- GB : "GB" ,
82- GiB : "GiB" ,
83- TB : "TB" ,
84- TiB : "TiB" ,
85- PB : "PB" ,
86- PiB : "PiB" ,
87- }
88-
89- var bytesUnitThresholds = []struct {
90- unit BytesUnit
91- less Bytes
92- }{
93- {B , 0x400 },
94- {KiB , Bytes (uint64 (MiB ) >> 4 )},
95- {MiB , Bytes (uint64 (GiB ) >> 4 )},
96- {GiB , Bytes (uint64 (TiB ) >> 4 )},
97- {TiB , Bytes (uint64 (PiB ) >> 4 )},
98- {PiB , 0x7FFFFFFFFFFFFFFF },
99- }
100- var bytesSIUnitThresholds = []struct {
101- unit BytesUnit
102- less Bytes
103- }{
104- {B , 5e2 },
105- {KB , 5e5 },
106- {MB , 5e8 },
107- {GB , 5e11 },
108- {TB , 5e14 },
109- {PB , 0x7FFFFFFFFFFFFFFF },
110- }
111-
112- // Convert a byte count into the (possibly fractional) count in this unit
113- func (u BytesUnit ) Convert (b Bytes ) float64 {
114- if u == B {
115- return float64 (b )
116- }
117- return float64 (b ) / float64 (u )
118- }
119-
120- func (u BytesUnit ) String () string {
121- return byteUnitToStr [u ]
122- }
123-
1248// Bytes is a formattable byte count
1259type Bytes int64
12610
@@ -132,20 +16,20 @@ func (b Bytes) Format(s fmt.State, verb rune) {
13216 case 's' :
13317 fallthrough
13418 case 'v' :
135- if b < 0 {
19+ bCopy := b
20+ if bCopy < 0 {
13621 _ , _ = s .Write ([]byte {'-' })
137- b = b * - 1
22+ bCopy = b * - 1
13823 }
13924 thresholds := bytesUnitThresholds
14025 if s .Flag ('+' ) {
14126 thresholds = bytesSIUnitThresholds
14227 }
14328 for _ , t := range thresholds {
144- if b < t .less {
145- f := t .unit .Convert (b )
29+ if bCopy < t .less {
30+ f := t .unit .Convert (bCopy )
14631 if prec , ok := s .Precision (); ok {
147- format := fmt .Sprintf ("%%.%df %%s" , prec )
148- _ , _ = fmt .Fprintf (s , format , f , t .unit )
32+ _ , _ = fmt .Fprintf (s , fmt .Sprintf ("%%.%df %%s" , prec ), f , t .unit )
14933 } else {
15034 _ , _ = fmt .Fprintf (s , "%s %s" , strconv .FormatFloat (f , 'f' , - 1 , 64 ), t .unit )
15135 }
@@ -158,50 +42,3 @@ func (b Bytes) Format(s fmt.State, verb rune) {
15842func (b Bytes ) String () string {
15943 return fmt .Sprintf ("%s" , b )
16044}
161-
162- var parseBytesRegex = regexp .MustCompile (`(?i)(?P<value>[0-9.+eE+-]+)\s*(?P<unit>[a-z]?(?:i?b)?)?` )
163-
164- // ParseBytes parses the given string with byte units into a Byte count
165- func ParseBytes (s string ) (Bytes , error ) {
166- gs := parseBytesRegex .FindStringSubmatch (s )
167- if len (gs ) == 0 {
168- return 0 , & ParseErr {
169- Func : "ParseBytes" ,
170- Str : s ,
171- Err : errors .New ("bad string format" ),
172- }
173- }
174- vs := strings .ToLower (gs [1 ])
175- us := gs [2 ]
176-
177- unit , ok := strToByteUnit [strings .ToUpper (strings .TrimSpace (us ))]
178- if ! ok {
179- return 0 , & ParseErr {
180- Func : "ParseBytes" ,
181- Str : s ,
182- Err : errors .New ("unknown bytes unit" ),
183- }
184- }
185-
186- vs = strings .Replace (vs , "," , "" , - 1 )
187- if strings .Contains (vs , "." ) || strings .Contains (vs , "e" ) {
188- f , err := strconv .ParseFloat (vs , 64 )
189- if err != nil {
190- return 0 , & ParseErr {
191- Func : "ParseBytes" ,
192- Str : s ,
193- Err : err ,
194- }
195- }
196- return Bytes (f * float64 (unit )), nil
197- }
198- i , err := strconv .ParseInt (vs , 10 , 64 )
199- if err != nil {
200- return 0 , & ParseErr {
201- Func : "ParseBytes" ,
202- Str : s ,
203- Err : err ,
204- }
205- }
206- return Bytes (i * int64 (unit )), nil
207- }
0 commit comments