github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/util/humanizeutil/humanize.go (about) 1 // Copyright 2016 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package humanizeutil 12 13 import ( 14 "flag" 15 "fmt" 16 "math" 17 "sync/atomic" 18 "time" 19 20 "github.com/dustin/go-humanize" 21 "github.com/spf13/pflag" 22 ) 23 24 // IBytes is an int64 version of go-humanize's IBytes. 25 func IBytes(value int64) string { 26 if value < 0 { 27 return fmt.Sprintf("-%s", humanize.IBytes(uint64(-value))) 28 } 29 return humanize.IBytes(uint64(value)) 30 } 31 32 // ParseBytes is an int64 version of go-humanize's ParseBytes. 33 func ParseBytes(s string) (int64, error) { 34 if len(s) == 0 { 35 return 0, fmt.Errorf("parsing \"\": invalid syntax") 36 } 37 var startIndex int 38 var negative bool 39 if s[0] == '-' { 40 negative = true 41 startIndex = 1 42 } 43 value, err := humanize.ParseBytes(s[startIndex:]) 44 if err != nil { 45 return 0, err 46 } 47 if value > math.MaxInt64 { 48 return 0, fmt.Errorf("too large: %s", s) 49 } 50 if negative { 51 return -int64(value), nil 52 } 53 return int64(value), nil 54 } 55 56 // BytesValue is a struct that implements flag.Value and pflag.Value 57 // suitable to create command-line parameters that accept sizes 58 // specified using a format recognized by humanize. 59 // The value is written atomically, so that it is safe to use this 60 // struct to make a parameter configurable that is used by an 61 // asynchronous process spawned before command-line argument handling. 62 // This is useful e.g. for the log file settings which are used 63 // by the asynchronous log file GC daemon. 64 type BytesValue struct { 65 val *int64 66 isSet bool 67 } 68 69 var _ flag.Value = &BytesValue{} 70 var _ pflag.Value = &BytesValue{} 71 72 // NewBytesValue creates a new pflag.Value bound to the specified 73 // int64 variable. It also happens to be a flag.Value. 74 func NewBytesValue(val *int64) *BytesValue { 75 return &BytesValue{val: val} 76 } 77 78 // Set implements the flag.Value and pflag.Value interfaces. 79 func (b *BytesValue) Set(s string) error { 80 v, err := ParseBytes(s) 81 if err != nil { 82 return err 83 } 84 atomic.StoreInt64(b.val, v) 85 b.isSet = true 86 return nil 87 } 88 89 // Type implements the pflag.Value interface. 90 func (b *BytesValue) Type() string { 91 return "bytes" 92 } 93 94 // String implements the flag.Value and pflag.Value interfaces. 95 func (b *BytesValue) String() string { 96 // We need to be able to print the zero value in order for go's flags 97 // package to not choke when comparing values to the zero value, 98 // as it does in isZeroValue as of go1.8: 99 // https://github.com/golang/go/blob/release-branch.go1.8/src/flag/flag.go#L384 100 if b.val == nil { 101 return IBytes(0) 102 } 103 // This uses the MiB, GiB, etc suffixes. If we use humanize.Bytes() we get 104 // the MB, GB, etc suffixes, but the conversion is done in multiples of 1000 105 // vs 1024. 106 return IBytes(atomic.LoadInt64(b.val)) 107 } 108 109 // IsSet returns true iff Set has successfully been called. 110 func (b *BytesValue) IsSet() bool { 111 return b.isSet 112 } 113 114 // DataRate formats the passed byte count over duration as "x MiB/s". 115 func DataRate(bytes int64, elapsed time.Duration) string { 116 if bytes == 0 { 117 return "0" 118 } 119 if elapsed == 0 { 120 return "inf" 121 } 122 return fmt.Sprintf("%0.2f MiB/s", (float64(bytes)/elapsed.Seconds())/float64(1<<20)) 123 }