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