github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/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/cockroachdb/errors" 21 "github.com/cockroachdb/redact" 22 "github.com/dustin/go-humanize" 23 "github.com/spf13/pflag" 24 ) 25 26 // IBytes is an int64 version of go-humanize's IBytes. 27 func IBytes(value int64) redact.SafeString { 28 if value < 0 { 29 return redact.SafeString("-" + humanize.IBytes(uint64(-value))) 30 } 31 return redact.SafeString(humanize.IBytes(uint64(value))) 32 } 33 34 // ParseBytes is an int64 version of go-humanize's ParseBytes. 35 func ParseBytes(s string) (int64, error) { 36 if len(s) == 0 { 37 return 0, errors.New("parsing \"\": invalid syntax") 38 } 39 var startIndex int 40 var negative bool 41 if s[0] == '-' { 42 negative = true 43 startIndex = 1 44 } 45 value, err := humanize.ParseBytes(s[startIndex:]) 46 if err != nil { 47 return 0, err 48 } 49 if value > math.MaxInt64 { 50 return 0, errors.Errorf("too large: %s", s) 51 } 52 if negative { 53 return -int64(value), nil 54 } 55 return int64(value), nil 56 } 57 58 // BytesValue is a struct that implements flag.Value and pflag.Value 59 // suitable to create command-line parameters that accept sizes 60 // specified using a format recognized by humanize. 61 // The value is written atomically, so that it is safe to use this 62 // struct to make a parameter configurable that is used by an 63 // asynchronous process spawned before command-line argument handling. 64 // This is useful e.g. for the log file settings which are used 65 // by the asynchronous log file GC daemon. 66 type BytesValue struct { 67 val *int64 68 isSet bool 69 } 70 71 var _ flag.Value = &BytesValue{} 72 var _ pflag.Value = &BytesValue{} 73 74 // NewBytesValue creates a new pflag.Value bound to the specified 75 // int64 variable. It also happens to be a flag.Value. 76 func NewBytesValue(val *int64) *BytesValue { 77 return &BytesValue{val: val} 78 } 79 80 // Set implements the flag.Value and pflag.Value interfaces. 81 func (b *BytesValue) Set(s string) error { 82 v, err := ParseBytes(s) 83 if err != nil { 84 return err 85 } 86 if b.val == nil { 87 b.val = new(int64) 88 } 89 atomic.StoreInt64(b.val, v) 90 b.isSet = true 91 return nil 92 } 93 94 // Type implements the pflag.Value interface. 95 func (b *BytesValue) Type() string { 96 return "bytes" 97 } 98 99 // String implements the flag.Value and pflag.Value interfaces. 100 func (b *BytesValue) String() string { 101 return redact.StringWithoutMarkers(b) 102 } 103 104 // SafeFormat implements the redact.SafeFormatter interface. 105 func (b *BytesValue) SafeFormat(w redact.SafePrinter, _ rune) { 106 // When b.val is nil, the real value of the flag will only be known after a 107 // Resolve() call. We do not want our flag package to report an erroneous 108 // default value for this flag. So the value we return here must cause 109 // defaultIsZeroValue to return true: 110 // https://github.com/spf13/pflag/blob/v1.0.5/flag.go#L724 111 if b.val == nil { 112 w.SafeString("<nil>") 113 return 114 } 115 // This uses the MiB, GiB, etc suffixes. If we use humanize.Bytes() we get 116 // the MB, GB, etc suffixes, but the conversion is done in multiples of 1000 117 // vs 1024. 118 w.Print(IBytes(atomic.LoadInt64(b.val))) 119 } 120 121 // IsSet returns true iff Set has successfully been called. 122 func (b *BytesValue) IsSet() bool { 123 return b.isSet 124 } 125 126 // DataRate formats the passed byte count over duration as "x MiB/s". 127 func DataRate(bytes int64, elapsed time.Duration) redact.SafeString { 128 if bytes == 0 { 129 return "0" 130 } 131 if elapsed == 0 { 132 return "inf" 133 } 134 return redact.SafeString(fmt.Sprintf("%0.2f MiB/s", 135 (float64(bytes)/elapsed.Seconds())/float64(1<<20))) 136 }