bytes.go (2610B)
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 }