...
1 package btf
2
3 import (
4 "bufio"
5 "bytes"
6 "errors"
7 "fmt"
8 "io"
9 )
10
11 type stringTable struct {
12 base *stringTable
13 offsets []uint32
14 strings []string
15 }
16
17
18 type sizedReader interface {
19 io.Reader
20 Size() int64
21 }
22
23 func readStringTable(r sizedReader, base *stringTable) (*stringTable, error) {
24
25
26 firstStringOffset := uint32(0)
27 if base != nil {
28 idx := len(base.offsets) - 1
29 firstStringOffset = base.offsets[idx] + uint32(len(base.strings[idx])) + 1
30 }
31
32
33 const averageStringLength = 16
34
35 n := int(r.Size() / averageStringLength)
36 offsets := make([]uint32, 0, n)
37 strings := make([]string, 0, n)
38
39 offset := firstStringOffset
40 scanner := bufio.NewScanner(r)
41 scanner.Split(splitNull)
42 for scanner.Scan() {
43 str := scanner.Text()
44 offsets = append(offsets, offset)
45 strings = append(strings, str)
46 offset += uint32(len(str)) + 1
47 }
48 if err := scanner.Err(); err != nil {
49 return nil, err
50 }
51
52 if len(strings) == 0 {
53 return nil, errors.New("string table is empty")
54 }
55
56 if firstStringOffset == 0 && strings[0] != "" {
57 return nil, errors.New("first item in string table is non-empty")
58 }
59
60 return &stringTable{base, offsets, strings}, nil
61 }
62
63 func splitNull(data []byte, atEOF bool) (advance int, token []byte, err error) {
64 i := bytes.IndexByte(data, 0)
65 if i == -1 {
66 if atEOF && len(data) > 0 {
67 return 0, nil, errors.New("string table isn't null terminated")
68 }
69 return 0, nil, nil
70 }
71
72 return i + 1, data[:i], nil
73 }
74
75 func (st *stringTable) Lookup(offset uint32) (string, error) {
76 if st.base != nil && offset <= st.base.offsets[len(st.base.offsets)-1] {
77 return st.base.lookup(offset)
78 }
79 return st.lookup(offset)
80 }
81
82 func (st *stringTable) lookup(offset uint32) (string, error) {
83 i := search(st.offsets, offset)
84 if i == len(st.offsets) || st.offsets[i] != offset {
85 return "", fmt.Errorf("offset %d isn't start of a string", offset)
86 }
87
88 return st.strings[i], nil
89 }
90
91 func (st *stringTable) Length() int {
92 last := len(st.offsets) - 1
93 return int(st.offsets[last]) + len(st.strings[last]) + 1
94 }
95
96 func (st *stringTable) Marshal(w io.Writer) error {
97 for _, str := range st.strings {
98 _, err := io.WriteString(w, str)
99 if err != nil {
100 return err
101 }
102 _, err = w.Write([]byte{0})
103 if err != nil {
104 return err
105 }
106 }
107 return nil
108 }
109
110
111
112
113 func search(ints []uint32, needle uint32) int {
114
115
116 i, j := 0, len(ints)
117 for i < j {
118 h := int(uint(i+j) >> 1)
119
120 if !(ints[h] >= needle) {
121 i = h + 1
122 } else {
123 j = h
124 }
125 }
126
127 return i
128 }
129
View as plain text