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