stringtab.mx raw

   1  // Copyright 2022 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  package stringtab
   6  
   7  import (
   8  	"fmt"
   9  	"internal/coverage/slicereader"
  10  	"internal/coverage/uleb128"
  11  	"io"
  12  )
  13  
  14  // This package implements string table writer and reader utilities,
  15  // for use in emitting and reading/decoding coverage meta-data and
  16  // counter-data files.
  17  
  18  // Writer implements a string table writing utility.
  19  type Writer struct {
  20  	stab   map[string]uint32
  21  	strs   [][]byte
  22  	tmp    []byte
  23  	frozen bool
  24  }
  25  
  26  // InitWriter initializes a stringtab.Writer.
  27  func (stw *Writer) InitWriter() {
  28  	stw.stab = map[string]uint32{}
  29  	stw.tmp = []byte{:64}
  30  }
  31  
  32  // Nentries returns the number of strings interned so far.
  33  func (stw *Writer) Nentries() uint32 {
  34  	return uint32(len(stw.strs))
  35  }
  36  
  37  // Lookup looks up string 's' in the writer's table, adding
  38  // a new entry if need be, and returning an index into the table.
  39  func (stw *Writer) Lookup(s []byte) uint32 {
  40  	if idx, ok := stw.stab[s]; ok {
  41  		return idx
  42  	}
  43  	if stw.frozen {
  44  		panic("internal error: string table previously frozen")
  45  	}
  46  	idx := uint32(len(stw.strs))
  47  	stw.stab[s] = idx
  48  	stw.strs = append(stw.strs, s)
  49  	return idx
  50  }
  51  
  52  // Size computes the memory in bytes needed for the serialized
  53  // version of a stringtab.Writer.
  54  func (stw *Writer) Size() uint32 {
  55  	rval := uint32(0)
  56  	stw.tmp = stw.tmp[:0]
  57  	stw.tmp = uleb128.AppendUleb128(stw.tmp, uint(len(stw.strs)))
  58  	rval += uint32(len(stw.tmp))
  59  	for _, s := range stw.strs {
  60  		stw.tmp = stw.tmp[:0]
  61  		slen := uint(len(s))
  62  		stw.tmp = uleb128.AppendUleb128(stw.tmp, slen)
  63  		rval += uint32(len(stw.tmp)) + uint32(slen)
  64  	}
  65  	return rval
  66  }
  67  
  68  // Write writes the string table in serialized form to the specified
  69  // io.Writer.
  70  func (stw *Writer) Write(w io.Writer) error {
  71  	wr128 := func(v uint) error {
  72  		stw.tmp = stw.tmp[:0]
  73  		stw.tmp = uleb128.AppendUleb128(stw.tmp, v)
  74  		if nw, err := w.Write(stw.tmp); err != nil {
  75  			return fmt.Errorf("writing string table: %v", err)
  76  		} else if nw != len(stw.tmp) {
  77  			return fmt.Errorf("short write emitting stringtab uleb")
  78  		}
  79  		return nil
  80  	}
  81  	if err := wr128(uint(len(stw.strs))); err != nil {
  82  		return err
  83  	}
  84  	for _, s := range stw.strs {
  85  		if err := wr128(uint(len(s))); err != nil {
  86  			return err
  87  		}
  88  		if nw, err := w.Write([]byte(s)); err != nil {
  89  			return fmt.Errorf("writing string table: %v", err)
  90  		} else if nw != len([]byte(s)) {
  91  			return fmt.Errorf("short write emitting stringtab")
  92  		}
  93  	}
  94  	return nil
  95  }
  96  
  97  // Freeze sends a signal to the writer that no more additions are
  98  // allowed, only lookups of existing strings (if a lookup triggers
  99  // addition, a panic will result). Useful as a mechanism for
 100  // "finalizing" a string table prior to writing it out.
 101  func (stw *Writer) Freeze() {
 102  	stw.frozen = true
 103  }
 104  
 105  // Reader is a helper for reading a string table previously
 106  // serialized by a Writer.Write call.
 107  type Reader struct {
 108  	r    *slicereader.Reader
 109  	strs [][]byte
 110  }
 111  
 112  // NewReader creates a stringtab.Reader to read the contents
 113  // of a string table from 'r'.
 114  func NewReader(r *slicereader.Reader) *Reader {
 115  	str := &Reader{
 116  		r: r,
 117  	}
 118  	return str
 119  }
 120  
 121  // Read reads/decodes a string table using the reader provided.
 122  func (str *Reader) Read() {
 123  	numEntries := int(str.r.ReadULEB128())
 124  	str.strs = [][]byte{:0:numEntries}
 125  	for idx := 0; idx < numEntries; idx++ {
 126  		slen := str.r.ReadULEB128()
 127  		str.strs = append(str.strs, str.r.ReadString(int64(slen)))
 128  	}
 129  }
 130  
 131  // Entries returns the number of decoded entries in a string table.
 132  func (str *Reader) Entries() int {
 133  	return len(str.strs)
 134  }
 135  
 136  // Get returns string 'idx' within the string table.
 137  func (str *Reader) Get(idx uint32) []byte {
 138  	return str.strs[idx]
 139  }
 140