bytes.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  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 defaultSizeTable = 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. var memorysSizeTable = map[string]uint64{
  60. "b": Byte,
  61. "kb": KiByte,
  62. "mb": MiByte,
  63. "gb": GiByte,
  64. "tb": TiByte,
  65. "pb": PiByte,
  66. "eb": EiByte,
  67. "": Byte,
  68. "k": KiByte,
  69. "m": MiByte,
  70. "g": GiByte,
  71. "t": TiByte,
  72. "p": PiByte,
  73. "e": EiByte,
  74. }
  75. var (
  76. defaultSizes = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"}
  77. iSizes = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}
  78. )
  79. func Bytes(s uint64) string {
  80. return humanateBytes(s, 1000, defaultSizes)
  81. }
  82. func MemoryBytes(s uint64) string {
  83. return humanateBytes(s, 1024, defaultSizes)
  84. }
  85. func IBytes(s uint64) string {
  86. return humanateBytes(s, 1024, iSizes)
  87. }
  88. func logn(n, b float64) float64 {
  89. return math.Log(n) / math.Log(b)
  90. }
  91. func humanateBytes(s uint64, base float64, sizes []string) string {
  92. if s < 10 {
  93. return fmt.Sprintf("%d B", 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 := "%.0f %s"
  99. if val < 10 {
  100. f = "%.1f %s"
  101. }
  102. return fmt.Sprintf(f, val, suffix)
  103. }
  104. func ParseBytes(s string) (uint64, error) {
  105. return parseBytes0(s, defaultSizeTable)
  106. }
  107. func ParseMemoryBytes(s string) (uint64, error) {
  108. return parseBytes0(s, memorysSizeTable)
  109. }
  110. func parseBytes0(s string, sizeTable map[string]uint64) (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. num := s[:lastDigit]
  123. if hasComma {
  124. num = strings.Replace(num, ",", "", -1)
  125. }
  126. f, err := strconv.ParseFloat(num, 64)
  127. if err != nil {
  128. return 0, err
  129. }
  130. extra := strings.ToLower(strings.TrimSpace(s[lastDigit:]))
  131. if m, ok := sizeTable[extra]; ok {
  132. f *= float64(m)
  133. if f >= math.MaxUint64 {
  134. return 0, fmt.Errorf("too large: %v", s)
  135. }
  136. return uint64(f), nil
  137. }
  138. return 0, fmt.Errorf("unhandled size name: %v", extra)
  139. }