fork of https://github.com/sourcegraph/zoekt
1package zoekt
2
3import (
4 "bytes"
5 "encoding/binary"
6 "fmt"
7 "unsafe"
8)
9
10// Wire-format of map[uint32]MinimalRepoListEntry is pretty straightforward:
11//
12// byte(1) version
13// uvarint(len(minimal))
14// uvarint(sum(len(entry.Branches) for entry in minimal))
15// for repoID, entry in minimal:
16// uvarint(repoID)
17// byte(entry.HasSymbols)
18// uvarint(len(entry.Branches))
19// for b in entry.Branches:
20// str(b.Name)
21// str(b.Version)
22
23// reposMapEncode implements an efficient encoder for ReposMap.
24func reposMapEncode(minimal ReposMap) ([]byte, error) {
25 if minimal == nil {
26 return nil, nil
27 }
28
29 var b bytes.Buffer
30 var enc [binary.MaxVarintLen64]byte
31 varint := func(n int) {
32 m := binary.PutUvarint(enc[:], uint64(n))
33 b.Write(enc[:m])
34 }
35 str := func(s string) {
36 varint(len(s))
37 b.WriteString(s)
38 }
39 strSize := func(s string) int {
40 return binary.PutUvarint(enc[:], uint64(len(s))) + len(s)
41 }
42
43 // We calculate this up front so when decoding we only need to allocate the
44 // underlying array once.
45 allBranchesLen := 0
46 for _, entry := range minimal {
47 allBranchesLen += len(entry.Branches)
48 }
49
50 // Calculate size
51 size := 1 // version
52 size += binary.PutUvarint(enc[:], uint64(len(minimal)))
53 size += binary.PutUvarint(enc[:], uint64(allBranchesLen))
54 for repoID, entry := range minimal {
55 size += binary.PutUvarint(enc[:], uint64(repoID))
56 size += 1 // HasSymbols
57 size += binary.PutUvarint(enc[:], uint64(len(entry.Branches)))
58 for _, b := range entry.Branches {
59 size += strSize(b.Name)
60 size += strSize(b.Version)
61 }
62 }
63 b.Grow(size)
64
65 // Version
66 b.WriteByte(1)
67
68 // Length
69 varint(len(minimal))
70
71 varint(allBranchesLen)
72
73 for repoID, entry := range minimal {
74 varint(int(repoID))
75
76 hasSymbols := byte(1)
77 if !entry.HasSymbols {
78 hasSymbols = 0
79 }
80 b.WriteByte(hasSymbols)
81
82 varint(len(entry.Branches))
83 for _, b := range entry.Branches {
84 str(b.Name)
85 str(b.Version)
86 }
87 }
88
89 return b.Bytes(), nil
90}
91
92// reposMapDecode implements an efficient decoder for map[string]struct{}.
93func reposMapDecode(b []byte) (ReposMap, error) {
94 // nil input
95 if len(b) == 0 {
96 return nil, nil
97 }
98
99 // binaryReader returns strings pointing into b to avoid allocations. We
100 // don't own b, so we create a copy of it.
101 r := binaryReader{
102 typ: "ReposMap",
103 b: append([]byte{}, b...),
104 }
105
106 // Version
107 if v := r.byt(); v != 1 {
108 return nil, fmt.Errorf("unsupported stringSet encoding version %d", v)
109 }
110
111 // Length
112 l := r.uvarint()
113 m := make(map[uint32]MinimalRepoListEntry, l)
114
115 // Pre-allocate slice for all branches
116 allBranchesLen := r.uvarint()
117 allBranches := make([]RepositoryBranch, 0, allBranchesLen)
118
119 for i := 0; i < l; i++ {
120 repoID := r.uvarint()
121 hasSymbols := r.byt() == 1
122 lb := r.uvarint()
123 for i := 0; i < lb; i++ {
124 allBranches = append(allBranches, RepositoryBranch{
125 Name: r.str(),
126 Version: r.str(),
127 })
128 }
129 branches := allBranches[len(allBranches)-lb:]
130 m[uint32(repoID)] = MinimalRepoListEntry{
131 HasSymbols: hasSymbols,
132 Branches: branches,
133 }
134 }
135
136 return m, r.err
137}
138
139type binaryReader struct {
140 typ string
141 b []byte
142 err error
143}
144
145func (b *binaryReader) uvarint() int {
146 x, n := binary.Uvarint(b.b)
147 if n < 0 {
148 b.b = nil
149 b.err = fmt.Errorf("malformed %s", b.typ)
150 return 0
151 }
152 b.b = b.b[n:]
153 return int(x)
154}
155
156func (b *binaryReader) str() string {
157 l := b.uvarint()
158 if l > len(b.b) {
159 b.b = nil
160 b.err = fmt.Errorf("malformed %s", b.typ)
161 return ""
162 }
163 s := b2s(b.b[:l])
164 b.b = b.b[l:]
165 return s
166}
167
168func (b *binaryReader) byt() byte {
169 if len(b.b) < 1 {
170 b.b = nil
171 b.err = fmt.Errorf("malformed %s", b.typ)
172 return 0
173 }
174 x := b.b[0]
175 b.b = b.b[1:]
176 return x
177}
178
179func b2s(b []byte) string {
180 return *(*string)(unsafe.Pointer(&b))
181}