summaryrefslogtreecommitdiffstats
path: root/src/internal/coverage/stringtab/stringtab.go
blob: 156c8ad1880f86a33bb391eaa52b43440420dbaa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package stringtab

import (
	"fmt"
	"internal/coverage/slicereader"
	"internal/coverage/uleb128"
	"io"
)

// This package implements string table writer and reader utilities,
// for use in emitting and reading/decoding coverage meta-data and
// counter-data files.

// Writer implements a string table writing utility.
type Writer struct {
	stab   map[string]uint32
	strs   []string
	tmp    []byte
	frozen bool
}

// InitWriter initializes a stringtab.Writer.
func (stw *Writer) InitWriter() {
	stw.stab = make(map[string]uint32)
	stw.tmp = make([]byte, 64)
}

// Nentries returns the number of strings interned so far.
func (stw *Writer) Nentries() uint32 {
	return uint32(len(stw.strs))
}

// Lookup looks up string 's' in the writer's table, adding
// a new entry if need be, and returning an index into the table.
func (stw *Writer) Lookup(s string) uint32 {
	if idx, ok := stw.stab[s]; ok {
		return idx
	}
	if stw.frozen {
		panic("internal error: string table previously frozen")
	}
	idx := uint32(len(stw.strs))
	stw.stab[s] = idx
	stw.strs = append(stw.strs, s)
	return idx
}

// Size computes the memory in bytes needed for the serialized
// version of a stringtab.Writer.
func (stw *Writer) Size() uint32 {
	rval := uint32(0)
	stw.tmp = stw.tmp[:0]
	stw.tmp = uleb128.AppendUleb128(stw.tmp, uint(len(stw.strs)))
	rval += uint32(len(stw.tmp))
	for _, s := range stw.strs {
		stw.tmp = stw.tmp[:0]
		slen := uint(len(s))
		stw.tmp = uleb128.AppendUleb128(stw.tmp, slen)
		rval += uint32(len(stw.tmp)) + uint32(slen)
	}
	return rval
}

// Write writes the string table in serialized form to the specified
// io.Writer.
func (stw *Writer) Write(w io.Writer) error {
	wr128 := func(v uint) error {
		stw.tmp = stw.tmp[:0]
		stw.tmp = uleb128.AppendUleb128(stw.tmp, v)
		if nw, err := w.Write(stw.tmp); err != nil {
			return fmt.Errorf("writing string table: %v", err)
		} else if nw != len(stw.tmp) {
			return fmt.Errorf("short write emitting stringtab uleb")
		}
		return nil
	}
	if err := wr128(uint(len(stw.strs))); err != nil {
		return err
	}
	for _, s := range stw.strs {
		if err := wr128(uint(len(s))); err != nil {
			return err
		}
		if nw, err := w.Write([]byte(s)); err != nil {
			return fmt.Errorf("writing string table: %v", err)
		} else if nw != len([]byte(s)) {
			return fmt.Errorf("short write emitting stringtab")
		}
	}
	return nil
}

// Freeze sends a signal to the writer that no more additions are
// allowed, only lookups of existing strings (if a lookup triggers
// addition, a panic will result). Useful as a mechanism for
// "finalizing" a string table prior to writing it out.
func (stw *Writer) Freeze() {
	stw.frozen = true
}

// Reader is a helper for reading a string table previously
// serialized by a Writer.Write call.
type Reader struct {
	r    *slicereader.Reader
	strs []string
}

// NewReader creates a stringtab.Reader to read the contents
// of a string table from 'r'.
func NewReader(r *slicereader.Reader) *Reader {
	str := &Reader{
		r: r,
	}
	return str
}

// Read reads/decodes a string table using the reader provided.
func (str *Reader) Read() {
	numEntries := int(str.r.ReadULEB128())
	str.strs = make([]string, 0, numEntries)
	for idx := 0; idx < numEntries; idx++ {
		slen := str.r.ReadULEB128()
		str.strs = append(str.strs, str.r.ReadString(int64(slen)))
	}
}

// Entries returns the number of decoded entries in a string table.
func (str *Reader) Entries() int {
	return len(str.strs)
}

// Get returns string 'idx' within the string table.
func (str *Reader) Get(idx uint32) string {
	return str.strs[idx]
}