bytes.go

  1package humanize
  2
  3import (
  4	"fmt"
  5	"math"
  6	"strconv"
  7	"strings"
  8	"unicode"
  9)
 10
 11// IEC Sizes.
 12// kibis of bits
 13const (
 14	Byte = 1 << (iota * 10)
 15	KiByte
 16	MiByte
 17	GiByte
 18	TiByte
 19	PiByte
 20	EiByte
 21)
 22
 23// SI Sizes.
 24const (
 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
 34var 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
 64func logn(n, b float64) float64 {
 65	return math.Log(n) / math.Log(b)
 66}
 67
 68func 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
 88func 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
 98func 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
110func 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}