bytes.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. // Package bytefmt contains helper methods and constants for converting to and from a human-readable byte format.
  2. //
  3. // bytefmt.ByteSize(100.5*bytefmt.MEGABYTE) // "100.5M"
  4. // bytefmt.ByteSize(uint64(1024)) // "1K"
  5. //
  6. package bytefmt
  7. import (
  8. "errors"
  9. "fmt"
  10. "regexp"
  11. "strconv"
  12. "strings"
  13. )
  14. const (
  15. BYTE = 1.0
  16. KILOBYTE = 1024 * BYTE
  17. MEGABYTE = 1024 * KILOBYTE
  18. GIGABYTE = 1024 * MEGABYTE
  19. TERABYTE = 1024 * GIGABYTE
  20. )
  21. var bytesPattern *regexp.Regexp = regexp.MustCompile(`(?i)^(-?\d+(?:\.\d+)?)([KMGT]i?B?|B)$`)
  22. var invalidByteQuantityError = errors.New("Byte quantity must be a positive integer with a unit of measurement like M, MB, MiB, G, GiB, or GB")
  23. // ByteSize returns a human-readable byte string of the form 10M, 12.5K, and so forth. The following units are available:
  24. // T: Terabyte
  25. // G: Gigabyte
  26. // M: Megabyte
  27. // K: Kilobyte
  28. // B: Byte
  29. // The unit that results in the smallest number greater than or equal to 1 is always chosen.
  30. func ByteSize(bytes uint64) string {
  31. unit := ""
  32. value := float32(bytes)
  33. switch {
  34. case bytes >= TERABYTE:
  35. unit = "T"
  36. value = value / TERABYTE
  37. case bytes >= GIGABYTE:
  38. unit = "G"
  39. value = value / GIGABYTE
  40. case bytes >= MEGABYTE:
  41. unit = "M"
  42. value = value / MEGABYTE
  43. case bytes >= KILOBYTE:
  44. unit = "K"
  45. value = value / KILOBYTE
  46. case bytes >= BYTE:
  47. unit = "B"
  48. case bytes == 0:
  49. return "0"
  50. }
  51. stringValue := fmt.Sprintf("%.1f", value)
  52. stringValue = strings.TrimSuffix(stringValue, ".0")
  53. return fmt.Sprintf("%s%s", stringValue, unit)
  54. }
  55. // ToMegabytes parses a string formatted by ByteSize as megabytes.
  56. func ToMegabytes(s string) (uint64, error) {
  57. bytes, err := ToBytes(s)
  58. if err != nil {
  59. return 0, err
  60. }
  61. return bytes / MEGABYTE, nil
  62. }
  63. // ToBytes parses a string formatted by ByteSize as bytes. Note binary-prefixed and SI prefixed units both mean a base-2 units
  64. // KB = K = KiB = 1024
  65. // MB = M = MiB = 1024 * K
  66. // GB = G = GiB = 1024 * M
  67. // TB = T = TiB = 1024 * G
  68. func ToBytes(s string) (uint64, error) {
  69. parts := bytesPattern.FindStringSubmatch(strings.TrimSpace(s))
  70. if len(parts) < 3 {
  71. return 0, invalidByteQuantityError
  72. }
  73. value, err := strconv.ParseFloat(parts[1], 64)
  74. if err != nil || value <= 0 {
  75. return 0, invalidByteQuantityError
  76. }
  77. var bytes uint64
  78. unit := strings.ToUpper(parts[2])
  79. switch unit[:1] {
  80. case "T":
  81. bytes = uint64(value * TERABYTE)
  82. case "G":
  83. bytes = uint64(value * GIGABYTE)
  84. case "M":
  85. bytes = uint64(value * MEGABYTE)
  86. case "K":
  87. bytes = uint64(value * KILOBYTE)
  88. case "B":
  89. bytes = uint64(value * BYTE)
  90. }
  91. return bytes, nil
  92. }