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.
 
 

96 lines
2.5 KiB

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