wstring.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. package stringbuffer
  2. import (
  3. "sync"
  4. "unicode/utf16"
  5. )
  6. // TODO: worth exporting and using in mkwinsyscall?
  7. // Uint16BufferSize is the buffer size in the pool, chosen somewhat arbitrarily to accommodate
  8. // large path strings:
  9. // MAX_PATH (260) + size of volume GUID prefix (49) + null terminator = 310.
  10. const MinWStringCap = 310
  11. // use *[]uint16 since []uint16 creates an extra allocation where the slice header
  12. // is copied to heap and then referenced via pointer in the interface header that sync.Pool
  13. // stores.
  14. var pathPool = sync.Pool{ // if go1.18+ adds Pool[T], use that to store []uint16 directly
  15. New: func() interface{} {
  16. b := make([]uint16, MinWStringCap)
  17. return &b
  18. },
  19. }
  20. func newBuffer() []uint16 { return *(pathPool.Get().(*[]uint16)) }
  21. // freeBuffer copies the slice header data, and puts a pointer to that in the pool.
  22. // This avoids taking a pointer to the slice header in WString, which can be set to nil.
  23. func freeBuffer(b []uint16) { pathPool.Put(&b) }
  24. // WString is a wide string buffer ([]uint16) meant for storing UTF-16 encoded strings
  25. // for interacting with Win32 APIs.
  26. // Sizes are specified as uint32 and not int.
  27. //
  28. // It is not thread safe.
  29. type WString struct {
  30. // type-def allows casting to []uint16 directly, use struct to prevent that and allow adding fields in the future.
  31. // raw buffer
  32. b []uint16
  33. }
  34. // NewWString returns a [WString] allocated from a shared pool with an
  35. // initial capacity of at least [MinWStringCap].
  36. // Since the buffer may have been previously used, its contents are not guaranteed to be empty.
  37. //
  38. // The buffer should be freed via [WString.Free]
  39. func NewWString() *WString {
  40. return &WString{
  41. b: newBuffer(),
  42. }
  43. }
  44. func (b *WString) Free() {
  45. if b.empty() {
  46. return
  47. }
  48. freeBuffer(b.b)
  49. b.b = nil
  50. }
  51. // ResizeTo grows the buffer to at least c and returns the new capacity, freeing the
  52. // previous buffer back into pool.
  53. func (b *WString) ResizeTo(c uint32) uint32 {
  54. // allready sufficient (or n is 0)
  55. if c <= b.Cap() {
  56. return b.Cap()
  57. }
  58. if c <= MinWStringCap {
  59. c = MinWStringCap
  60. }
  61. // allocate at-least double buffer size, as is done in [bytes.Buffer] and other places
  62. if c <= 2*b.Cap() {
  63. c = 2 * b.Cap()
  64. }
  65. b2 := make([]uint16, c)
  66. if !b.empty() {
  67. copy(b2, b.b)
  68. freeBuffer(b.b)
  69. }
  70. b.b = b2
  71. return c
  72. }
  73. // Buffer returns the underlying []uint16 buffer.
  74. func (b *WString) Buffer() []uint16 {
  75. if b.empty() {
  76. return nil
  77. }
  78. return b.b
  79. }
  80. // Pointer returns a pointer to the first uint16 in the buffer.
  81. // If the [WString.Free] has already been called, the pointer will be nil.
  82. func (b *WString) Pointer() *uint16 {
  83. if b.empty() {
  84. return nil
  85. }
  86. return &b.b[0]
  87. }
  88. // String returns the returns the UTF-8 encoding of the UTF-16 string in the buffer.
  89. //
  90. // It assumes that the data is null-terminated.
  91. func (b *WString) String() string {
  92. // Using [windows.UTF16ToString] would require importing "golang.org/x/sys/windows"
  93. // and would make this code Windows-only, which makes no sense.
  94. // So copy UTF16ToString code into here.
  95. // If other windows-specific code is added, switch to [windows.UTF16ToString]
  96. s := b.b
  97. for i, v := range s {
  98. if v == 0 {
  99. s = s[:i]
  100. break
  101. }
  102. }
  103. return string(utf16.Decode(s))
  104. }
  105. // Cap returns the underlying buffer capacity.
  106. func (b *WString) Cap() uint32 {
  107. if b.empty() {
  108. return 0
  109. }
  110. return b.cap()
  111. }
  112. func (b *WString) cap() uint32 { return uint32(cap(b.b)) }
  113. func (b *WString) empty() bool { return b == nil || b.cap() == 0 }