stringutil.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. package stringutil
  2. import (
  3. "fmt"
  4. "math"
  5. "math/rand"
  6. "sync"
  7. "time"
  8. )
  9. const (
  10. _ = 1 << (10 * iota)
  11. // KiB is bytes per Kibibyte
  12. KiB
  13. // MiB is bytes per Mebibyte
  14. MiB
  15. // GiB is bytes per Gibibyte
  16. GiB
  17. // TiB is bytes per Tebibyte
  18. TiB
  19. )
  20. var alpha = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
  21. var alphanumeric = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
  22. type stringBank struct {
  23. lock sync.Mutex
  24. m map[string]string
  25. }
  26. func newStringBank() *stringBank {
  27. return &stringBank{
  28. m: make(map[string]string),
  29. }
  30. }
  31. func (sb *stringBank) LoadOrStore(key, value string) (string, bool) {
  32. sb.lock.Lock()
  33. if v, ok := sb.m[key]; ok {
  34. sb.lock.Unlock()
  35. return v, ok
  36. }
  37. sb.m[key] = value
  38. sb.lock.Unlock()
  39. return value, false
  40. }
  41. func (sb *stringBank) LoadOrStoreFunc(key string, f func() string) (string, bool) {
  42. sb.lock.Lock()
  43. if v, ok := sb.m[key]; ok {
  44. sb.lock.Unlock()
  45. return v, ok
  46. }
  47. // create the key and value using the func (the key could be deallocated later)
  48. value := f()
  49. sb.m[value] = value
  50. sb.lock.Unlock()
  51. return value, false
  52. }
  53. func (sb *stringBank) Clear() {
  54. sb.lock.Lock()
  55. sb.m = make(map[string]string)
  56. sb.lock.Unlock()
  57. }
  58. // stringBank is an unbounded string cache that is thread-safe. It is especially useful if
  59. // storing a large frequency of dynamically allocated duplicate strings.
  60. var strings = newStringBank() // sync.Map
  61. func init() {
  62. rand.Seed(time.Now().UnixNano())
  63. }
  64. // Bank will return a non-copy of a string if it has been used before. Otherwise, it will store
  65. // the string as the unique instance.
  66. func Bank(s string) string {
  67. ss, _ := strings.LoadOrStore(s, s)
  68. return ss
  69. }
  70. // BankFunc will use the provided s string to check for an existing allocation of the string. However,
  71. // if no allocation exists, the f parameter will be used to create the string and store in the bank.
  72. func BankFunc(s string, f func() string) string {
  73. ss, _ := strings.LoadOrStoreFunc(s, f)
  74. return ss
  75. }
  76. func ClearBank() {
  77. strings.Clear()
  78. }
  79. // RandSeq generates a pseudo-random alphabetic string of the given length
  80. func RandSeq(n int) string {
  81. b := make([]rune, n)
  82. for i := range b {
  83. b[i] = alpha[rand.Intn(len(alpha))] // #nosec No need for a cryptographic strength random here
  84. }
  85. return string(b)
  86. }
  87. // FormatBytes takes a number of bytes and formats it as a string
  88. func FormatBytes(numBytes int64) string {
  89. if numBytes > TiB {
  90. return fmt.Sprintf("%.2fTiB", float64(numBytes)/TiB)
  91. }
  92. if numBytes > GiB {
  93. return fmt.Sprintf("%.2fGiB", float64(numBytes)/GiB)
  94. }
  95. if numBytes > MiB {
  96. return fmt.Sprintf("%.2fMiB", float64(numBytes)/MiB)
  97. }
  98. if numBytes > KiB {
  99. return fmt.Sprintf("%.2fKiB", float64(numBytes)/KiB)
  100. }
  101. return fmt.Sprintf("%dB", numBytes)
  102. }
  103. // FormatUTCOffset converts a duration to a string of format "-07:00"
  104. func FormatUTCOffset(dur time.Duration) string {
  105. utcOffSig := "+"
  106. if dur.Hours() < 0 {
  107. utcOffSig = "-"
  108. }
  109. utcOffHrs := int(math.Trunc(math.Abs(dur.Hours())))
  110. utcOffMin := int(math.Abs(dur.Minutes())) - (utcOffHrs * 60)
  111. return fmt.Sprintf("%s%02d:%02d", utcOffSig, utcOffHrs, utcOffMin)
  112. }
  113. // StringSlicesEqual checks if two string slices with arbitrary order have the same elements
  114. func StringSlicesEqual(left, right []string) bool {
  115. if len(left) != len(right) {
  116. return false
  117. }
  118. // Build maps for each slice that counts each unique instance
  119. leftMap := make(map[string]int, len(left))
  120. for _, str := range left {
  121. count, ok := leftMap[str]
  122. if ok {
  123. leftMap[str] = count + 1
  124. } else {
  125. leftMap[str] = 1
  126. }
  127. }
  128. rightMap := make(map[string]int, len(right))
  129. for _, str := range right {
  130. count, ok := rightMap[str]
  131. if ok {
  132. rightMap[str] = count + 1
  133. } else {
  134. rightMap[str] = 1
  135. }
  136. }
  137. // check that each unique key has the same count in each slice
  138. for key, count := range leftMap {
  139. if rightMap[key] != count {
  140. return false
  141. }
  142. }
  143. return true
  144. }
  145. // DeleteEmptyStringsFromArray removes the empty strings from an array.
  146. func DeleteEmptyStringsFromArray(input []string) (output []string) {
  147. for _, str := range input {
  148. if str != "" {
  149. output = append(output, str)
  150. }
  151. }
  152. return
  153. }