| // 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] |
| } |