bytes.go raw

   1  package humanize
   2  
   3  import (
   4  	"fmt"
   5  	"math"
   6  	"strconv"
   7  	"strings"
   8  	"unicode"
   9  )
  10  
  11  // IEC Sizes.
  12  // kibis of bits
  13  const (
  14  	Byte = 1 << (iota * 10)
  15  	KiByte
  16  	MiByte
  17  	GiByte
  18  	TiByte
  19  	PiByte
  20  	EiByte
  21  )
  22  
  23  // SI Sizes.
  24  const (
  25  	IByte = 1
  26  	KByte = IByte * 1000
  27  	MByte = KByte * 1000
  28  	GByte = MByte * 1000
  29  	TByte = GByte * 1000
  30  	PByte = TByte * 1000
  31  	EByte = PByte * 1000
  32  )
  33  
  34  var bytesSizeTable = map[string]uint64{
  35  	"b":   Byte,
  36  	"kib": KiByte,
  37  	"kb":  KByte,
  38  	"mib": MiByte,
  39  	"mb":  MByte,
  40  	"gib": GiByte,
  41  	"gb":  GByte,
  42  	"tib": TiByte,
  43  	"tb":  TByte,
  44  	"pib": PiByte,
  45  	"pb":  PByte,
  46  	"eib": EiByte,
  47  	"eb":  EByte,
  48  	// Without suffix
  49  	"":   Byte,
  50  	"ki": KiByte,
  51  	"k":  KByte,
  52  	"mi": MiByte,
  53  	"m":  MByte,
  54  	"gi": GiByte,
  55  	"g":  GByte,
  56  	"ti": TiByte,
  57  	"t":  TByte,
  58  	"pi": PiByte,
  59  	"p":  PByte,
  60  	"ei": EiByte,
  61  	"e":  EByte,
  62  }
  63  
  64  func logn(n, b float64) float64 {
  65  	return math.Log(n) / math.Log(b)
  66  }
  67  
  68  func humanateBytes(s uint64, base float64, sizes []string) string {
  69  	if s < 10 {
  70  		return fmt.Sprintf("%d B", s)
  71  	}
  72  	e := math.Floor(logn(float64(s), base))
  73  	suffix := sizes[int(e)]
  74  	val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10
  75  	f := "%.0f %s"
  76  	if val < 10 {
  77  		f = "%.1f %s"
  78  	}
  79  
  80  	return fmt.Sprintf(f, val, suffix)
  81  }
  82  
  83  // Bytes produces a human readable representation of an SI size.
  84  //
  85  // See also: ParseBytes.
  86  //
  87  // Bytes(82854982) -> 83 MB
  88  func Bytes(s uint64) string {
  89  	sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"}
  90  	return humanateBytes(s, 1000, sizes)
  91  }
  92  
  93  // IBytes produces a human readable representation of an IEC size.
  94  //
  95  // See also: ParseBytes.
  96  //
  97  // IBytes(82854982) -> 79 MiB
  98  func IBytes(s uint64) string {
  99  	sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}
 100  	return humanateBytes(s, 1024, sizes)
 101  }
 102  
 103  // ParseBytes parses a string representation of bytes into the number
 104  // of bytes it represents.
 105  //
 106  // See Also: Bytes, IBytes.
 107  //
 108  // ParseBytes("42 MB") -> 42000000, nil
 109  // ParseBytes("42 mib") -> 44040192, nil
 110  func ParseBytes(s string) (uint64, error) {
 111  	lastDigit := 0
 112  	hasComma := false
 113  	for _, r := range s {
 114  		if !(unicode.IsDigit(r) || r == '.' || r == ',') {
 115  			break
 116  		}
 117  		if r == ',' {
 118  			hasComma = true
 119  		}
 120  		lastDigit++
 121  	}
 122  
 123  	num := s[:lastDigit]
 124  	if hasComma {
 125  		num = strings.Replace(num, ",", "", -1)
 126  	}
 127  
 128  	f, err := strconv.ParseFloat(num, 64)
 129  	if err != nil {
 130  		return 0, err
 131  	}
 132  
 133  	extra := strings.ToLower(strings.TrimSpace(s[lastDigit:]))
 134  	if m, ok := bytesSizeTable[extra]; ok {
 135  		f *= float64(m)
 136  		if f >= math.MaxUint64 {
 137  			return 0, fmt.Errorf("too large: %v", s)
 138  		}
 139  		return uint64(f), nil
 140  	}
 141  
 142  	return 0, fmt.Errorf("unhandled size name: %v", extra)
 143  }
 144