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  }