You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

string.go 2.3KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. // Copyright 2022 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package util
  4. import "unsafe"
  5. func isSnakeCaseUpper(c byte) bool {
  6. return 'A' <= c && c <= 'Z'
  7. }
  8. func isSnakeCaseLowerOrNumber(c byte) bool {
  9. return 'a' <= c && c <= 'z' || '0' <= c && c <= '9'
  10. }
  11. // ToSnakeCase convert the input string to snake_case format.
  12. //
  13. // Some samples.
  14. //
  15. // "FirstName" => "first_name"
  16. // "HTTPServer" => "http_server"
  17. // "NoHTTPS" => "no_https"
  18. // "GO_PATH" => "go_path"
  19. // "GO PATH" => "go_path" // space is converted to underscore.
  20. // "GO-PATH" => "go_path" // hyphen is converted to underscore.
  21. func ToSnakeCase(input string) string {
  22. if len(input) == 0 {
  23. return ""
  24. }
  25. var res []byte
  26. if len(input) == 1 {
  27. c := input[0]
  28. if isSnakeCaseUpper(c) {
  29. res = []byte{c + 'a' - 'A'}
  30. } else if isSnakeCaseLowerOrNumber(c) {
  31. res = []byte{c}
  32. } else {
  33. res = []byte{'_'}
  34. }
  35. } else {
  36. res = make([]byte, 0, len(input)*4/3)
  37. pos := 0
  38. needSep := false
  39. for pos < len(input) {
  40. c := input[pos]
  41. if c >= 0x80 {
  42. res = append(res, c)
  43. pos++
  44. continue
  45. }
  46. isUpper := isSnakeCaseUpper(c)
  47. if isUpper || isSnakeCaseLowerOrNumber(c) {
  48. end := pos + 1
  49. if isUpper {
  50. // skip the following upper letters
  51. for end < len(input) && isSnakeCaseUpper(input[end]) {
  52. end++
  53. }
  54. if end-pos > 1 && end < len(input) && isSnakeCaseLowerOrNumber(input[end]) {
  55. end--
  56. }
  57. }
  58. // skip the following lower or number letters
  59. for end < len(input) && (isSnakeCaseLowerOrNumber(input[end]) || input[end] >= 0x80) {
  60. end++
  61. }
  62. if needSep {
  63. res = append(res, '_')
  64. }
  65. res = append(res, input[pos:end]...)
  66. pos = end
  67. needSep = true
  68. } else {
  69. res = append(res, '_')
  70. pos++
  71. needSep = false
  72. }
  73. }
  74. for i := 0; i < len(res); i++ {
  75. if isSnakeCaseUpper(res[i]) {
  76. res[i] += 'a' - 'A'
  77. }
  78. }
  79. }
  80. return UnsafeBytesToString(res)
  81. }
  82. // UnsafeBytesToString uses Go's unsafe package to convert a byte slice to a string.
  83. // TODO: replace all "goldmark/util.BytesToReadOnlyString" with this official approach
  84. func UnsafeBytesToString(b []byte) string {
  85. return unsafe.String(unsafe.SliceData(b), len(b))
  86. }
  87. func UnsafeStringToBytes(s string) []byte {
  88. return unsafe.Slice(unsafe.StringData(s), len(s))
  89. }