size.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. package units
  2. import (
  3. "fmt"
  4. "regexp"
  5. "strconv"
  6. "strings"
  7. )
  8. // See: http://en.wikipedia.org/wiki/Binary_prefix
  9. const (
  10. // Decimal
  11. KB = 1000
  12. MB = 1000 * KB
  13. GB = 1000 * MB
  14. TB = 1000 * GB
  15. PB = 1000 * TB
  16. // Binary
  17. KiB = 1024
  18. MiB = 1024 * KiB
  19. GiB = 1024 * MiB
  20. TiB = 1024 * GiB
  21. PiB = 1024 * TiB
  22. )
  23. type unitMap map[string]int64
  24. var (
  25. decimalMap = unitMap{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB}
  26. binaryMap = unitMap{"k": KiB, "m": MiB, "g": GiB, "t": TiB, "p": PiB}
  27. sizeRegex = regexp.MustCompile(`^(\d+(\.\d+)*) ?([kKmMgGtTpP])?[bB]?$`)
  28. )
  29. var decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
  30. var binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
  31. func getSizeAndUnit(size float64, base float64, _map []string) (float64, string) {
  32. i := 0
  33. unitsLimit := len(_map) - 1
  34. for size >= base && i < unitsLimit {
  35. size = size / base
  36. i++
  37. }
  38. return size, _map[i]
  39. }
  40. // CustomSize returns a human-readable approximation of a size
  41. // using custom format.
  42. func CustomSize(format string, size float64, base float64, _map []string) string {
  43. size, unit := getSizeAndUnit(size, base, _map)
  44. return fmt.Sprintf(format, size, unit)
  45. }
  46. // HumanSizeWithPrecision allows the size to be in any precision,
  47. // instead of 4 digit precision used in units.HumanSize.
  48. func HumanSizeWithPrecision(size float64, precision int) string {
  49. size, unit := getSizeAndUnit(size, 1000.0, decimapAbbrs)
  50. return fmt.Sprintf("%.*g%s", precision, size, unit)
  51. }
  52. // HumanSize returns a human-readable approximation of a size
  53. // capped at 4 valid numbers (eg. "2.746 MB", "796 KB").
  54. func HumanSize(size float64) string {
  55. return HumanSizeWithPrecision(size, 4)
  56. }
  57. // BytesSize returns a human-readable size in bytes, kibibytes,
  58. // mebibytes, gibibytes, or tebibytes (eg. "44kiB", "17MiB").
  59. func BytesSize(size float64) string {
  60. return CustomSize("%.4g%s", size, 1024.0, binaryAbbrs)
  61. }
  62. // FromHumanSize returns an integer from a human-readable specification of a
  63. // size using SI standard (eg. "44kB", "17MB").
  64. func FromHumanSize(size string) (int64, error) {
  65. return parseSize(size, decimalMap)
  66. }
  67. // RAMInBytes parses a human-readable string representing an amount of RAM
  68. // in bytes, kibibytes, mebibytes, gibibytes, or tebibytes and
  69. // returns the number of bytes, or -1 if the string is unparseable.
  70. // Units are case-insensitive, and the 'b' suffix is optional.
  71. func RAMInBytes(size string) (int64, error) {
  72. return parseSize(size, binaryMap)
  73. }
  74. // Parses the human-readable size string into the amount it represents.
  75. func parseSize(sizeStr string, uMap unitMap) (int64, error) {
  76. matches := sizeRegex.FindStringSubmatch(sizeStr)
  77. if len(matches) != 4 {
  78. return -1, fmt.Errorf("invalid size: '%s'", sizeStr)
  79. }
  80. size, err := strconv.ParseFloat(matches[1], 64)
  81. if err != nil {
  82. return -1, err
  83. }
  84. unitPrefix := strings.ToLower(matches[3])
  85. if mul, ok := uMap[unitPrefix]; ok {
  86. size *= float64(mul)
  87. }
  88. return int64(size), nil
  89. }