size.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. // Copyright (C) 2017 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package config
  7. import (
  8. "fmt"
  9. "strconv"
  10. "strings"
  11. "github.com/syncthing/syncthing/lib/fs"
  12. )
  13. func ParseSize(s string) (Size, error) {
  14. s = strings.TrimSpace(s)
  15. if s == "" {
  16. return Size{}, nil
  17. }
  18. var num, unit string
  19. for i := 0; i < len(s) && (s[i] >= '0' && s[i] <= '9' || s[i] == '.' || s[i] == ','); i++ {
  20. num = s[:i+1]
  21. }
  22. var i = len(num)
  23. for i < len(s) && s[i] == ' ' {
  24. i++
  25. }
  26. unit = s[i:]
  27. val, err := strconv.ParseFloat(num, 64)
  28. if err != nil {
  29. return Size{}, err
  30. }
  31. return Size{val, unit}, nil
  32. }
  33. func (s Size) BaseValue() float64 {
  34. unitPrefix := s.Unit
  35. if len(unitPrefix) > 1 {
  36. unitPrefix = unitPrefix[:1]
  37. }
  38. mult := 1.0
  39. switch unitPrefix {
  40. case "k", "K":
  41. mult = 1000
  42. case "m", "M":
  43. mult = 1000 * 1000
  44. case "g", "G":
  45. mult = 1000 * 1000 * 1000
  46. case "t", "T":
  47. mult = 1000 * 1000 * 1000 * 1000
  48. }
  49. return s.Value * mult
  50. }
  51. func (s Size) Percentage() bool {
  52. return strings.Contains(s.Unit, "%")
  53. }
  54. func (s Size) String() string {
  55. return fmt.Sprintf("%v %s", s.Value, s.Unit)
  56. }
  57. func (s *Size) ParseDefault(str string) error {
  58. sz, err := ParseSize(str)
  59. *s = sz
  60. return err
  61. }
  62. // CheckFreeSpace checks that the free space does not fall below the minimum required free space.
  63. func CheckFreeSpace(minFree Size, usage fs.Usage) error {
  64. val := minFree.BaseValue()
  65. if val <= 0 {
  66. return nil
  67. }
  68. if minFree.Percentage() {
  69. freePct := (float64(usage.Free) / float64(usage.Total)) * 100
  70. if freePct < val {
  71. return fmt.Errorf("current %.2f %% < required %v", freePct, minFree)
  72. }
  73. } else if float64(usage.Free) < val {
  74. return fmt.Errorf("current %sB < required %v", formatSI(usage.Free), minFree)
  75. }
  76. return nil
  77. }
  78. // checkAvailableSpace checks that the free space does not fall below the minimum
  79. // required free space, considering additional required space for a future operation.
  80. func checkAvailableSpace(req uint64, minFree Size, usage fs.Usage) error {
  81. if usage.Free < req {
  82. return fmt.Errorf("current %sB < required %sB", formatSI(usage.Free), formatSI(req))
  83. }
  84. usage.Free -= req
  85. return CheckFreeSpace(minFree, usage)
  86. }
  87. func formatSI(b uint64) string {
  88. switch {
  89. case b < 1000:
  90. return fmt.Sprintf("%d ", b)
  91. case b < 1000*1000:
  92. return fmt.Sprintf("%.1f K", float64(b)/1000)
  93. case b < 1000*1000*1000:
  94. return fmt.Sprintf("%.1f M", float64(b)/(1000*1000))
  95. case b < 1000*1000*1000*1000:
  96. return fmt.Sprintf("%.1f G", float64(b)/(1000*1000*1000))
  97. default:
  98. return fmt.Sprintf("%.1f T", float64(b)/(1000*1000*1000*1000))
  99. }
  100. }