stringutil.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  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. // Any strings created at runtime, duplicate or not, are copied, even though by specification,
  23. // a go string is immutable. This utility allows us to cache runtime strings and retrieve them
  24. // when we expect heavy duplicates.
  25. var strings sync.Map
  26. func init() {
  27. rand.Seed(time.Now().UnixNano())
  28. }
  29. // Bank will return a non-copy of a string if it has been used before. Otherwise, it will store
  30. // the string as the unique instance.
  31. func Bank(s string) string {
  32. ss, _ := strings.LoadOrStore(s, s)
  33. return ss.(string)
  34. }
  35. // RandSeq generates a pseudo-random alphabetic string of the given length
  36. func RandSeq(n int) string {
  37. b := make([]rune, n)
  38. for i := range b {
  39. b[i] = alpha[rand.Intn(len(alpha))] // #nosec No need for a cryptographic strength random here
  40. }
  41. return string(b)
  42. }
  43. // FormatBytes takes a number of bytes and formats it as a string
  44. func FormatBytes(numBytes int64) string {
  45. if numBytes > TiB {
  46. return fmt.Sprintf("%.2fTiB", float64(numBytes)/TiB)
  47. }
  48. if numBytes > GiB {
  49. return fmt.Sprintf("%.2fGiB", float64(numBytes)/GiB)
  50. }
  51. if numBytes > MiB {
  52. return fmt.Sprintf("%.2fMiB", float64(numBytes)/MiB)
  53. }
  54. if numBytes > KiB {
  55. return fmt.Sprintf("%.2fKiB", float64(numBytes)/KiB)
  56. }
  57. return fmt.Sprintf("%dB", numBytes)
  58. }
  59. // FormatUTCOffset converts a duration to a string of format "-07:00"
  60. func FormatUTCOffset(dur time.Duration) string {
  61. utcOffSig := "+"
  62. if dur.Hours() < 0 {
  63. utcOffSig = "-"
  64. }
  65. utcOffHrs := int(math.Trunc(math.Abs(dur.Hours())))
  66. utcOffMin := int(math.Abs(dur.Minutes())) - (utcOffHrs * 60)
  67. return fmt.Sprintf("%s%02d:%02d", utcOffSig, utcOffHrs, utcOffMin)
  68. }
  69. // StringSlicesEqual checks if two string slices with arbitrary order have the same elements
  70. func StringSlicesEqual(left, right []string) bool {
  71. if len(left) != len(right) {
  72. return false
  73. }
  74. // Build maps for each slice that counts each unique instance
  75. leftMap := make(map[string]int, len(left))
  76. for _, str := range left {
  77. count, ok := leftMap[str]; if ok {
  78. leftMap[str] = count + 1
  79. } else {
  80. leftMap[str] = 1
  81. }
  82. }
  83. rightMap := make(map[string]int, len(right))
  84. for _, str := range right {
  85. count, ok := rightMap[str]; if ok {
  86. rightMap[str] = count + 1
  87. } else {
  88. rightMap[str] = 1
  89. }
  90. }
  91. // check that each unique key has the same count in each slice
  92. for key, count := range leftMap {
  93. if rightMap[key] != count {
  94. return false
  95. }
  96. }
  97. return true
  98. }