tree_utils.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. package tree
  2. import (
  3. "errors"
  4. "fmt"
  5. "math"
  6. "regexp"
  7. "strconv"
  8. "sync"
  9. )
  10. var ErrUnparsableSize = errors.New("unparsable size")
  11. var matchSize = regexp.MustCompile("^(\\d+(\\.\\d+)?)([KMGTP]?B?)$")
  12. type ByteSize uint
  13. const (
  14. B ByteSize = 1 << (10 * iota)
  15. KB
  16. MB
  17. GB
  18. TB
  19. PB
  20. )
  21. func isLastLevel(node Node, maxLevel int) bool {
  22. return maxLevel-1 == node.Level()
  23. }
  24. func parseUnit(unit string) ByteSize {
  25. switch unit {
  26. case "B":
  27. return B
  28. case "KB":
  29. return KB
  30. case "MB":
  31. return MB
  32. case "GB":
  33. return GB
  34. case "TB":
  35. return TB
  36. case "PB":
  37. // I reckon this will never happen, but you never know
  38. return PB
  39. default:
  40. panic(ErrUnknownUnit)
  41. }
  42. }
  43. func parseUnitSafe(unit string) (ByteSize, error) {
  44. var returnVal ByteSize
  45. var err error
  46. defer func() {
  47. if r := recover(); r != nil {
  48. returnVal = 0
  49. err = ErrUnparsableSize
  50. }
  51. }()
  52. returnVal = parseUnit(unit)
  53. return returnVal, err
  54. }
  55. func fmtUnit(unit ByteSize) string {
  56. switch unit {
  57. case B:
  58. return "B"
  59. case KB:
  60. return "KB"
  61. case MB:
  62. return "MB"
  63. case GB:
  64. return "GB"
  65. case TB:
  66. return "TB"
  67. case PB:
  68. // I reckon this will never happen, but you never know
  69. return "PB"
  70. default:
  71. panic(ErrUnknownUnit)
  72. }
  73. }
  74. func fmtSize(size int64, unit ByteSize) string {
  75. var dimension float64
  76. dimension = float64(size) / float64(unit)
  77. return fmt.Sprintf("%.2f %s", dimension, fmtUnit(unit))
  78. }
  79. func ParseSize(size string) (int64, error) {
  80. result := matchSize.FindStringSubmatch(size)
  81. sizeNum := result[1]
  82. if sizeNum == "" {
  83. return 0, ErrUnparsableSize
  84. }
  85. unit, err := parseUnitSafe(result[3])
  86. if err != nil {
  87. return 0, err
  88. }
  89. num, err := strconv.ParseFloat(sizeNum, 64)
  90. if err != nil {
  91. return 0, err
  92. }
  93. return int64(math.Floor(num * float64(unit))), nil
  94. }
  95. func isIntoLimits(size, min, max int64) bool {
  96. if min <= size {
  97. if max == 0 {
  98. return true
  99. }
  100. if size <= max {
  101. return true
  102. }
  103. }
  104. return false
  105. }
  106. func merge(cs []<-chan int64) <-chan int64 {
  107. var wg sync.WaitGroup
  108. out := make(chan int64)
  109. // Start an output goroutine for each input channel in cs. output
  110. // copies values from c to out until c is closed, then calls wg.Done.
  111. output := func(c <-chan int64) {
  112. for n := range c {
  113. out <- n
  114. }
  115. wg.Done()
  116. }
  117. wg.Add(len(cs))
  118. for _, c := range cs {
  119. go output(c)
  120. }
  121. // Start a goroutine to close out once all the output goroutines are
  122. // done. This must start after the wg.Add call.
  123. go func() {
  124. wg.Wait()
  125. close(out)
  126. }()
  127. return out
  128. }