strings.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. package btf
  2. import (
  3. "bufio"
  4. "bytes"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "strings"
  9. "golang.org/x/exp/maps"
  10. )
  11. type stringTable struct {
  12. base *stringTable
  13. offsets []uint32
  14. strings []string
  15. }
  16. // sizedReader is implemented by bytes.Reader, io.SectionReader, strings.Reader, etc.
  17. type sizedReader interface {
  18. io.Reader
  19. Size() int64
  20. }
  21. func readStringTable(r sizedReader, base *stringTable) (*stringTable, error) {
  22. // When parsing split BTF's string table, the first entry offset is derived
  23. // from the last entry offset of the base BTF.
  24. firstStringOffset := uint32(0)
  25. if base != nil {
  26. idx := len(base.offsets) - 1
  27. firstStringOffset = base.offsets[idx] + uint32(len(base.strings[idx])) + 1
  28. }
  29. // Derived from vmlinux BTF.
  30. const averageStringLength = 16
  31. n := int(r.Size() / averageStringLength)
  32. offsets := make([]uint32, 0, n)
  33. strings := make([]string, 0, n)
  34. offset := firstStringOffset
  35. scanner := bufio.NewScanner(r)
  36. scanner.Split(splitNull)
  37. for scanner.Scan() {
  38. str := scanner.Text()
  39. offsets = append(offsets, offset)
  40. strings = append(strings, str)
  41. offset += uint32(len(str)) + 1
  42. }
  43. if err := scanner.Err(); err != nil {
  44. return nil, err
  45. }
  46. if len(strings) == 0 {
  47. return nil, errors.New("string table is empty")
  48. }
  49. if firstStringOffset == 0 && strings[0] != "" {
  50. return nil, errors.New("first item in string table is non-empty")
  51. }
  52. return &stringTable{base, offsets, strings}, nil
  53. }
  54. func splitNull(data []byte, atEOF bool) (advance int, token []byte, err error) {
  55. i := bytes.IndexByte(data, 0)
  56. if i == -1 {
  57. if atEOF && len(data) > 0 {
  58. return 0, nil, errors.New("string table isn't null terminated")
  59. }
  60. return 0, nil, nil
  61. }
  62. return i + 1, data[:i], nil
  63. }
  64. func (st *stringTable) Lookup(offset uint32) (string, error) {
  65. if st.base != nil && offset <= st.base.offsets[len(st.base.offsets)-1] {
  66. return st.base.lookup(offset)
  67. }
  68. return st.lookup(offset)
  69. }
  70. func (st *stringTable) lookup(offset uint32) (string, error) {
  71. i := search(st.offsets, offset)
  72. if i == len(st.offsets) || st.offsets[i] != offset {
  73. return "", fmt.Errorf("offset %d isn't start of a string", offset)
  74. }
  75. return st.strings[i], nil
  76. }
  77. func (st *stringTable) Marshal(w io.Writer) error {
  78. for _, str := range st.strings {
  79. _, err := io.WriteString(w, str)
  80. if err != nil {
  81. return err
  82. }
  83. _, err = w.Write([]byte{0})
  84. if err != nil {
  85. return err
  86. }
  87. }
  88. return nil
  89. }
  90. // Num returns the number of strings in the table.
  91. func (st *stringTable) Num() int {
  92. return len(st.strings)
  93. }
  94. // search is a copy of sort.Search specialised for uint32.
  95. //
  96. // Licensed under https://go.dev/LICENSE
  97. func search(ints []uint32, needle uint32) int {
  98. // Define f(-1) == false and f(n) == true.
  99. // Invariant: f(i-1) == false, f(j) == true.
  100. i, j := 0, len(ints)
  101. for i < j {
  102. h := int(uint(i+j) >> 1) // avoid overflow when computing h
  103. // i ≤ h < j
  104. if !(ints[h] >= needle) {
  105. i = h + 1 // preserves f(i-1) == false
  106. } else {
  107. j = h // preserves f(j) == true
  108. }
  109. }
  110. // i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i.
  111. return i
  112. }
  113. // stringTableBuilder builds BTF string tables.
  114. type stringTableBuilder struct {
  115. length uint32
  116. strings map[string]uint32
  117. }
  118. // newStringTableBuilder creates a builder with the given capacity.
  119. //
  120. // capacity may be zero.
  121. func newStringTableBuilder(capacity int) *stringTableBuilder {
  122. var stb stringTableBuilder
  123. if capacity == 0 {
  124. // Use the runtime's small default size.
  125. stb.strings = make(map[string]uint32)
  126. } else {
  127. stb.strings = make(map[string]uint32, capacity)
  128. }
  129. // Ensure that the empty string is at index 0.
  130. stb.append("")
  131. return &stb
  132. }
  133. // Add a string to the table.
  134. //
  135. // Adding the same string multiple times will only store it once.
  136. func (stb *stringTableBuilder) Add(str string) (uint32, error) {
  137. if strings.IndexByte(str, 0) != -1 {
  138. return 0, fmt.Errorf("string contains null: %q", str)
  139. }
  140. offset, ok := stb.strings[str]
  141. if ok {
  142. return offset, nil
  143. }
  144. return stb.append(str), nil
  145. }
  146. func (stb *stringTableBuilder) append(str string) uint32 {
  147. offset := stb.length
  148. stb.length += uint32(len(str)) + 1
  149. stb.strings[str] = offset
  150. return offset
  151. }
  152. // Lookup finds the offset of a string in the table.
  153. //
  154. // Returns an error if str hasn't been added yet.
  155. func (stb *stringTableBuilder) Lookup(str string) (uint32, error) {
  156. offset, ok := stb.strings[str]
  157. if !ok {
  158. return 0, fmt.Errorf("string %q is not in table", str)
  159. }
  160. return offset, nil
  161. }
  162. // Length returns the length in bytes.
  163. func (stb *stringTableBuilder) Length() int {
  164. return int(stb.length)
  165. }
  166. // AppendEncoded appends the string table to the end of the provided buffer.
  167. func (stb *stringTableBuilder) AppendEncoded(buf []byte) []byte {
  168. n := len(buf)
  169. buf = append(buf, make([]byte, stb.Length())...)
  170. strings := buf[n:]
  171. for str, offset := range stb.strings {
  172. copy(strings[offset:], str)
  173. }
  174. return buf
  175. }
  176. // Copy the string table builder.
  177. func (stb *stringTableBuilder) Copy() *stringTableBuilder {
  178. return &stringTableBuilder{
  179. stb.length,
  180. maps.Clone(stb.strings),
  181. }
  182. }