strings_unsafe.go 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. // Copyright 2018 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // +build !purego,!appengine
  5. package strs
  6. import (
  7. "unsafe"
  8. pref "google.golang.org/protobuf/reflect/protoreflect"
  9. )
  10. type (
  11. stringHeader struct {
  12. Data unsafe.Pointer
  13. Len int
  14. }
  15. sliceHeader struct {
  16. Data unsafe.Pointer
  17. Len int
  18. Cap int
  19. }
  20. )
  21. // UnsafeString returns an unsafe string reference of b.
  22. // The caller must treat the input slice as immutable.
  23. //
  24. // WARNING: Use carefully. The returned result must not leak to the end user
  25. // unless the input slice is provably immutable.
  26. func UnsafeString(b []byte) (s string) {
  27. src := (*sliceHeader)(unsafe.Pointer(&b))
  28. dst := (*stringHeader)(unsafe.Pointer(&s))
  29. dst.Data = src.Data
  30. dst.Len = src.Len
  31. return s
  32. }
  33. // UnsafeBytes returns an unsafe bytes slice reference of s.
  34. // The caller must treat returned slice as immutable.
  35. //
  36. // WARNING: Use carefully. The returned result must not leak to the end user.
  37. func UnsafeBytes(s string) (b []byte) {
  38. src := (*stringHeader)(unsafe.Pointer(&s))
  39. dst := (*sliceHeader)(unsafe.Pointer(&b))
  40. dst.Data = src.Data
  41. dst.Len = src.Len
  42. dst.Cap = src.Len
  43. return b
  44. }
  45. // Builder builds a set of strings with shared lifetime.
  46. // This differs from strings.Builder, which is for building a single string.
  47. type Builder struct {
  48. buf []byte
  49. }
  50. // AppendFullName is equivalent to protoreflect.FullName.Append,
  51. // but optimized for large batches where each name has a shared lifetime.
  52. func (sb *Builder) AppendFullName(prefix pref.FullName, name pref.Name) pref.FullName {
  53. n := len(prefix) + len(".") + len(name)
  54. if len(prefix) == 0 {
  55. n -= len(".")
  56. }
  57. sb.grow(n)
  58. sb.buf = append(sb.buf, prefix...)
  59. sb.buf = append(sb.buf, '.')
  60. sb.buf = append(sb.buf, name...)
  61. return pref.FullName(sb.last(n))
  62. }
  63. // MakeString is equivalent to string(b), but optimized for large batches
  64. // with a shared lifetime.
  65. func (sb *Builder) MakeString(b []byte) string {
  66. sb.grow(len(b))
  67. sb.buf = append(sb.buf, b...)
  68. return sb.last(len(b))
  69. }
  70. func (sb *Builder) grow(n int) {
  71. if cap(sb.buf)-len(sb.buf) >= n {
  72. return
  73. }
  74. // Unlike strings.Builder, we do not need to copy over the contents
  75. // of the old buffer since our builder provides no API for
  76. // retrieving previously created strings.
  77. sb.buf = make([]byte, 2*(cap(sb.buf)+n))
  78. }
  79. func (sb *Builder) last(n int) string {
  80. return UnsafeString(sb.buf[len(sb.buf)-n:])
  81. }