strings.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. package btf
  2. import (
  3. "bufio"
  4. "bytes"
  5. "errors"
  6. "fmt"
  7. "io"
  8. )
  9. type stringTable struct {
  10. base *stringTable
  11. offsets []uint32
  12. strings []string
  13. }
  14. // sizedReader is implemented by bytes.Reader, io.SectionReader, strings.Reader, etc.
  15. type sizedReader interface {
  16. io.Reader
  17. Size() int64
  18. }
  19. func readStringTable(r sizedReader, base *stringTable) (*stringTable, error) {
  20. // When parsing split BTF's string table, the first entry offset is derived
  21. // from the last entry offset of the base BTF.
  22. firstStringOffset := uint32(0)
  23. if base != nil {
  24. idx := len(base.offsets) - 1
  25. firstStringOffset = base.offsets[idx] + uint32(len(base.strings[idx])) + 1
  26. }
  27. // Derived from vmlinux BTF.
  28. const averageStringLength = 16
  29. n := int(r.Size() / averageStringLength)
  30. offsets := make([]uint32, 0, n)
  31. strings := make([]string, 0, n)
  32. offset := firstStringOffset
  33. scanner := bufio.NewScanner(r)
  34. scanner.Split(splitNull)
  35. for scanner.Scan() {
  36. str := scanner.Text()
  37. offsets = append(offsets, offset)
  38. strings = append(strings, str)
  39. offset += uint32(len(str)) + 1
  40. }
  41. if err := scanner.Err(); err != nil {
  42. return nil, err
  43. }
  44. if len(strings) == 0 {
  45. return nil, errors.New("string table is empty")
  46. }
  47. if firstStringOffset == 0 && strings[0] != "" {
  48. return nil, errors.New("first item in string table is non-empty")
  49. }
  50. return &stringTable{base, offsets, strings}, nil
  51. }
  52. func splitNull(data []byte, atEOF bool) (advance int, token []byte, err error) {
  53. i := bytes.IndexByte(data, 0)
  54. if i == -1 {
  55. if atEOF && len(data) > 0 {
  56. return 0, nil, errors.New("string table isn't null terminated")
  57. }
  58. return 0, nil, nil
  59. }
  60. return i + 1, data[:i], nil
  61. }
  62. func (st *stringTable) Lookup(offset uint32) (string, error) {
  63. if st.base != nil && offset <= st.base.offsets[len(st.base.offsets)-1] {
  64. return st.base.lookup(offset)
  65. }
  66. return st.lookup(offset)
  67. }
  68. func (st *stringTable) lookup(offset uint32) (string, error) {
  69. i := search(st.offsets, offset)
  70. if i == len(st.offsets) || st.offsets[i] != offset {
  71. return "", fmt.Errorf("offset %d isn't start of a string", offset)
  72. }
  73. return st.strings[i], nil
  74. }
  75. func (st *stringTable) Length() int {
  76. last := len(st.offsets) - 1
  77. return int(st.offsets[last]) + len(st.strings[last]) + 1
  78. }
  79. func (st *stringTable) Marshal(w io.Writer) error {
  80. for _, str := range st.strings {
  81. _, err := io.WriteString(w, str)
  82. if err != nil {
  83. return err
  84. }
  85. _, err = w.Write([]byte{0})
  86. if err != nil {
  87. return err
  88. }
  89. }
  90. return nil
  91. }
  92. // search is a copy of sort.Search specialised for uint32.
  93. //
  94. // Licensed under https://go.dev/LICENSE
  95. func search(ints []uint32, needle uint32) int {
  96. // Define f(-1) == false and f(n) == true.
  97. // Invariant: f(i-1) == false, f(j) == true.
  98. i, j := 0, len(ints)
  99. for i < j {
  100. h := int(uint(i+j) >> 1) // avoid overflow when computing h
  101. // i ≤ h < j
  102. if !(ints[h] >= needle) {
  103. i = h + 1 // preserves f(i-1) == false
  104. } else {
  105. j = h // preserves f(j) == true
  106. }
  107. }
  108. // i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i.
  109. return i
  110. }