GoPLS Viewer

Home|gopls/internal/pkgbits/encoder.go
1// Copyright 2021 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
5package pkgbits
6
7import (
8    "bytes"
9    "crypto/md5"
10    "encoding/binary"
11    "go/constant"
12    "io"
13    "math/big"
14    "runtime"
15)
16
17// currentVersion is the current version number.
18//
19//   - v0: initial prototype
20//
21//   - v1: adds the flags uint32 word
22const currentVersion uint32 = 1
23
24// A PkgEncoder provides methods for encoding a package's Unified IR
25// export data.
26type PkgEncoder struct {
27    // elems holds the bitstream for previously encoded elements.
28    elems [numRelocs][]string
29
30    // stringsIdx maps previously encoded strings to their index within
31    // the RelocString section, to allow deduplication. That is,
32    // elems[RelocString][stringsIdx[s]] == s (if present).
33    stringsIdx map[string]Index
34
35    // syncFrames is the number of frames to write at each sync
36    // marker. A negative value means sync markers are omitted.
37    syncFrames int
38}
39
40// SyncMarkers reports whether pw uses sync markers.
41func (pw *PkgEncoderSyncMarkers() bool { return pw.syncFrames >= 0 }
42
43// NewPkgEncoder returns an initialized PkgEncoder.
44//
45// syncFrames is the number of caller frames that should be serialized
46// at Sync points. Serializing additional frames results in larger
47// export data files, but can help diagnosing desync errors in
48// higher-level Unified IR reader/writer code. If syncFrames is
49// negative, then sync markers are omitted entirely.
50func NewPkgEncoder(syncFrames intPkgEncoder {
51    return PkgEncoder{
52        stringsIdxmake(map[string]Index),
53        syncFramessyncFrames,
54    }
55}
56
57// DumpTo writes the package's encoded data to out0 and returns the
58// package fingerprint.
59func (pw *PkgEncoderDumpTo(out0 io.Writer) (fingerprint [8]byte) {
60    h := md5.New()
61    out := io.MultiWriter(out0h)
62
63    writeUint32 := func(x uint32) {
64        assert(binary.Write(outbinary.LittleEndianx) == nil)
65    }
66
67    writeUint32(currentVersion)
68
69    var flags uint32
70    if pw.SyncMarkers() {
71        flags |= flagSyncMarkers
72    }
73    writeUint32(flags)
74
75    // Write elemEndsEnds.
76    var sum uint32
77    for _elems := range &pw.elems {
78        sum += uint32(len(elems))
79        writeUint32(sum)
80    }
81
82    // Write elemEnds.
83    sum = 0
84    for _elems := range &pw.elems {
85        for _elem := range elems {
86            sum += uint32(len(elem))
87            writeUint32(sum)
88        }
89    }
90
91    // Write elemData.
92    for _elems := range &pw.elems {
93        for _elem := range elems {
94            _err := io.WriteString(outelem)
95            assert(err == nil)
96        }
97    }
98
99    // Write fingerprint.
100    copy(fingerprint[:], h.Sum(nil))
101    _err := out0.Write(fingerprint[:])
102    assert(err == nil)
103
104    return
105}
106
107// StringIdx adds a string value to the strings section, if not
108// already present, and returns its index.
109func (pw *PkgEncoderStringIdx(s stringIndex {
110    if idxok := pw.stringsIdx[s]; ok {
111        assert(pw.elems[RelocString][idx] == s)
112        return idx
113    }
114
115    idx := Index(len(pw.elems[RelocString]))
116    pw.elems[RelocString] = append(pw.elems[RelocString], s)
117    pw.stringsIdx[s] = idx
118    return idx
119}
120
121// NewEncoder returns an Encoder for a new element within the given
122// section, and encodes the given SyncMarker as the start of the
123// element bitstream.
124func (pw *PkgEncoderNewEncoder(k RelocKindmarker SyncMarkerEncoder {
125    e := pw.NewEncoderRaw(k)
126    e.Sync(marker)
127    return e
128}
129
130// NewEncoderRaw returns an Encoder for a new element within the given
131// section.
132//
133// Most callers should use NewEncoder instead.
134func (pw *PkgEncoderNewEncoderRaw(k RelocKindEncoder {
135    idx := Index(len(pw.elems[k]))
136    pw.elems[k] = append(pw.elems[k], ""// placeholder
137
138    return Encoder{
139        p:   pw,
140        k:   k,
141        Idxidx,
142    }
143}
144
145// An Encoder provides methods for encoding an individual element's
146// bitstream data.
147type Encoder struct {
148    p *PkgEncoder
149
150    Relocs   []RelocEnt
151    RelocMap map[RelocEnt]uint32
152    Data     bytes.Buffer // accumulated element bitstream data
153
154    encodingRelocHeader bool
155
156    k   RelocKind
157    Idx Index // index within relocation section
158}
159
160// Flush finalizes the element's bitstream and returns its Index.
161func (w *EncoderFlush() Index {
162    var sb bytes.Buffer // TODO(mdempsky): strings.Builder after #44505 is resolved
163
164    // Backup the data so we write the relocations at the front.
165    var tmp bytes.Buffer
166    io.Copy(&tmp, &w.Data)
167
168    // TODO(mdempsky): Consider writing these out separately so they're
169    // easier to strip, along with function bodies, so that we can prune
170    // down to just the data that's relevant to go/types.
171    if w.encodingRelocHeader {
172        panic("encodingRelocHeader already true; recursive flush?")
173    }
174    w.encodingRelocHeader = true
175    w.Sync(SyncRelocs)
176    w.Len(len(w.Relocs))
177    for _rEnt := range w.Relocs {
178        w.Sync(SyncReloc)
179        w.Len(int(rEnt.Kind))
180        w.Len(int(rEnt.Idx))
181    }
182
183    io.Copy(&sb, &w.Data)
184    io.Copy(&sb, &tmp)
185    w.p.elems[w.k][w.Idx] = sb.String()
186
187    return w.Idx
188}
189
190func (w *EncodercheckErr(err error) {
191    if err != nil {
192        errorf("unexpected encoding error: %v"err)
193    }
194}
195
196func (w *EncoderrawUvarint(x uint64) {
197    var buf [binary.MaxVarintLen64]byte
198    n := binary.PutUvarint(buf[:], x)
199    _err := w.Data.Write(buf[:n])
200    w.checkErr(err)
201}
202
203func (w *EncoderrawVarint(x int64) {
204    // Zig-zag encode.
205    ux := uint64(x) << 1
206    if x < 0 {
207        ux = ^ux
208    }
209
210    w.rawUvarint(ux)
211}
212
213func (w *EncoderrawReloc(r RelocKindidx Indexint {
214    e := RelocEnt{ridx}
215    if w.RelocMap != nil {
216        if iok := w.RelocMap[e]; ok {
217            return int(i)
218        }
219    } else {
220        w.RelocMap = make(map[RelocEnt]uint32)
221    }
222
223    i := len(w.Relocs)
224    w.RelocMap[e] = uint32(i)
225    w.Relocs = append(w.Relocse)
226    return i
227}
228
229func (w *EncoderSync(m SyncMarker) {
230    if !w.p.SyncMarkers() {
231        return
232    }
233
234    // Writing out stack frame string references requires working
235    // relocations, but writing out the relocations themselves involves
236    // sync markers. To prevent infinite recursion, we simply trim the
237    // stack frame for sync markers within the relocation header.
238    var frames []string
239    if !w.encodingRelocHeader && w.p.syncFrames > 0 {
240        pcs := make([]uintptrw.p.syncFrames)
241        n := runtime.Callers(2pcs)
242        frames = fmtFrames(pcs[:n]...)
243    }
244
245    // TODO(mdempsky): Save space by writing out stack frames as a
246    // linked list so we can share common stack frames.
247    w.rawUvarint(uint64(m))
248    w.rawUvarint(uint64(len(frames)))
249    for _frame := range frames {
250        w.rawUvarint(uint64(w.rawReloc(RelocStringw.p.StringIdx(frame))))
251    }
252}
253
254// Bool encodes and writes a bool value into the element bitstream,
255// and then returns the bool value.
256//
257// For simple, 2-alternative encodings, the idiomatic way to call Bool
258// is something like:
259//
260//    if w.Bool(x != 0) {
261//        // alternative #1
262//    } else {
263//        // alternative #2
264//    }
265//
266// For multi-alternative encodings, use Code instead.
267func (w *EncoderBool(b boolbool {
268    w.Sync(SyncBool)
269    var x byte
270    if b {
271        x = 1
272    }
273    err := w.Data.WriteByte(x)
274    w.checkErr(err)
275    return b
276}
277
278// Int64 encodes and writes an int64 value into the element bitstream.
279func (w *EncoderInt64(x int64) {
280    w.Sync(SyncInt64)
281    w.rawVarint(x)
282}
283
284// Uint64 encodes and writes a uint64 value into the element bitstream.
285func (w *EncoderUint64(x uint64) {
286    w.Sync(SyncUint64)
287    w.rawUvarint(x)
288}
289
290// Len encodes and writes a non-negative int value into the element bitstream.
291func (w *EncoderLen(x int) { assert(x >= 0); w.Uint64(uint64(x)) }
292
293// Int encodes and writes an int value into the element bitstream.
294func (w *EncoderInt(x int) { w.Int64(int64(x)) }
295
296// Len encodes and writes a uint value into the element bitstream.
297func (w *EncoderUint(x uint) { w.Uint64(uint64(x)) }
298
299// Reloc encodes and writes a relocation for the given (section,
300// index) pair into the element bitstream.
301//
302// Note: Only the index is formally written into the element
303// bitstream, so bitstream decoders must know from context which
304// section an encoded relocation refers to.
305func (w *EncoderReloc(r RelocKindidx Index) {
306    w.Sync(SyncUseReloc)
307    w.Len(w.rawReloc(ridx))
308}
309
310// Code encodes and writes a Code value into the element bitstream.
311func (w *EncoderCode(c Code) {
312    w.Sync(c.Marker())
313    w.Len(c.Value())
314}
315
316// String encodes and writes a string value into the element
317// bitstream.
318//
319// Internally, strings are deduplicated by adding them to the strings
320// section (if not already present), and then writing a relocation
321// into the element bitstream.
322func (w *EncoderString(s string) {
323    w.Sync(SyncString)
324    w.Reloc(RelocStringw.p.StringIdx(s))
325}
326
327// Strings encodes and writes a variable-length slice of strings into
328// the element bitstream.
329func (w *EncoderStrings(ss []string) {
330    w.Len(len(ss))
331    for _s := range ss {
332        w.String(s)
333    }
334}
335
336// Value encodes and writes a constant.Value into the element
337// bitstream.
338func (w *EncoderValue(val constant.Value) {
339    w.Sync(SyncValue)
340    if w.Bool(val.Kind() == constant.Complex) {
341        w.scalar(constant.Real(val))
342        w.scalar(constant.Imag(val))
343    } else {
344        w.scalar(val)
345    }
346}
347
348func (w *Encoderscalar(val constant.Value) {
349    switch v := constant.Val(val).(type) {
350    default:
351        errorf("unhandled %v (%v)"valval.Kind())
352    case bool:
353        w.Code(ValBool)
354        w.Bool(v)
355    case string:
356        w.Code(ValString)
357        w.String(v)
358    case int64:
359        w.Code(ValInt64)
360        w.Int64(v)
361    case *big.Int:
362        w.Code(ValBigInt)
363        w.bigInt(v)
364    case *big.Rat:
365        w.Code(ValBigRat)
366        w.bigInt(v.Num())
367        w.bigInt(v.Denom())
368    case *big.Float:
369        w.Code(ValBigFloat)
370        w.bigFloat(v)
371    }
372}
373
374func (w *EncoderbigInt(v *big.Int) {
375    b := v.Bytes()
376    w.String(string(b)) // TODO: More efficient encoding.
377    w.Bool(v.Sign() < 0)
378}
379
380func (w *EncoderbigFloat(v *big.Float) {
381    b := v.Append(nil'p', -1)
382    w.String(string(b)) // TODO: More efficient encoding.
383}
384
MembersX
Encoder.Int64.x
Encoder.String.w
Encoder.Strings.ss
Encoder.Data
Encoder.Sync.BlockStmt.n
Encoder.Int.x
Encoder.Uint.w
Encoder.scalar.val
md5
Encoder.rawUvarint.buf
PkgEncoder.NewEncoderRaw.pw
Encoder.p
Encoder.scalar.w
PkgEncoder.DumpTo.RangeStmt_2353.BlockStmt.RangeStmt_2389.elem
PkgEncoder.DumpTo.RangeStmt_2353.BlockStmt.RangeStmt_2389.BlockStmt._
Encoder.rawUvarint.err
Encoder.Sync.w
Encoder.bigInt.w
Encoder.Code
bytes
PkgEncoder.DumpTo.RangeStmt_2353.elems
PkgEncoder.NewEncoderRaw.k
PkgEncoder.NewEncoderRaw.idx
Encoder.rawVarint.w
Encoder.rawReloc.i
Encoder.Bool.x
Encoder.Bool.err
PkgEncoder.SyncMarkers.pw
PkgEncoder.DumpTo.out
Encoder.Bool.w
Encoder.Int64.w
Encoder.Strings.w
PkgEncoder.NewEncoder
PkgEncoder.NewEncoderRaw
Encoder.Relocs
Encoder.scalar
Encoder.bigFloat.b
Encoder.Flush.sb
Encoder.checkErr.err
Encoder.Reloc
NewPkgEncoder
PkgEncoder.StringIdx.pw
Encoder.Flush.tmp
Encoder.Len.x
PkgEncoder.StringIdx
Encoder.k
Encoder.Uint64.x
Encoder.Code.c
Encoder.rawReloc.r
Encoder.Sync.RangeStmt_6218.frame
Encoder.Reloc.r
Encoder.Strings.RangeStmt_8523.s
PkgEncoder.DumpTo
Encoder.rawUvarint.w
Encoder.rawUvarint
Encoder.bigFloat.w
PkgEncoder.DumpTo.fingerprint
Encoder.checkErr.w
Encoder.Uint64.w
Encoder.bigInt.v
Encoder.Value
Encoder.bigInt.b
PkgEncoder.StringIdx.s
PkgEncoder.NewEncoder.pw
PkgEncoder.NewEncoder.k
Encoder
Encoder.Sync
Encoder.bigInt
PkgEncoder.DumpTo.h
PkgEncoder.DumpTo.RangeStmt_2353.BlockStmt.RangeStmt_2389.BlockStmt.err
Encoder.bigFloat
Encoder.Sync.frames
Encoder.Bool
Encoder.Uint64
PkgEncoder.DumpTo.sum
Encoder.checkErr
Encoder.Sync.m
Encoder.Flush.w
Encoder.Flush
Encoder.Int64
PkgEncoder.DumpTo.out0
PkgEncoder.StringIdx.idx
Encoder.Strings
PkgEncoder.DumpTo.err
Encoder.Len
Encoder.Uint
PkgEncoder.DumpTo.pw
Encoder.RelocMap
Encoder.Int
Encoder.Code.w
currentVersion
PkgEncoder.DumpTo.RangeStmt_2096.elems
Encoder.rawUvarint._
Encoder.rawReloc.idx
Encoder.Len.w
Encoder.String
PkgEncoder.SyncMarkers
PkgEncoder.DumpTo.flags
Encoder.Idx
Encoder.String.s
PkgEncoder.syncFrames
NewPkgEncoder.syncFrames
Encoder.Int.w
Encoder.Reloc.idx
Encoder.encodingRelocHeader
Encoder.Flush.RangeStmt_4607.rEnt
Encoder.rawReloc.w
Encoder.rawReloc.e
Encoder.Bool.b
Encoder.Value.w
PkgEncoder.elems
PkgEncoder.DumpTo.RangeStmt_2211.elems
Encoder.bigFloat.v
Encoder.Reloc.w
Encoder.rawUvarint.x
Encoder.rawVarint
Encoder.rawVarint.x
Encoder.rawReloc
Encoder.Sync.BlockStmt.pcs
Encoder.Uint.x
Encoder.Value.val
PkgEncoder
PkgEncoder.NewEncoder.marker
PkgEncoder.NewEncoder.e
Encoder.rawUvarint.n
PkgEncoder.DumpTo.RangeStmt_2211.BlockStmt.RangeStmt_2247.elem
PkgEncoder.DumpTo._
PkgEncoder.stringsIdx
Members
X