|
@@ -0,0 +1,158 @@
|
|
|
+package humanize
|
|
|
+
|
|
|
+import (
|
|
|
+ "fmt"
|
|
|
+ "math"
|
|
|
+ "strconv"
|
|
|
+ "strings"
|
|
|
+ "unicode"
|
|
|
+)
|
|
|
+
|
|
|
+// IEC Sizes.
|
|
|
+// kibis of bits
|
|
|
+const (
|
|
|
+ Byte = 1 << (iota * 10)
|
|
|
+ KiByte
|
|
|
+ MiByte
|
|
|
+ GiByte
|
|
|
+ TiByte
|
|
|
+ PiByte
|
|
|
+ EiByte
|
|
|
+)
|
|
|
+
|
|
|
+// SI Sizes.
|
|
|
+const (
|
|
|
+ IByte = 1
|
|
|
+ KByte = IByte * 1000
|
|
|
+ MByte = KByte * 1000
|
|
|
+ GByte = MByte * 1000
|
|
|
+ TByte = GByte * 1000
|
|
|
+ PByte = TByte * 1000
|
|
|
+ EByte = PByte * 1000
|
|
|
+)
|
|
|
+
|
|
|
+var defaultSizeTable = map[string]uint64{
|
|
|
+ "b": Byte,
|
|
|
+ "kib": KiByte,
|
|
|
+ "kb": KByte,
|
|
|
+ "mib": MiByte,
|
|
|
+ "mb": MByte,
|
|
|
+ "gib": GiByte,
|
|
|
+ "gb": GByte,
|
|
|
+ "tib": TiByte,
|
|
|
+ "tb": TByte,
|
|
|
+ "pib": PiByte,
|
|
|
+ "pb": PByte,
|
|
|
+ "eib": EiByte,
|
|
|
+ "eb": EByte,
|
|
|
+ // Without suffix
|
|
|
+ "": Byte,
|
|
|
+ "ki": KiByte,
|
|
|
+ "k": KByte,
|
|
|
+ "mi": MiByte,
|
|
|
+ "m": MByte,
|
|
|
+ "gi": GiByte,
|
|
|
+ "g": GByte,
|
|
|
+ "ti": TiByte,
|
|
|
+ "t": TByte,
|
|
|
+ "pi": PiByte,
|
|
|
+ "p": PByte,
|
|
|
+ "ei": EiByte,
|
|
|
+ "e": EByte,
|
|
|
+}
|
|
|
+
|
|
|
+var memorysSizeTable = map[string]uint64{
|
|
|
+ "b": Byte,
|
|
|
+ "kb": KiByte,
|
|
|
+ "mb": MiByte,
|
|
|
+ "gb": GiByte,
|
|
|
+ "tb": TiByte,
|
|
|
+ "pb": PiByte,
|
|
|
+ "eb": EiByte,
|
|
|
+ "": Byte,
|
|
|
+ "k": KiByte,
|
|
|
+ "m": MiByte,
|
|
|
+ "g": GiByte,
|
|
|
+ "t": TiByte,
|
|
|
+ "p": PiByte,
|
|
|
+ "e": EiByte,
|
|
|
+}
|
|
|
+
|
|
|
+var (
|
|
|
+ defaultSizes = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"}
|
|
|
+ iSizes = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}
|
|
|
+)
|
|
|
+
|
|
|
+func Bytes(s uint64) string {
|
|
|
+ return humanateBytes(s, 1000, defaultSizes)
|
|
|
+}
|
|
|
+
|
|
|
+func MemoryBytes(s uint64) string {
|
|
|
+ return humanateBytes(s, 1024, defaultSizes)
|
|
|
+}
|
|
|
+
|
|
|
+func IBytes(s uint64) string {
|
|
|
+ return humanateBytes(s, 1024, iSizes)
|
|
|
+}
|
|
|
+
|
|
|
+func logn(n, b float64) float64 {
|
|
|
+ return math.Log(n) / math.Log(b)
|
|
|
+}
|
|
|
+
|
|
|
+func humanateBytes(s uint64, base float64, sizes []string) string {
|
|
|
+ if s < 10 {
|
|
|
+ return fmt.Sprintf("%d B", s)
|
|
|
+ }
|
|
|
+ e := math.Floor(logn(float64(s), base))
|
|
|
+ suffix := sizes[int(e)]
|
|
|
+ val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10
|
|
|
+ f := "%.0f %s"
|
|
|
+ if val < 10 {
|
|
|
+ f = "%.1f %s"
|
|
|
+ }
|
|
|
+
|
|
|
+ return fmt.Sprintf(f, val, suffix)
|
|
|
+}
|
|
|
+
|
|
|
+func ParseBytes(s string) (uint64, error) {
|
|
|
+ return parseBytes0(s, defaultSizeTable)
|
|
|
+}
|
|
|
+
|
|
|
+func ParseMemoryBytes(s string) (uint64, error) {
|
|
|
+ return parseBytes0(s, memorysSizeTable)
|
|
|
+}
|
|
|
+
|
|
|
+func parseBytes0(s string, sizeTable map[string]uint64) (uint64, error) {
|
|
|
+ lastDigit := 0
|
|
|
+ hasComma := false
|
|
|
+ for _, r := range s {
|
|
|
+ if !(unicode.IsDigit(r) || r == '.' || r == ',') {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ if r == ',' {
|
|
|
+ hasComma = true
|
|
|
+ }
|
|
|
+ lastDigit++
|
|
|
+ }
|
|
|
+
|
|
|
+ num := s[:lastDigit]
|
|
|
+ if hasComma {
|
|
|
+ num = strings.Replace(num, ",", "", -1)
|
|
|
+ }
|
|
|
+
|
|
|
+ f, err := strconv.ParseFloat(num, 64)
|
|
|
+ if err != nil {
|
|
|
+ return 0, err
|
|
|
+ }
|
|
|
+
|
|
|
+ extra := strings.ToLower(strings.TrimSpace(s[lastDigit:]))
|
|
|
+ if m, ok := sizeTable[extra]; ok {
|
|
|
+ f *= float64(m)
|
|
|
+ if f >= math.MaxUint64 {
|
|
|
+ return 0, fmt.Errorf("too large: %v", s)
|
|
|
+ }
|
|
|
+ return uint64(f), nil
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0, fmt.Errorf("unhandled size name: %v", extra)
|
|
|
+}
|