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
5package modindex
6
7import (
8	"cmd/go/internal/base"
9	"encoding/binary"
10	"go/token"
11	"sort"
12)
13
14const indexVersion = "go index v2" // 11 bytes (plus \n), to align uint32s in index
15
16// encodeModuleBytes produces the encoded representation of the module index.
17// encodeModuleBytes may modify the packages slice.
18func encodeModuleBytes(packages []*rawPackage) []byte {
19	e := newEncoder()
20	e.Bytes([]byte(indexVersion + "\n"))
21	stringTableOffsetPos := e.Pos() // fill this at the end
22	e.Uint32(0)                     // string table offset
23	sort.Slice(packages, func(i, j int) bool {
24		return packages[i].dir < packages[j].dir
25	})
26	e.Int(len(packages))
27	packagesPos := e.Pos()
28	for _, p := range packages {
29		e.String(p.dir)
30		e.Int(0)
31	}
32	for i, p := range packages {
33		e.IntAt(e.Pos(), packagesPos+8*i+4)
34		encodePackage(e, p)
35	}
36	e.IntAt(e.Pos(), stringTableOffsetPos)
37	e.Bytes(e.stringTable)
38	e.Bytes([]byte{0xFF}) // end of string table marker
39	return e.b
40}
41
42func encodePackageBytes(p *rawPackage) []byte {
43	return encodeModuleBytes([]*rawPackage{p})
44}
45
46func encodePackage(e *encoder, p *rawPackage) {
47	e.String(p.error)
48	e.String(p.dir)
49	e.Int(len(p.sourceFiles))      // number of source files
50	sourceFileOffsetPos := e.Pos() // the pos of the start of the source file offsets
51	for range p.sourceFiles {
52		e.Int(0)
53	}
54	for i, f := range p.sourceFiles {
55		e.IntAt(e.Pos(), sourceFileOffsetPos+4*i)
56		encodeFile(e, f)
57	}
58}
59
60func encodeFile(e *encoder, f *rawFile) {
61	e.String(f.error)
62	e.String(f.parseError)
63	e.String(f.synopsis)
64	e.String(f.name)
65	e.String(f.pkgName)
66	e.Bool(f.ignoreFile)
67	e.Bool(f.binaryOnly)
68	e.String(f.cgoDirectives)
69	e.String(f.goBuildConstraint)
70
71	e.Int(len(f.plusBuildConstraints))
72	for _, s := range f.plusBuildConstraints {
73		e.String(s)
74	}
75
76	e.Int(len(f.imports))
77	for _, m := range f.imports {
78		e.String(m.path)
79		e.Position(m.position)
80	}
81
82	e.Int(len(f.embeds))
83	for _, embed := range f.embeds {
84		e.String(embed.pattern)
85		e.Position(embed.position)
86	}
87
88	e.Int(len(f.directives))
89	for _, d := range f.directives {
90		e.String(d.Text)
91		e.Position(d.Pos)
92	}
93}
94
95func newEncoder() *encoder {
96	e := &encoder{strings: make(map[string]int)}
97
98	// place the empty string at position 0 in the string table
99	e.stringTable = append(e.stringTable, 0)
100	e.strings[""] = 0
101
102	return e
103}
104
105func (e *encoder) Position(position token.Position) {
106	e.String(position.Filename)
107	e.Int(position.Offset)
108	e.Int(position.Line)
109	e.Int(position.Column)
110}
111
112type encoder struct {
113	b           []byte
114	stringTable []byte
115	strings     map[string]int
116}
117
118func (e *encoder) Pos() int {
119	return len(e.b)
120}
121
122func (e *encoder) Bytes(b []byte) {
123	e.b = append(e.b, b...)
124}
125
126func (e *encoder) String(s string) {
127	if n, ok := e.strings[s]; ok {
128		e.Int(n)
129		return
130	}
131	pos := len(e.stringTable)
132	e.strings[s] = pos
133	e.Int(pos)
134	e.stringTable = binary.AppendUvarint(e.stringTable, uint64(len(s)))
135	e.stringTable = append(e.stringTable, s...)
136}
137
138func (e *encoder) Bool(b bool) {
139	if b {
140		e.Uint32(1)
141	} else {
142		e.Uint32(0)
143	}
144}
145
146func (e *encoder) Uint32(n uint32) {
147	e.b = binary.LittleEndian.AppendUint32(e.b, n)
148}
149
150// Int encodes n. Note that all ints are written to the index as uint32s,
151// and to avoid problems on 32-bit systems we require fitting into a 32-bit int.
152func (e *encoder) Int(n int) {
153	if n < 0 || int(int32(n)) != n {
154		base.Fatalf("go: attempting to write an int to the index that overflows int32")
155	}
156	e.Uint32(uint32(n))
157}
158
159func (e *encoder) IntAt(n int, at int) {
160	if n < 0 || int(int32(n)) != n {
161		base.Fatalf("go: attempting to write an int to the index that overflows int32")
162	}
163	binary.LittleEndian.PutUint32(e.b[at:], uint32(n))
164}
165