bytes.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. package humanize
  2. import (
  3. "fmt"
  4. "math"
  5. "strconv"
  6. "strings"
  7. "unicode"
  8. )
  9. // IEC Sizes.
  10. // kibis of bits
  11. const (
  12. Byte = 1 << (iota * 10)
  13. KiByte
  14. MiByte
  15. GiByte
  16. TiByte
  17. PiByte
  18. EiByte
  19. )
  20. // SI Sizes.
  21. const (
  22. IByte = 1
  23. KByte = IByte * 1000
  24. MByte = KByte * 1000
  25. GByte = MByte * 1000
  26. TByte = GByte * 1000
  27. PByte = TByte * 1000
  28. EByte = PByte * 1000
  29. )
  30. var bytesSizeTable = map[string]uint64{
  31. "b": Byte,
  32. "kib": KiByte,
  33. "kb": KByte,
  34. "mib": MiByte,
  35. "mb": MByte,
  36. "gib": GiByte,
  37. "gb": GByte,
  38. "tib": TiByte,
  39. "tb": TByte,
  40. "pib": PiByte,
  41. "pb": PByte,
  42. "eib": EiByte,
  43. "eb": EByte,
  44. // Without suffix
  45. "": Byte,
  46. "ki": KiByte,
  47. "k": KByte,
  48. "mi": MiByte,
  49. "m": MByte,
  50. "gi": GiByte,
  51. "g": GByte,
  52. "ti": TiByte,
  53. "t": TByte,
  54. "pi": PiByte,
  55. "p": PByte,
  56. "ei": EiByte,
  57. "e": EByte,
  58. }
  59. func logn(n, b float64) float64 {
  60. return math.Log(n) / math.Log(b)
  61. }
  62. func humanateBytes(s uint64, base float64, sizes []string) string {
  63. if s < 10 {
  64. return fmt.Sprintf("%d B", s)
  65. }
  66. e := math.Floor(logn(float64(s), base))
  67. suffix := sizes[int(e)]
  68. val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10
  69. f := "%.0f %s"
  70. if val < 10 {
  71. f = "%.1f %s"
  72. }
  73. return fmt.Sprintf(f, val, suffix)
  74. }
  75. // Bytes produces a human readable representation of an SI size.
  76. //
  77. // See also: ParseBytes.
  78. //
  79. // Bytes(82854982) -> 83MB
  80. func Bytes(s uint64) string {
  81. sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"}
  82. return humanateBytes(s, 1000, sizes)
  83. }
  84. // IBytes produces a human readable representation of an IEC size.
  85. //
  86. // See also: ParseBytes.
  87. //
  88. // IBytes(82854982) -> 79MiB
  89. func IBytes(s uint64) string {
  90. sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}
  91. return humanateBytes(s, 1024, sizes)
  92. }
  93. // ParseBytes parses a string representation of bytes into the number
  94. // of bytes it represents.
  95. //
  96. // See Also: Bytes, IBytes.
  97. //
  98. // ParseBytes("42MB") -> 42000000, nil
  99. // ParseBytes("42mib") -> 44040192, nil
  100. func ParseBytes(s string) (uint64, error) {
  101. lastDigit := 0
  102. for _, r := range s {
  103. if !(unicode.IsDigit(r) || r == '.') {
  104. break
  105. }
  106. lastDigit++
  107. }
  108. f, err := strconv.ParseFloat(s[:lastDigit], 64)
  109. if err != nil {
  110. return 0, err
  111. }
  112. extra := strings.ToLower(strings.TrimSpace(s[lastDigit:]))
  113. if m, ok := bytesSizeTable[extra]; ok {
  114. f *= float64(m)
  115. if f >= math.MaxUint64 {
  116. return 0, fmt.Errorf("too large: %v", s)
  117. }
  118. return uint64(f), nil
  119. }
  120. return 0, fmt.Errorf("unhandled size name: %v", extra)
  121. }