You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

173 lines
4.2 KiB

  1. package humanize
  2. import (
  3. "fmt"
  4. "math/big"
  5. "strings"
  6. "unicode"
  7. )
  8. var (
  9. bigIECExp = big.NewInt(1024)
  10. // BigByte is one byte in bit.Ints
  11. BigByte = big.NewInt(1)
  12. // BigKiByte is 1,024 bytes in bit.Ints
  13. BigKiByte = (&big.Int{}).Mul(BigByte, bigIECExp)
  14. // BigMiByte is 1,024 k bytes in bit.Ints
  15. BigMiByte = (&big.Int{}).Mul(BigKiByte, bigIECExp)
  16. // BigGiByte is 1,024 m bytes in bit.Ints
  17. BigGiByte = (&big.Int{}).Mul(BigMiByte, bigIECExp)
  18. // BigTiByte is 1,024 g bytes in bit.Ints
  19. BigTiByte = (&big.Int{}).Mul(BigGiByte, bigIECExp)
  20. // BigPiByte is 1,024 t bytes in bit.Ints
  21. BigPiByte = (&big.Int{}).Mul(BigTiByte, bigIECExp)
  22. // BigEiByte is 1,024 p bytes in bit.Ints
  23. BigEiByte = (&big.Int{}).Mul(BigPiByte, bigIECExp)
  24. // BigZiByte is 1,024 e bytes in bit.Ints
  25. BigZiByte = (&big.Int{}).Mul(BigEiByte, bigIECExp)
  26. // BigYiByte is 1,024 z bytes in bit.Ints
  27. BigYiByte = (&big.Int{}).Mul(BigZiByte, bigIECExp)
  28. )
  29. var (
  30. bigSIExp = big.NewInt(1000)
  31. // BigSIByte is one SI byte in big.Ints
  32. BigSIByte = big.NewInt(1)
  33. // BigKByte is 1,000 SI bytes in big.Ints
  34. BigKByte = (&big.Int{}).Mul(BigSIByte, bigSIExp)
  35. // BigMByte is 1,000 SI k bytes in big.Ints
  36. BigMByte = (&big.Int{}).Mul(BigKByte, bigSIExp)
  37. // BigGByte is 1,000 SI m bytes in big.Ints
  38. BigGByte = (&big.Int{}).Mul(BigMByte, bigSIExp)
  39. // BigTByte is 1,000 SI g bytes in big.Ints
  40. BigTByte = (&big.Int{}).Mul(BigGByte, bigSIExp)
  41. // BigPByte is 1,000 SI t bytes in big.Ints
  42. BigPByte = (&big.Int{}).Mul(BigTByte, bigSIExp)
  43. // BigEByte is 1,000 SI p bytes in big.Ints
  44. BigEByte = (&big.Int{}).Mul(BigPByte, bigSIExp)
  45. // BigZByte is 1,000 SI e bytes in big.Ints
  46. BigZByte = (&big.Int{}).Mul(BigEByte, bigSIExp)
  47. // BigYByte is 1,000 SI z bytes in big.Ints
  48. BigYByte = (&big.Int{}).Mul(BigZByte, bigSIExp)
  49. )
  50. var bigBytesSizeTable = map[string]*big.Int{
  51. "b": BigByte,
  52. "kib": BigKiByte,
  53. "kb": BigKByte,
  54. "mib": BigMiByte,
  55. "mb": BigMByte,
  56. "gib": BigGiByte,
  57. "gb": BigGByte,
  58. "tib": BigTiByte,
  59. "tb": BigTByte,
  60. "pib": BigPiByte,
  61. "pb": BigPByte,
  62. "eib": BigEiByte,
  63. "eb": BigEByte,
  64. "zib": BigZiByte,
  65. "zb": BigZByte,
  66. "yib": BigYiByte,
  67. "yb": BigYByte,
  68. // Without suffix
  69. "": BigByte,
  70. "ki": BigKiByte,
  71. "k": BigKByte,
  72. "mi": BigMiByte,
  73. "m": BigMByte,
  74. "gi": BigGiByte,
  75. "g": BigGByte,
  76. "ti": BigTiByte,
  77. "t": BigTByte,
  78. "pi": BigPiByte,
  79. "p": BigPByte,
  80. "ei": BigEiByte,
  81. "e": BigEByte,
  82. "z": BigZByte,
  83. "zi": BigZiByte,
  84. "y": BigYByte,
  85. "yi": BigYiByte,
  86. }
  87. var ten = big.NewInt(10)
  88. func humanateBigBytes(s, base *big.Int, sizes []string) string {
  89. if s.Cmp(ten) < 0 {
  90. return fmt.Sprintf("%d B", s)
  91. }
  92. c := (&big.Int{}).Set(s)
  93. val, mag := oomm(c, base, len(sizes)-1)
  94. suffix := sizes[mag]
  95. f := "%.0f %s"
  96. if val < 10 {
  97. f = "%.1f %s"
  98. }
  99. return fmt.Sprintf(f, val, suffix)
  100. }
  101. // BigBytes produces a human readable representation of an SI size.
  102. //
  103. // See also: ParseBigBytes.
  104. //
  105. // BigBytes(82854982) -> 83 MB
  106. func BigBytes(s *big.Int) string {
  107. sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
  108. return humanateBigBytes(s, bigSIExp, sizes)
  109. }
  110. // BigIBytes produces a human readable representation of an IEC size.
  111. //
  112. // See also: ParseBigBytes.
  113. //
  114. // BigIBytes(82854982) -> 79 MiB
  115. func BigIBytes(s *big.Int) string {
  116. sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
  117. return humanateBigBytes(s, bigIECExp, sizes)
  118. }
  119. // ParseBigBytes parses a string representation of bytes into the number
  120. // of bytes it represents.
  121. //
  122. // See also: BigBytes, BigIBytes.
  123. //
  124. // ParseBigBytes("42 MB") -> 42000000, nil
  125. // ParseBigBytes("42 mib") -> 44040192, nil
  126. func ParseBigBytes(s string) (*big.Int, error) {
  127. lastDigit := 0
  128. hasComma := false
  129. for _, r := range s {
  130. if !(unicode.IsDigit(r) || r == '.' || r == ',') {
  131. break
  132. }
  133. if r == ',' {
  134. hasComma = true
  135. }
  136. lastDigit++
  137. }
  138. num := s[:lastDigit]
  139. if hasComma {
  140. num = strings.Replace(num, ",", "", -1)
  141. }
  142. val := &big.Rat{}
  143. _, err := fmt.Sscanf(num, "%f", val)
  144. if err != nil {
  145. return nil, err
  146. }
  147. extra := strings.ToLower(strings.TrimSpace(s[lastDigit:]))
  148. if m, ok := bigBytesSizeTable[extra]; ok {
  149. mv := (&big.Rat{}).SetInt(m)
  150. val.Mul(val, mv)
  151. rv := &big.Int{}
  152. rv.Div(val.Num(), val.Denom())
  153. return rv, nil
  154. }
  155. return nil, fmt.Errorf("unhandled size name: %v", extra)
  156. }