[822] | 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 | }
|
---|