stringutil.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  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 interface {
  23. LoadOrStore(key, value string) (string, bool)
  24. LoadOrStoreFunc(key string, f func() string) (string, bool)
  25. Clear()
  26. }
  27. var (
  28. lock sync.RWMutex
  29. // stringBank is an unbounded string cache that is thread-safe. It is especially useful if
  30. // storing a large frequency of dynamically allocated duplicate strings.
  31. strings StringBank = NewStringBank()
  32. )
  33. func init() {
  34. rand.Seed(time.Now().UnixNano())
  35. }
  36. func UpdateStringBank(sb StringBank) {
  37. lock.Lock()
  38. defer lock.Unlock()
  39. strings.Clear()
  40. strings = sb
  41. }
  42. // GetStringBank returns the _current_ StringBank implementation. Note that the read-lock is
  43. // not held for the duration of usage, so the returned string bank could be swapped out
  44. // after being retrieved.
  45. func GetStringBank() StringBank {
  46. lock.RLock()
  47. defer lock.RUnlock()
  48. return strings
  49. }
  50. // Bank will return a non-copy of a string if it has been used before. Otherwise, it will store
  51. // the string as the unique instance.
  52. func Bank(s string) string {
  53. ss, _ := GetStringBank().LoadOrStore(s, s)
  54. return ss
  55. }
  56. // BankFunc will use the provided s string to check for an existing allocation of the string. However,
  57. // if no allocation exists, the f parameter will be used to create the string and store in the bank.
  58. func BankFunc(s string, f func() string) string {
  59. ss, _ := GetStringBank().LoadOrStoreFunc(s, f)
  60. return ss
  61. }
  62. func ClearBank() {
  63. GetStringBank().Clear()
  64. }
  65. // RandSeq generates a pseudo-random alphabetic string of the given length
  66. func RandSeq(n int) string {
  67. b := make([]rune, n)
  68. for i := range b {
  69. b[i] = alpha[rand.Intn(len(alpha))] // #nosec No need for a cryptographic strength random here
  70. }
  71. return string(b)
  72. }
  73. // RandSeq generates a pseudo-random alphabetic string of the given length
  74. func RandSeqWith(r *rand.Rand, n int) string {
  75. b := make([]rune, n)
  76. for i := range b {
  77. b[i] = alpha[r.Intn(len(alpha))] // #nosec No need for a cryptographic strength random here
  78. }
  79. return string(b)
  80. }
  81. // FormatBytes takes a number of bytes and formats it as a string
  82. func FormatBytes(numBytes int64) string {
  83. if numBytes > TiB {
  84. return fmt.Sprintf("%.2fTiB", float64(numBytes)/TiB)
  85. }
  86. if numBytes > GiB {
  87. return fmt.Sprintf("%.2fGiB", float64(numBytes)/GiB)
  88. }
  89. if numBytes > MiB {
  90. return fmt.Sprintf("%.2fMiB", float64(numBytes)/MiB)
  91. }
  92. if numBytes > KiB {
  93. return fmt.Sprintf("%.2fKiB", float64(numBytes)/KiB)
  94. }
  95. return fmt.Sprintf("%dB", numBytes)
  96. }
  97. // FormatUTCOffset converts a duration to a string of format "-07:00"
  98. func FormatUTCOffset(dur time.Duration) string {
  99. utcOffSig := "+"
  100. if dur.Hours() < 0 {
  101. utcOffSig = "-"
  102. }
  103. utcOffHrs := int(math.Trunc(math.Abs(dur.Hours())))
  104. utcOffMin := int(math.Abs(dur.Minutes())) - (utcOffHrs * 60)
  105. return fmt.Sprintf("%s%02d:%02d", utcOffSig, utcOffHrs, utcOffMin)
  106. }
  107. // StringSlicesEqual checks if two string slices with arbitrary order have the same elements
  108. func StringSlicesEqual(left, right []string) bool {
  109. if len(left) != len(right) {
  110. return false
  111. }
  112. // Build maps for each slice that counts each unique instance
  113. leftMap := make(map[string]int, len(left))
  114. for _, str := range left {
  115. count, ok := leftMap[str]
  116. if ok {
  117. leftMap[str] = count + 1
  118. } else {
  119. leftMap[str] = 1
  120. }
  121. }
  122. rightMap := make(map[string]int, len(right))
  123. for _, str := range right {
  124. count, ok := rightMap[str]
  125. if ok {
  126. rightMap[str] = count + 1
  127. } else {
  128. rightMap[str] = 1
  129. }
  130. }
  131. // check that each unique key has the same count in each slice
  132. for key, count := range leftMap {
  133. if rightMap[key] != count {
  134. return false
  135. }
  136. }
  137. return true
  138. }
  139. // DeleteEmptyStringsFromArray removes the empty strings from an array.
  140. func DeleteEmptyStringsFromArray(input []string) (output []string) {
  141. for _, str := range input {
  142. if str != "" {
  143. output = append(output, str)
  144. }
  145. }
  146. return
  147. }