size.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. package humanize
  2. import (
  3. "bytes"
  4. "fmt"
  5. "git.nspix.com/golang/kos/util/bs"
  6. "math"
  7. "strconv"
  8. "strings"
  9. "unicode"
  10. )
  11. type Size uint64
  12. // IEC Sizes.
  13. // kibis of bits
  14. const (
  15. Byte = 1 << (iota * 10)
  16. KiByte
  17. MiByte
  18. GiByte
  19. TiByte
  20. PiByte
  21. EiByte
  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. var bytesSizeTable = map[string]uint64{
  34. "b": Byte,
  35. "kib": KiByte,
  36. "kb": KByte,
  37. "mib": MiByte,
  38. "mb": MByte,
  39. "gib": GiByte,
  40. "gb": GByte,
  41. "tib": TiByte,
  42. "tb": TByte,
  43. "pib": PiByte,
  44. "pb": PByte,
  45. "eib": EiByte,
  46. "eb": EByte,
  47. // Without suffix
  48. "": Byte,
  49. "ki": KiByte,
  50. "k": KByte,
  51. "mi": MiByte,
  52. "m": MByte,
  53. "gi": GiByte,
  54. "g": GByte,
  55. "ti": TiByte,
  56. "t": TByte,
  57. "pi": PiByte,
  58. "p": PByte,
  59. "ei": EiByte,
  60. "e": EByte,
  61. }
  62. func logn(n, b float64) float64 {
  63. return math.Log(n) / math.Log(b)
  64. }
  65. func humanateBytes(s uint64, base float64, sizes []string) string {
  66. if s < 10 {
  67. return fmt.Sprintf("%d B", s)
  68. }
  69. e := math.Floor(logn(float64(s), base))
  70. suffix := sizes[int(e)]
  71. val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10
  72. f := "%.0f %s"
  73. if val < 10 {
  74. f = "%.1f %s"
  75. }
  76. return fmt.Sprintf(f, val, suffix)
  77. }
  78. // Bytes produces a human readable representation of an SI size.
  79. //
  80. // See also: ParseBytes.
  81. //
  82. // Bytes(82854982) -> 83 MB
  83. func Bytes(s uint64) string {
  84. sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"}
  85. return humanateBytes(s, 1000, sizes)
  86. }
  87. // IBytes produces a human readable representation of an IEC size.
  88. //
  89. // See also: ParseBytes.
  90. //
  91. // IBytes(82854982) -> 79 MiB
  92. func IBytes(s uint64) string {
  93. sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}
  94. return humanateBytes(s, 1024, sizes)
  95. }
  96. // ParseBytes parses a string representation of bytes into the number
  97. // of bytes it represents.
  98. //
  99. // See Also: Bytes, IBytes.
  100. //
  101. // ParseBytes("42 MB") -> 42000000, nil
  102. // ParseBytes("42 mib") -> 44040192, nil
  103. func ParseBytes(s string) (uint64, error) {
  104. lastDigit := 0
  105. hasComma := false
  106. for _, r := range s {
  107. if !(unicode.IsDigit(r) || r == '.' || r == ',') {
  108. break
  109. }
  110. if r == ',' {
  111. hasComma = true
  112. }
  113. lastDigit++
  114. }
  115. num := s[:lastDigit]
  116. if hasComma {
  117. num = strings.Replace(num, ",", "", -1)
  118. }
  119. f, err := strconv.ParseFloat(num, 64)
  120. if err != nil {
  121. return 0, err
  122. }
  123. extra := strings.ToLower(strings.TrimSpace(s[lastDigit:]))
  124. if m, ok := bytesSizeTable[extra]; ok {
  125. f *= float64(m)
  126. if f >= math.MaxUint64 {
  127. return 0, fmt.Errorf("too large: %v", s)
  128. }
  129. return uint64(f), nil
  130. }
  131. return 0, fmt.Errorf("unhandled size name: %v", extra)
  132. }
  133. func (b *Size) UnmarshalJSON(buf []byte) error {
  134. var (
  135. n uint64
  136. err error
  137. )
  138. buf = bytes.TrimFunc(buf, func(r rune) bool {
  139. if r == '"' {
  140. return true
  141. }
  142. return false
  143. })
  144. if n, err = ParseBytes(bs.BytesToString(buf)); err == nil {
  145. *b = Size(n)
  146. }
  147. return err
  148. }
  149. func (b Size) MarshalJSON() ([]byte, error) {
  150. s := `"` + IBytes(uint64(b)) + `"`
  151. return bs.StringToBytes(s), nil
  152. }
  153. func (b Size) String() string {
  154. return IBytes(uint64(b))
  155. }