github.com/sagernet/sing-box@v1.9.0-rc.20/common/humanize/bytes.go (about) 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 defaultSizeTable = 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 var memorysSizeTable = map[string]uint64{ 65 "b": Byte, 66 "kb": KiByte, 67 "mb": MiByte, 68 "gb": GiByte, 69 "tb": TiByte, 70 "pb": PiByte, 71 "eb": EiByte, 72 "": Byte, 73 "k": KiByte, 74 "m": MiByte, 75 "g": GiByte, 76 "t": TiByte, 77 "p": PiByte, 78 "e": EiByte, 79 } 80 81 var ( 82 defaultSizes = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"} 83 iSizes = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"} 84 ) 85 86 func Bytes(s uint64) string { 87 return humanateBytes(s, 1000, defaultSizes) 88 } 89 90 func MemoryBytes(s uint64) string { 91 return humanateBytes(s, 1024, defaultSizes) 92 } 93 94 func IBytes(s uint64) string { 95 return humanateBytes(s, 1024, iSizes) 96 } 97 98 func logn(n, b float64) float64 { 99 return math.Log(n) / math.Log(b) 100 } 101 102 func humanateBytes(s uint64, base float64, sizes []string) string { 103 if s < 10 { 104 return fmt.Sprintf("%d B", s) 105 } 106 e := math.Floor(logn(float64(s), base)) 107 suffix := sizes[int(e)] 108 val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10 109 f := "%.0f %s" 110 if val < 10 { 111 f = "%.1f %s" 112 } 113 114 return fmt.Sprintf(f, val, suffix) 115 } 116 117 func ParseBytes(s string) (uint64, error) { 118 return parseBytes0(s, defaultSizeTable) 119 } 120 121 func ParseMemoryBytes(s string) (uint64, error) { 122 return parseBytes0(s, memorysSizeTable) 123 } 124 125 func parseBytes0(s string, sizeTable map[string]uint64) (uint64, error) { 126 lastDigit := 0 127 hasComma := false 128 for _, r := range s { 129 if !(unicode.IsDigit(r) || r == '.' || r == ',') { 130 break 131 } 132 if r == ',' { 133 hasComma = true 134 } 135 lastDigit++ 136 } 137 138 num := s[:lastDigit] 139 if hasComma { 140 num = strings.Replace(num, ",", "", -1) 141 } 142 143 f, err := strconv.ParseFloat(num, 64) 144 if err != nil { 145 return 0, err 146 } 147 148 extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) 149 if m, ok := sizeTable[extra]; ok { 150 f *= float64(m) 151 if f >= math.MaxUint64 { 152 return 0, fmt.Errorf("too large: %v", s) 153 } 154 return uint64(f), nil 155 } 156 157 return 0, fmt.Errorf("unhandled size name: %v", extra) 158 }