v0.1.0 602baa7

requests.go RawHistoryFolder

1package main
2
3import (
4	"fmt"
5	"path/filepath"
6	"strconv"
7	"strings"
8)
9
10type Request struct {
11	Name           string
12	Opcode         uint8
13	DataByte       *Field
14	Fixed          int
15	Params, Fields []Field
16	Node           *Node
17	Derived        map[string]string
18	Lists          []Field
19	Values         *ValueList
20}
21
22// resolveRequests lowers scalar, list and mask-bitcase value-list requests.
23func resolveRequests(m *Module) ([]Request, error) {
24	var out []Request
25	for _, n := range m.Root.Children {
26		if n.Kind != "request" {
27			continue
28		}
29		name := goName(n.Attr["name"])
30		if err := m.claim(name, n); err != nil {
31			return nil, err
32		}
33		opcode, _ := strconv.Atoi(n.Attr["opcode"])
34		r := Request{Name: name, Opcode: uint8(opcode), Node: n, Derived: map[string]string{}}
35		var err error
36		r.Values, err = resolveValues(m, n, name)
37		if err != nil {
38			return nil, err
39		}
40		refs := map[string]int{}
41		var countRefs func(*Node)
42		countRefs = func(x *Node) {
43			if x.Kind == "reply" || x.Kind == "doc" {
44				return
45			}
46			if x.Kind == "fieldref" {
47				refs[x.Text]++
48			}
49			for _, c := range x.Children {
50				countRefs(c)
51			}
52		}
53		countRefs(n)
54		for _, x := range n.Children {
55			if x.Kind == "list" && len(x.Children) == 1 && x.Children[0].Kind == "fieldref" && refs[x.Children[0].Text] == 1 {
56				r.Derived[x.Children[0].Text] = paramName(x.Attr["name"])
57			}
58		}
59		dynamic := false
60		off := 4
61		first := true
62		for _, n := range n.Children {
63			if n.Kind == "reply" || n.Kind == "doc" {
64				continue
65			}
66			if n.Kind == "switch" {
67				r.Params = append(r.Params, Field{Param: "v", GoType: "*" + name + "Values"})
68				continue
69			}
70			f := Field{Name: goName(n.Attr["name"]), Param: paramName(n.Attr["name"]), Kind: n.Kind, Node: n, Off: off - 4}
71			if n.Kind == "pad" {
72				if n.Attr["align"] != "" {
73					a, _ := strconv.Atoi(n.Attr["align"])
74					f.Size = (-off) & (a - 1)
75				} else {
76					f.Size, _ = strconv.Atoi(n.Attr["bytes"])
77				}
78			} else {
79				typ, err := lookup(m, n.Attr["type"])
80				if err != nil {
81					return nil, n.fail("%v", err)
82				}
83				if typ.Variable && n.Kind != "list" {
84					return nil, n.fail("variable field requires list support")
85				}
86				f.Type = typ
87				f.Size = typ.Size
88				f.GoType = qualified(m, typ.Module, typ.GoType)
89				if enum := n.Attr["enum"]; enum != "" {
90					e, owner := findEnum(m, enum)
91					if e == nil {
92						return nil, n.fail("unknown enum %s", enum)
93					}
94					f.GoType = qualified(m, owner, e.Name)
95				}
96				if mask := n.Attr["mask"]; mask != "" {
97					e, owner := findEnum(m, mask)
98					if e == nil {
99						return nil, n.fail("unknown mask %s", mask)
100					}
101					f.GoType = qualified(m, owner, e.Name)
102				}
103				if n.Kind == "list" {
104					f.Size = 0
105					f.GoType = "[]" + f.GoType
106					if n.Attr["type"] == "char" {
107						f.GoType = "string"
108					}
109					if n.Attr["type"] == "void" || n.Attr["type"] == "BYTE" || n.Attr["type"] == "CARD8" {
110						f.GoType = "[]byte"
111					}
112					if len(n.Children) > 0 {
113						f.Expr = n.Children[0]
114					}
115					if m.Header == "xproto" && r.Name == "SendEvent" {
116						f.GoType = "*[32]byte"
117					}
118					r.Lists = append(r.Lists, f)
119					r.Params = append(r.Params, f)
120				} else if n.Kind == "field" && r.Derived[n.Attr["name"]] == "" {
121					if r.Values != nil && n == r.Values.Mask.Node {
122						f.Param = "v.Mask"
123					} else {
124						r.Params = append(r.Params, f)
125					}
126				}
127				if n.Kind == "exprfield" {
128					f.Expr = n.Children[0]
129				}
130			}
131			// Core's first XML byte is header byte 1; extensions have no data byte.
132			if first && m.ExtName == "" {
133				if f.Size != 1 {
134					return nil, n.fail("core request first member must occupy header data byte")
135				}
136				if f.Kind != "pad" {
137					copy := f
138					r.DataByte = &copy
139				}
140				first = false
141				continue
142			}
143			first = false
144			if f.Kind == "list" {
145				dynamic = true
146			}
147			if dynamic {
148				f.Off = -1
149			}
150			if f.Kind == "pad" && n.Attr["align"] != "" {
151				f.Align, _ = strconv.Atoi(n.Attr["align"])
152			}
153			r.Fields = append(r.Fields, f)
154			off += f.Size
155		}
156		r.Fixed = off - 4
157		out = append(out, r)
158	}
159	return out, nil
160}
161func requestDecl(m *Module, r Request) (string, error) {
162	var b strings.Builder
163	if r.Values != nil {
164		b.WriteString(valuesDecl(r.Name, r.Values))
165	}
166	fmt.Fprintf(&b, "func %s(b *xturbo.Buf", r.Name)
167	for _, p := range r.Params {
168		fmt.Fprintf(&b, ", %s %s", p.Param, p.GoType)
169	}
170	b.WriteString(") xturbo.Seq {\n")
171	ref := func(s string) string {
172		// xcb-proto permits an exprfield to refer to a trailing list's
173		// implicit <list>_len (QueryTextExtents.string_len).
174		declared := false
175		for _, n := range r.Node.Children {
176			if n.Kind == "field" && n.Attr["name"] == s {
177				declared = true
178			}
179		}
180		for _, list := range r.Lists {
181			if !declared && s == list.Node.Attr["name"]+"_len" {
182				return "len(" + list.Param + ")"
183			}
184		}
185		if list := r.Derived[s]; list != "" {
186			return "len(" + list + ")"
187		}
188		return "int(" + paramName(s) + ")"
189	}
190	// Derived lengths are cast only after checking their wire width.
191	for _, f := range append(append([]Field{}, r.Fields...), r.Params...) {
192		if f.Kind != "field" || r.Derived[f.Node.Attr["name"]] == "" {
193			continue
194		}
195		if f.Type.Size < 8 {
196			fmt.Fprintf(&b, "if uint64(%s)>%d { b.Fail(xturbo.ErrBadLength); return 0 };", ref(f.Node.Attr["name"]), uint64(1)<<(8*f.Type.Size)-1)
197		}
198	}
199	if r.DataByte != nil {
200		f := r.DataByte
201		if list := r.Derived[f.Node.Attr["name"]]; list != "" {
202			fmt.Fprintf(&b, "if len(%s)>255 { b.Fail(xturbo.ErrBadLength); return 0 };", list)
203		}
204	}
205	for _, f := range r.Lists {
206		if f.Expr != nil && !(f.Expr.Kind == "fieldref" && r.Derived[f.Expr.Text] != "") {
207			ex, err := renderExpr(f.Expr, ref, m)
208			if err != nil {
209				return "", err
210			}
211			fmt.Fprintf(&b, "if len(%s)!=%s { b.Fail(xturbo.ErrBadLength); return 0 };", f.Param, ex)
212		}
213	}
214	body := fmt.Sprint((r.Fixed + 3) &^ 3)
215	if len(r.Lists) > 0 {
216		b.WriteString("size:=0;")
217		for _, f := range r.Fields {
218			if f.Kind == "list" {
219				if f.Type.Variable {
220					fmt.Fprintf(&b, "for i:=range %s { size+=%s[i].Size() };", f.Param, f.Param)
221				} else {
222					fmt.Fprintf(&b, "size+=len(%s)*%d;", f.Param, f.Type.Size)
223				}
224			} else if f.Align > 0 {
225				fmt.Fprintf(&b, "size=(size+%d)&^%d;", f.Align-1, f.Align-1)
226			} else {
227				fmt.Fprintf(&b, "size+=%d;", f.Size)
228			}
229		}
230		b.WriteString("size=(size+3)&^3;")
231		body = "size"
232	}
233	if r.Values != nil {
234		body += "+4*bits.OnesCount32(uint32(v.Mask))"
235	}
236	major := fmt.Sprint(r.Opcode)
237	data := "0"
238	if m.ExtName != "" {
239		fmt.Fprintf(&b, "if b.Ext==nil { b.Fail(xturbo.ErrExtMissing); return 0 }; major:=b.Ext.Major(xturbo.Ext%s); if major==0 { b.Fail(xturbo.ErrExtMissing); return 0 };\n", m.ExtName)
240		major = "major"
241		data = fmt.Sprint(r.Opcode)
242	} else if r.DataByte != nil {
243		f := r.DataByte
244		if f.Type.Base == "bool" {
245			value := f.Param
246			if f.Kind == "exprfield" {
247				ex, err := renderExpr(f.Expr, ref, m)
248				if err != nil {
249					return "", err
250				}
251				value = "(" + ex + " != 0)"
252			}
253			fmt.Fprintf(&b, "var data uint8; if %s { data=1 };", value)
254			data = "data"
255		} else {
256			data = "uint8(" + ref(f.Node.Attr["name"]) + ")"
257		}
258	}
259	fmt.Fprintf(&b, "p,seq:=b.Request(%s,%s,%s); if p==nil { return 0 };\n", major, data, body)
260	if len(r.Lists) > 0 {
261		b.WriteString("off:=0;")
262	}
263	for _, f := range r.Fields {
264		if f.Kind == "pad" {
265			if len(r.Lists) > 0 {
266				if f.Align > 0 {
267					fmt.Fprintf(&b, "off=(off+%d)&^%d;", f.Align-1, f.Align-1)
268				} else {
269					fmt.Fprintf(&b, "off+=%d;", f.Size)
270				}
271			}
272			continue
273		}
274		if f.Kind == "list" {
275			if f.GoType == "*[32]byte" {
276				fmt.Fprintf(&b, "copy(p[off:],%s[:]);", f.Param)
277			} else if f.GoType == "string" || f.GoType == "[]byte" {
278				fmt.Fprintf(&b, "copy(p[off:],%s); off+=len(%s);", f.Param, f.Param)
279			} else {
280				fmt.Fprintf(&b, "for i:=range %s {", f.Param)
281				if isScalar(f.Type) {
282					b.WriteString(putScalar(f.Type, f.Param+"[i]", "p[off:]"))
283				} else {
284					fmt.Fprintf(&b, "%s[i].Put(p[off:]);", f.Param)
285				}
286				if f.Type.Variable {
287					fmt.Fprintf(&b, "off+=%s[i].Size();", f.Param)
288				} else {
289					fmt.Fprintf(&b, "off+=%d;", f.Type.Size)
290				}
291				b.WriteString("};")
292			}
293			continue
294		}
295		src := f.Param
296		if list := r.Derived[f.Node.Attr["name"]]; list != "" {
297			src = "len(" + list + ")"
298		}
299		if f.Kind == "exprfield" {
300			var err error
301			src, err = renderExpr(f.Expr, ref, m)
302			if err != nil {
303				return "", err
304			}
305		}
306		dst := fmt.Sprintf("p[%d:]", f.Off)
307		if len(r.Lists) > 0 {
308			dst = "p[off:]"
309		}
310		if isScalar(f.Type) {
311			b.WriteString(putScalar(f.Type, src, dst))
312		} else {
313			fmt.Fprintf(&b, "%s.Put(%s);", src, dst)
314		}
315		if len(r.Lists) > 0 {
316			fmt.Fprintf(&b, "off+=%d;", f.Size)
317		}
318	}
319	if r.Values != nil {
320		fmt.Fprintf(&b, "off:=%d;", r.Fixed)
321		for _, f := range r.Values.Fields {
322			fmt.Fprintf(&b, "if v.Mask & %s != 0 {", f.Bit)
323			b.WriteString(putScalar(f.Field.Type, "v."+f.Field.Name, "p[off:]"))
324			b.WriteString("off+=4 };")
325		}
326		b.WriteString("_ = off;")
327	}
328	b.WriteString("return seq }\n")
329	return b.String(), nil
330}
331func emitRequests(modules []*Module) error {
332	for _, m := range modules {
333		requests, err := resolveRequests(m)
334		if err != nil {
335			return err
336		}
337		data := struct {
338			Pkg   string
339			Decls []string
340		}{Pkg: m.Pkg}
341		for _, r := range requests {
342			decl, err := requestDecl(m, r)
343			if err != nil {
344				return err
345			}
346			data.Decls = append(data.Decls, decl)
347		}
348		all := strings.Join(data.Decls, "\n")
349		imp := importsFor(m, all)
350		if len(requests) > 0 {
351			imp += "\nimport \"bnema.dev/xturbo\""
352		}
353		data.Decls = append([]string{imp}, data.Decls...)
354		if err := writeTemplate(filepath.Join(m.Pkg, "requests_generated.go"), "requests", data); err != nil {
355			return err
356		}
357	}
358	return nil
359}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 1

↑↓ moveenter openesc close