detached d08e097

feat(xtgen): decode replies as zero-copy views with list iterators

Generate <Req>Reply value views and Parse<Req>Reply for every reply in all
nine packages: fixed-offset getters from reply byte 0, []byte views for byte
lists, <Elem>List views for scalar and fixed-struct lists, and <Struct>View
plus <Struct>Iter for variable-size structs. Parse validates the full length
once; list getters recompute running offsets from fixed fields.

Capture eleven reply goldens from Xvfb through the socketpair tee, emit a
FuzzParse<Req>Reply per reply seeded from the goldens, and assert zero
allocations for parse, getters and list iteration.
commit
d08e09740c9dfda20d9091dd6605af9de66a9a00
author
brice · · 2026-10-22 07:09 UTC
parent
017e663
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
checks
4/4
36 files · +5795 -12
A bigreq/replies_fuzz_test.go +21 -0
@@ -0,0 +1,21 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package bigreq
3+
4+import "testing"
5+
6+func FuzzParseEnableReply(f *testing.F) {
7+ f.Add(make([]byte, 32))
8+ f.Fuzz(func(t *testing.T, raw []byte) {
9+ r, ok := ParseEnableReply(raw)
10+ if !ok {
11+ if len(raw) >= 32 {
12+ t.Fatal("rejected a complete fixed-size reply")
13+ }
14+ return
15+ }
16+ if len(r.Raw()) != len(raw) {
17+ t.Fatal("Raw must return the parsed bytes")
18+ }
19+ _ = r.MaximumRequestLength()
20+ })
21+}
A bigreq/replies_generated.go +21 -0
@@ -0,0 +1,21 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package bigreq
3+
4+import "encoding/binary"
5+
6+// EnableReply is a zero-copy view over a Enable reply; offsets count from reply byte 0.
7+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
8+type EnableReply struct{ raw []byte }
9+
10+// ParseEnableReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
11+func ParseEnableReply(raw []byte) (EnableReply, bool) {
12+ if len(raw) < 32 {
13+ return EnableReply{}, false
14+ }
15+ r := EnableReply{raw: raw}
16+ return r, true
17+}
18+func (r EnableReply) Raw() []byte { return r.raw }
19+func (r EnableReply) MaximumRequestLength() uint32 {
20+ return uint32(binary.LittleEndian.Uint32(r.raw[8:]))
21+}
M cmd/xtgen/main.go +3 -0
@@ -32,6 +32,9 @@ func main() {
32 if err == nil {32 if err == nil {
33 err = emitRequests(modules)33 err = emitRequests(modules)
34 }34 }
35+ if err == nil {
36+ err = emitReplies(modules)
37+ }
35 if err != nil {38 if err != nil {
36 fmt.Fprintln(os.Stderr, err)39 fmt.Fprintln(os.Stderr, err)
37 os.Exit(1)40 os.Exit(1)
A cmd/xtgen/replies.go +554 -0
@@ -0,0 +1,554 @@
1+package main
2+
3+import (
4+ "fmt"
5+ "path/filepath"
6+ "sort"
7+ "strconv"
8+ "strings"
9+)
10+
11+// member is one wire member of a decoded container (reply or variable struct
12+// view). Static is the offset from the container's byte 0, or -1 when it
13+// follows a variable-length list and must be recomputed from earlier members.
14+type member struct {
15+ XName, Method, Kind, GoType string
16+ Elem string // annotated element Go type for list views
17+ Type *Type
18+ Size, Align, Static int
19+ Count *Node
20+ ListKind string // bytes, scalar, struct, iter
21+}
22+
23+// container is the lowered layout shared by replies and variable struct views.
24+type container struct {
25+ Name, Recv string
26+ Members []member
27+ Fixed int // minimum byte length (static prefix)
28+ Dynamic int // index of the first variable list, -1 when none
29+ Reply bool
30+ Module *Module
31+}
32+
33+const replyLifetime = "It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep."
34+
35+func isByteElem(xml string) bool {
36+ return xml == "void" || xml == "BYTE" || xml == "CARD8" || xml == "char"
37+}
38+
39+func annotatedType(m *Module, n *Node, typ *Type) (string, error) {
40+ goType := qualified(m, typ.Module, typ.GoType)
41+ for _, attr := range []string{"enum", "mask"} {
42+ if name := n.Attr[attr]; name != "" {
43+ e, owner := findEnum(m, name)
44+ if e == nil {
45+ return "", n.fail("unknown %s %s", attr, name)
46+ }
47+ goType = qualified(m, owner, e.Name)
48+ }
49+ }
50+ return goType, nil
51+}
52+
53+// elemName names the per-package list view for an element type.
54+func elemName(goType string) string {
55+ if i := strings.LastIndex(goType, "."); i >= 0 {
56+ goType = goType[i+1:]
57+ }
58+ return strings.ToUpper(goType[:1]) + goType[1:]
59+}
60+
61+// buildContainer lowers XML members. Replies start at byte 1 (the first XML
62+// member occupies the header data byte) and continue at byte 8.
63+func buildContainer(m *Module, name, recv string, nodes []*Node, reply bool, reserved string) (*container, error) {
64+ c := &container{Name: name, Recv: recv, Reply: reply, Dynamic: -1, Module: m}
65+ off := 0
66+ if reply {
67+ off = 1
68+ }
69+ first := true
70+ methods := map[string]bool{}
71+ for _, n := range nodes {
72+ if n.Kind == "doc" {
73+ continue
74+ }
75+ mem := member{XName: n.Attr["name"], Kind: n.Kind, Static: off}
76+ if c.Dynamic >= 0 {
77+ mem.Static = -1
78+ }
79+ switch n.Kind {
80+ case "pad":
81+ if a := n.Attr["align"]; a != "" {
82+ mem.Align, _ = strconv.Atoi(a)
83+ if c.Dynamic < 0 {
84+ mem.Size = (-off) & (mem.Align - 1)
85+ }
86+ } else {
87+ mem.Size, _ = strconv.Atoi(n.Attr["bytes"])
88+ }
89+ case "field", "list":
90+ typ, err := lookup(m, n.Attr["type"])
91+ if err != nil {
92+ return nil, n.fail("%v", err)
93+ }
94+ mem.Type = typ
95+ mem.Method = goName(mem.XName)
96+ if contains(reserved, mem.Method) {
97+ mem.Method += "_"
98+ }
99+ if methods[mem.Method] {
100+ return nil, n.fail("duplicate getter %s", mem.Method)
101+ }
102+ methods[mem.Method] = true
103+ elem, err := annotatedType(m, n, typ)
104+ if err != nil {
105+ return nil, err
106+ }
107+ mem.Elem = elem
108+ if n.Kind == "field" {
109+ if typ.Variable {
110+ return nil, n.fail("variable-size field outside a list is unsupported")
111+ }
112+ mem.GoType = elem
113+ mem.Size = typ.Size
114+ break
115+ }
116+ if len(n.Children) != 1 {
117+ return nil, n.fail("reply-side list requires a length expression")
118+ }
119+ mem.Count = n.Children[0]
120+ switch {
121+ case isByteElem(n.Attr["type"]):
122+ mem.ListKind, mem.GoType = "bytes", "[]byte"
123+ case typ.Variable:
124+ if typ.Size == 0 {
125+ return nil, n.fail("variable struct %s has an empty fixed prefix", typ.Name)
126+ }
127+ mem.ListKind, mem.GoType = "iter", qualified(m, typ.Module, typ.GoType+"Iter")
128+ case isScalar(typ):
129+ mem.ListKind, mem.GoType = "scalar", elemName(elem)+"List"
130+ default:
131+ mem.ListKind, mem.GoType = "struct", elemName(elem)+"List"
132+ }
133+ constant := mem.Count.Kind == "value" && !typ.Variable
134+ if constant {
135+ count, _ := strconv.Atoi(mem.Count.Text)
136+ mem.Size = count * typ.Size
137+ } else if c.Dynamic < 0 {
138+ c.Dynamic = len(c.Members)
139+ }
140+ default:
141+ return nil, n.fail("unsupported %s member %s", map[bool]string{true: "reply", false: "struct"}[reply], n.Kind)
142+ }
143+ if reply && first {
144+ if mem.Size != 1 {
145+ return nil, n.fail("first reply member must occupy the header data byte")
146+ }
147+ off = 8
148+ first = false
149+ c.Members = append(c.Members, mem)
150+ continue
151+ }
152+ first = false
153+ if c.Dynamic < 0 {
154+ off += mem.Size
155+ }
156+ c.Members = append(c.Members, mem)
157+ }
158+ if c.Dynamic >= 0 {
159+ c.Fixed = c.Members[c.Dynamic].Static
160+ } else {
161+ c.Fixed = off
162+ }
163+ return c, nil
164+}
165+
166+// ref renders a field reference as a getter call on the receiver. Replies may
167+// refer to the implicit header length word.
168+func (c *container) ref(s string) string {
169+ for _, mem := range c.Members {
170+ if mem.Kind == "field" && mem.XName == s {
171+ return "int(" + c.Recv + "." + mem.Method + "())"
172+ }
173+ }
174+ if c.Reply && s == "length" {
175+ return "int(binary.LittleEndian.Uint32(" + c.Recv + ".raw[4:]))"
176+ }
177+ return "<unresolved " + s + ">"
178+}
179+
180+func (c *container) count(mem member) (string, error) {
181+ s, err := renderExpr(mem.Count, c.ref, c.Module)
182+ if err != nil {
183+ return "", err
184+ }
185+ if strings.Contains(s, "<unresolved") {
186+ return "", mem.Count.fail("list length references an unknown field")
187+ }
188+ return s, nil
189+}
190+
191+// advance emits code moving off past member i. With fail != "" every step is
192+// bounds-checked against raw.
193+func (c *container) advance(b *strings.Builder, i int, fail string) error {
194+ mem := c.Members[i]
195+ raw := c.Recv + ".raw"
196+ switch mem.Kind {
197+ case "pad":
198+ if mem.Align > 0 {
199+ fmt.Fprintf(b, "off=(off+%d)&^%d;", mem.Align-1, mem.Align-1)
200+ } else {
201+ fmt.Fprintf(b, "off+=%d;", mem.Size)
202+ }
203+ case "field":
204+ fmt.Fprintf(b, "off+=%d;", mem.Size)
205+ case "list":
206+ n, err := c.count(mem)
207+ if err != nil {
208+ return err
209+ }
210+ if mem.ListKind == "iter" {
211+ fmt.Fprintf(b, "{ n:=%s;", n)
212+ if fail != "" {
213+ fmt.Fprintf(b, "if off>len(%s) || n<0 || n>len(%s) { %s };", raw, raw, fail)
214+ }
215+ fmt.Fprintf(b, "it:=%s(%s[off:],n); for it.Next() { off+=it.Value().Size() };", qualified(c.Module, mem.Type.Module, "New"+mem.Type.GoType+"Iter"), raw)
216+ if fail != "" {
217+ fmt.Fprintf(b, "if it.Err() { %s };", fail)
218+ }
219+ b.WriteString("};")
220+ return nil
221+ }
222+ fmt.Fprintf(b, "{ n:=%s;", n)
223+ if fail != "" {
224+ fmt.Fprintf(b, "if n<0 || n>len(%s) { %s };", raw, fail)
225+ }
226+ fmt.Fprintf(b, "off+=n*%d };", mem.Type.Size)
227+ if fail != "" {
228+ fmt.Fprintf(b, "if off>len(%s) { %s };", raw, fail)
229+ }
230+ }
231+ return nil
232+}
233+
234+// offset emits statements leaving member i's offset in off.
235+func (c *container) offset(b *strings.Builder, i int) error {
236+ if c.Members[i].Static >= 0 {
237+ fmt.Fprintf(b, "off:=%d;", c.Members[i].Static)
238+ return nil
239+ }
240+ fmt.Fprintf(b, "off:=%d;", c.Fixed)
241+ for j := c.Dynamic; j < i; j++ {
242+ if err := c.advance(b, j, ""); err != nil {
243+ return err
244+ }
245+ }
246+ return nil
247+}
248+
249+// validate emits the full-length walk used by Parse and Iter.Next.
250+func (c *container) validate(b *strings.Builder, fail string) error {
251+ if c.Dynamic < 0 {
252+ return nil
253+ }
254+ fmt.Fprintf(b, "off:=%d;", c.Fixed)
255+ for j := c.Dynamic; j < len(c.Members); j++ {
256+ if err := c.advance(b, j, fail); err != nil {
257+ return err
258+ }
259+ }
260+ fmt.Fprintf(b, "if off>len(%s.raw) { %s };", c.Recv, fail)
261+ return nil
262+}
263+
264+// getters emits one method per field and list.
265+func (c *container) getters(b *strings.Builder, typ string, lists map[string]string) error {
266+ r := c.Recv
267+ for i, mem := range c.Members {
268+ if mem.Kind == "pad" {
269+ continue
270+ }
271+ fmt.Fprintf(b, "func (%s %s) %s() %s {", r, typ, mem.Method, mem.GoType)
272+ static := mem.Static >= 0
273+ at := fmt.Sprintf("%s.raw[%d:]", r, mem.Static)
274+ if !static {
275+ if err := c.offset(b, i); err != nil {
276+ return err
277+ }
278+ at = r + ".raw[off:]"
279+ }
280+ if mem.Kind == "field" {
281+ if isScalar(mem.Type) {
282+ fmt.Fprintf(b, "return %s };\n", getScalar(mem.Type, mem.GoType, at))
283+ } else {
284+ fmt.Fprintf(b, "return %s(%s) };\n", qualified(c.Module, mem.Type.Module, "Get"+mem.Type.GoType), at)
285+ }
286+ continue
287+ }
288+ if static {
289+ fmt.Fprintf(b, "off:=%d;", mem.Static)
290+ }
291+ n, err := c.count(mem)
292+ if err != nil {
293+ return err
294+ }
295+ switch mem.ListKind {
296+ case "iter":
297+ fmt.Fprintf(b, "return %s(%s.raw[off:],%s) };\n", qualified(c.Module, mem.Type.Module, "New"+mem.Type.GoType+"Iter"), r, n)
298+ case "bytes":
299+ fmt.Fprintf(b, "end:=off+%s; return %s.raw[off:end:end] };\n", n, r)
300+ default:
301+ fmt.Fprintf(b, "end:=off+(%s)*%d; return %s{b:%s.raw[off:end:end]} };\n", n, mem.Type.Size, mem.GoType, r)
302+ lists[mem.GoType] = listDecl(c.Module, mem.GoType, mem.Elem, mem.Type)
303+ }
304+ }
305+ return nil
306+}
307+
308+func listDecl(m *Module, name, goType string, elem *Type) string {
309+ var b strings.Builder
310+ fmt.Fprintf(&b, "// %s is a borrowed view over a wire list of %s. %s\n", name, goType, "It shares the lifetime of the bytes it was taken from; copy what you keep.")
311+ fmt.Fprintf(&b, "type %s struct { b []byte };\n", name)
312+ fmt.Fprintf(&b, "func (l %s) Len() int { return len(l.b)/%d };\n", name, elem.Size)
313+ src := fmt.Sprintf("l.b[i*%d:]", elem.Size)
314+ if isScalar(elem) {
315+ fmt.Fprintf(&b, "func (l %s) At(i int) %s { return %s };\n", name, goType, getScalar(elem, goType, src))
316+ } else {
317+ fmt.Fprintf(&b, "func (l %s) At(i int) %s { return %s(%s) };\n", name, goType, qualified(m, elem.Module, "Get"+elem.GoType), src)
318+ }
319+ return b.String()
320+}
321+
322+type replyIR struct {
323+ Req string
324+ C *container
325+}
326+
327+func resolveReplies(m *Module) ([]replyIR, []*container, error) {
328+ var replies []replyIR
329+ for _, n := range m.Root.Children {
330+ if n.Kind != "request" {
331+ continue
332+ }
333+ for _, r := range n.Children {
334+ if r.Kind != "reply" {
335+ continue
336+ }
337+ name := goName(n.Attr["name"])
338+ for _, s := range []string{name + "Reply", "Parse" + name + "Reply"} {
339+ if err := m.claim(s, r); err != nil {
340+ return nil, nil, err
341+ }
342+ }
343+ c, err := buildContainer(m, name+"Reply", "r", r.Children, true, "Raw")
344+ if err != nil {
345+ return nil, nil, err
346+ }
347+ replies = append(replies, replyIR{Req: name, C: c})
348+ }
349+ }
350+ var views []*container
351+ for _, t := range m.Types {
352+ if t.Kind != "struct" || !t.Variable {
353+ continue
354+ }
355+ for _, s := range []string{t.GoType + "View", t.GoType + "Iter", "New" + t.GoType + "Iter"} {
356+ if err := m.claim(s, t.Node); err != nil {
357+ return nil, nil, err
358+ }
359+ }
360+ c, err := buildContainer(m, t.GoType+"View", "v", t.Node.Children, false, "Raw Size")
361+ if err != nil {
362+ return nil, nil, err
363+ }
364+ views = append(views, c)
365+ }
366+ return replies, views, nil
367+}
368+
369+func replyDecl(r replyIR, lists map[string]string) (string, error) {
370+ c := r.C
371+ var b strings.Builder
372+ fmt.Fprintf(&b, "// %s is a zero-copy view over a %s reply; offsets count from reply byte 0.\n// %s\n", c.Name, r.Req, replyLifetime)
373+ fmt.Fprintf(&b, "type %s struct { raw []byte };\n", c.Name)
374+ fmt.Fprintf(&b, "// Parse%s validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.\n", c.Name)
375+ fmt.Fprintf(&b, "func Parse%s(raw []byte) (%s, bool) {", c.Name, c.Name)
376+ fixed := max(c.Fixed, 32)
377+ fmt.Fprintf(&b, "if len(raw)<%d { return %s{}, false };", fixed, c.Name)
378+ fmt.Fprintf(&b, "r:=%s{raw:raw};", c.Name)
379+ if err := c.validate(&b, "return "+c.Name+"{}, false"); err != nil {
380+ return "", err
381+ }
382+ b.WriteString("return r, true };\n")
383+ fmt.Fprintf(&b, "func (r %s) Raw() []byte { return r.raw };\n", c.Name)
384+ if err := c.getters(&b, c.Name, lists); err != nil {
385+ return "", err
386+ }
387+ return b.String(), nil
388+}
389+
390+func viewDecl(c *container, lists map[string]string) (string, error) {
391+ base := strings.TrimSuffix(c.Name, "View")
392+ iter := base + "Iter"
393+ var b strings.Builder
394+ fmt.Fprintf(&b, "// %s is a zero-copy view over one wire-encoded %s, yielded by %s.\n// It shares the lifetime of the bytes it was taken from (for replies: until the Handler callback returns). Copy what you keep.\n", c.Name, base, iter)
395+ fmt.Fprintf(&b, "type %s struct { raw []byte };\n", c.Name)
396+ fmt.Fprintf(&b, "func (v %s) Raw() []byte { return v.raw };\n", c.Name)
397+ fmt.Fprintf(&b, "// Size is the element's wire size, including trailing alignment.\nfunc (v %s) Size() int { return len(v.raw) };\n", c.Name)
398+ // size%s returns the element's wire size at the start of p, -1 when malformed.
399+ fmt.Fprintf(&b, "func size%s(p []byte) int { if len(p)<%d { return -1 }; v:=%s{raw:p}; _=v;", base, c.Fixed, c.Name)
400+ if c.Dynamic < 0 {
401+ fmt.Fprintf(&b, "return %d };\n", c.Fixed)
402+ } else {
403+ if err := c.validate(&b, "return -1"); err != nil {
404+ return "", err
405+ }
406+ b.WriteString("return off };\n")
407+ }
408+ if err := c.getters(&b, c.Name, lists); err != nil {
409+ return "", err
410+ }
411+ fmt.Fprintf(&b, "// %s walks n consecutive %s elements; Err reports a malformed length.\n// It shares the lifetime of the bytes it walks.\n", iter, base)
412+ fmt.Fprintf(&b, "type %s struct { b []byte; n int; v %s; err bool };\n", iter, c.Name)
413+ fmt.Fprintf(&b, "// New%s iterates n elements encoded at the start of b.\nfunc New%s(b []byte, n int) %s { return %s{b:b, n:n} };\n", iter, iter, iter, iter)
414+ fmt.Fprintf(&b, "func (it *%s) Next() bool { if it.n<=0 || it.err { return false }; s:=size%s(it.b); if s<0 { it.err=true; it.n=0; return false }; it.v=%s{raw:it.b[:s:s]}; it.b=it.b[s:]; it.n--; return true };\n", iter, base, c.Name)
415+ fmt.Fprintf(&b, "func (it *%s) Value() %s { return it.v };\n", iter, c.Name)
416+ fmt.Fprintf(&b, "func (it *%s) Err() bool { return it.err };\n", iter)
417+ return b.String(), nil
418+}
419+
420+// touch emits fuzz statements calling every getter and walking every list,
421+// recursing into variable struct views.
422+func touch(b *strings.Builder, c *container, expr string, depth int, all map[*Type]*container, failIter string) {
423+ for _, mem := range c.Members {
424+ switch {
425+ case mem.Kind == "field":
426+ fmt.Fprintf(b, "_ = %s.%s();", expr, mem.Method)
427+ case mem.Kind == "list" && mem.ListKind == "bytes":
428+ fmt.Fprintf(b, "_ = len(%s.%s());", expr, mem.Method)
429+ case mem.Kind == "list" && mem.ListKind == "iter":
430+ it := fmt.Sprintf("it%d", depth)
431+ fmt.Fprintf(b, "{ %s := %s.%s(); for %s.Next() {", it, expr, mem.Method, it)
432+ if child := all[mem.Type]; child != nil {
433+ touch(b, child, it+".Value()", depth+1, all, failIter)
434+ }
435+ fmt.Fprintf(b, "}; if %s.Err() { %s } };", it, failIter)
436+ case mem.Kind == "list":
437+ l := fmt.Sprintf("l%d", depth)
438+ fmt.Fprintf(b, "{ %s := %s.%s(); for i := 0; i < %s.Len(); i++ { _ = %s.At(i) } };", l, expr, mem.Method, l, l)
439+ }
440+ }
441+}
442+
443+func emitReplies(modules []*Module) error {
444+ type pending struct {
445+ m *Module
446+ replies []replyIR
447+ views []*container
448+ }
449+ var all []pending
450+ views := map[*Type]*container{}
451+ for _, m := range modules {
452+ replies, vs, err := resolveReplies(m)
453+ if err != nil {
454+ return err
455+ }
456+ for _, v := range vs {
457+ for _, t := range m.Types {
458+ if t.GoType+"View" == v.Name {
459+ views[t] = v
460+ }
461+ }
462+ }
463+ all = append(all, pending{m, replies, vs})
464+ }
465+ for _, p := range all {
466+ m := p.m
467+ lists := map[string]string{}
468+ var decls []string
469+ for _, r := range p.replies {
470+ d, err := replyDecl(r, lists)
471+ if err != nil {
472+ return err
473+ }
474+ decls = append(decls, d)
475+ }
476+ for _, v := range p.views {
477+ d, err := viewDecl(v, lists)
478+ if err != nil {
479+ return err
480+ }
481+ decls = append(decls, d)
482+ }
483+ var names []string
484+ for name := range lists {
485+ if err := m.claim(name, m.Root); err != nil {
486+ return err
487+ }
488+ names = append(names, name)
489+ }
490+ sort.Strings(names)
491+ for _, name := range names {
492+ decls = append(decls, lists[name])
493+ }
494+ if len(decls) == 0 {
495+ continue
496+ }
497+ decls = append([]string{importsFor(m, strings.Join(decls, "\n"))}, decls...)
498+ data := struct {
499+ Pkg string
500+ Decls []string
501+ }{Pkg: m.Pkg, Decls: decls}
502+ if err := writeTemplate(filepath.Join(m.Pkg, "replies_generated.go"), "replies", data); err != nil {
503+ return err
504+ }
505+ if err := emitReplyFuzz(m, p.replies, views); err != nil {
506+ return err
507+ }
508+ }
509+ return nil
510+}
511+
512+// emitReplyFuzz writes FuzzParse<Req>Reply for every reply. Seeds are the
513+// zero-filled fixed prefix plus any captured goldens <Req>*.reply.hex.
514+func emitReplyFuzz(m *Module, replies []replyIR, views map[*Type]*container) error {
515+ var decls []string
516+ usesGolden := false
517+ for _, r := range replies {
518+ var b strings.Builder
519+ c := r.C
520+ fmt.Fprintf(&b, "func FuzzParse%s(f *testing.F) {\n", c.Name)
521+ fmt.Fprintf(&b, "f.Add(make([]byte, %d));\n", (max(c.Fixed, 32)+3)&^3)
522+ goldens, _ := filepath.Glob(filepath.Join("testdata/golden", m.Pkg, r.Req+"*.reply.hex"))
523+ sort.Strings(goldens)
524+ for _, g := range goldens {
525+ base := filepath.Base(g)
526+ if strings.TrimSuffix(base, ".reply.hex") != r.Req && !startsUpper(strings.TrimPrefix(strings.TrimSuffix(base, ".reply.hex"), r.Req)) {
527+ continue
528+ }
529+ usesGolden = true
530+ fmt.Fprintf(&b, "if raw, err := xtest.LoadHex(%q); err != nil { f.Fatal(err) } else { f.Add(raw) };\n", "../"+filepath.ToSlash(g))
531+ }
532+ fmt.Fprintf(&b, "f.Fuzz(func(t *testing.T, raw []byte) {\nr, ok := Parse%s(raw);\nif !ok {", c.Name)
533+ if c.Dynamic < 0 {
534+ fmt.Fprintf(&b, "if len(raw) >= %d { t.Fatal(\"rejected a complete fixed-size reply\") };", max(c.Fixed, 32))
535+ }
536+ b.WriteString("return };\n")
537+ b.WriteString("if len(r.Raw()) != len(raw) { t.Fatal(\"Raw must return the parsed bytes\") };\n")
538+ touch(&b, c, "r", 0, views, `t.Fatal("iterator error after successful Parse")`)
539+ b.WriteString("\n})\n}\n")
540+ decls = append(decls, b.String())
541+ }
542+ imports := "import \"testing\""
543+ if usesGolden {
544+ imports += "\nimport \"bnema.dev/xturbo/internal/xtest\""
545+ }
546+ decls = append([]string{imports}, decls...)
547+ data := struct {
548+ Pkg string
549+ Decls []string
550+ }{Pkg: m.Pkg, Decls: decls}
551+ return writeTemplate(filepath.Join(m.Pkg, "replies_fuzz_test.go"), "replies", data)
552+}
553+
554+func startsUpper(s string) bool { return s != "" && s[0] >= 'A' && s[0] <= 'Z' }
A cmd/xtgen/replies_test.go +70 -0
@@ -0,0 +1,70 @@
1+package main
2+
3+import "testing"
4+
5+// TestReplyLayouts checks reply containers against the independent packet-size
6+// walker and pins list-view classification for representative replies.
7+func TestReplyLayouts(t *testing.T) {
8+ kinds := map[string]map[string]string{
9+ "xproto.GetPropertyReply": {"Value": "bytes"},
10+ "xproto.QueryTreeReply": {"Children": "scalar"},
11+ "xproto.ListExtensionsReply": {"Names": "iter"},
12+ "xproto.QueryFontReply": {"Properties": "struct", "CharInfos": "struct"},
13+ "xproto.ListHostsReply": {"Hosts": "iter"},
14+ "render.QueryPictFormatsReply": {"Formats": "struct", "Screens": "iter", "Subpixels": "scalar"},
15+ "xfixes.GetCursorImageAndNameReply": {"CursorImage": "scalar", "Name": "bytes"},
16+ }
17+ for _, m := range testModules(t) {
18+ replies, _, err := resolveReplies(m)
19+ if err != nil {
20+ t.Fatal(err)
21+ }
22+ for _, r := range replies {
23+ var node *Node
24+ for _, n := range m.Root.Children {
25+ if n.Kind == "request" && goName(n.Attr["name"]) == r.Req {
26+ for _, c := range n.Children {
27+ if c.Kind == "reply" {
28+ node = c
29+ }
30+ }
31+ }
32+ }
33+ want, err := packetFixed(m, node, true)
34+ if err != nil {
35+ t.Fatal(err)
36+ }
37+ if got := max(r.C.Fixed, 32); got != want {
38+ t.Errorf("%s.%s fixed %d, packet walker %d", m.Pkg, r.C.Name, got, want)
39+ }
40+ for _, mem := range r.C.Members {
41+ if exp, ok := kinds[m.Pkg+"."+r.C.Name]; ok && mem.Kind == "list" {
42+ if exp[mem.Method] != mem.ListKind {
43+ t.Errorf("%s.%s.%s kind %s want %s", m.Pkg, r.C.Name, mem.Method, mem.ListKind, exp[mem.Method])
44+ }
45+ delete(exp, mem.Method)
46+ }
47+ }
48+ }
49+ }
50+ for name, rest := range kinds {
51+ if len(rest) != 0 {
52+ t.Errorf("%s: unchecked lists %v", name, rest)
53+ }
54+ }
55+}
56+
57+func TestReplyReservedGetter(t *testing.T) {
58+ m := &Module{Pkg: "x", Symbols: map[string]*Type{}, Names: map[string]*Node{}}
59+ nodes := []*Node{
60+ {Kind: "pad", Attr: map[string]string{"bytes": "1"}},
61+ {Kind: "field", Attr: map[string]string{"name": "raw", "type": "CARD32"}},
62+ }
63+ c, err := buildContainer(m, "XReply", "r", nodes, true, "Raw")
64+ if err != nil {
65+ t.Fatal(err)
66+ }
67+ if c.Members[1].Method != "Raw_" || c.Members[1].Static != 8 || c.Fixed != 12 {
68+ t.Fatalf("%+v", c.Members[1])
69+ }
70+}
A cmd/xtgen/templates/replies.tmpl +5 -0
@@ -0,0 +1,5 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package {{.Pkg}}
3+{{range .Decls}}
4+{{.}}
5+{{end}}
A composite/replies_fuzz_test.go +39 -0
@@ -0,0 +1,39 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package composite
3+
4+import "testing"
5+
6+func FuzzParseQueryVersionReply(f *testing.F) {
7+ f.Add(make([]byte, 32))
8+ f.Fuzz(func(t *testing.T, raw []byte) {
9+ r, ok := ParseQueryVersionReply(raw)
10+ if !ok {
11+ if len(raw) >= 32 {
12+ t.Fatal("rejected a complete fixed-size reply")
13+ }
14+ return
15+ }
16+ if len(r.Raw()) != len(raw) {
17+ t.Fatal("Raw must return the parsed bytes")
18+ }
19+ _ = r.MajorVersion()
20+ _ = r.MinorVersion()
21+ })
22+}
23+
24+func FuzzParseGetOverlayWindowReply(f *testing.F) {
25+ f.Add(make([]byte, 32))
26+ f.Fuzz(func(t *testing.T, raw []byte) {
27+ r, ok := ParseGetOverlayWindowReply(raw)
28+ if !ok {
29+ if len(raw) >= 32 {
30+ t.Fatal("rejected a complete fixed-size reply")
31+ }
32+ return
33+ }
34+ if len(r.Raw()) != len(raw) {
35+ t.Fatal("Raw must return the parsed bytes")
36+ }
37+ _ = r.OverlayWin()
38+ })
39+}
A composite/replies_generated.go +42 -0
@@ -0,0 +1,42 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package composite
3+
4+import "bnema.dev/xturbo/xproto"
5+import "encoding/binary"
6+
7+// QueryVersionReply is a zero-copy view over a QueryVersion reply; offsets count from reply byte 0.
8+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
9+type QueryVersionReply struct{ raw []byte }
10+
11+// ParseQueryVersionReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
12+func ParseQueryVersionReply(raw []byte) (QueryVersionReply, bool) {
13+ if len(raw) < 32 {
14+ return QueryVersionReply{}, false
15+ }
16+ r := QueryVersionReply{raw: raw}
17+ return r, true
18+}
19+func (r QueryVersionReply) Raw() []byte { return r.raw }
20+func (r QueryVersionReply) MajorVersion() uint32 {
21+ return uint32(binary.LittleEndian.Uint32(r.raw[8:]))
22+}
23+func (r QueryVersionReply) MinorVersion() uint32 {
24+ return uint32(binary.LittleEndian.Uint32(r.raw[12:]))
25+}
26+
27+// GetOverlayWindowReply is a zero-copy view over a GetOverlayWindow reply; offsets count from reply byte 0.
28+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
29+type GetOverlayWindowReply struct{ raw []byte }
30+
31+// ParseGetOverlayWindowReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
32+func ParseGetOverlayWindowReply(raw []byte) (GetOverlayWindowReply, bool) {
33+ if len(raw) < 32 {
34+ return GetOverlayWindowReply{}, false
35+ }
36+ r := GetOverlayWindowReply{raw: raw}
37+ return r, true
38+}
39+func (r GetOverlayWindowReply) Raw() []byte { return r.raw }
40+func (r GetOverlayWindowReply) OverlayWin() xproto.Window {
41+ return xproto.Window(binary.LittleEndian.Uint32(r.raw[8:]))
42+}
A ge/replies_fuzz_test.go +22 -0
@@ -0,0 +1,22 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package ge
3+
4+import "testing"
5+
6+func FuzzParseQueryVersionReply(f *testing.F) {
7+ f.Add(make([]byte, 32))
8+ f.Fuzz(func(t *testing.T, raw []byte) {
9+ r, ok := ParseQueryVersionReply(raw)
10+ if !ok {
11+ if len(raw) >= 32 {
12+ t.Fatal("rejected a complete fixed-size reply")
13+ }
14+ return
15+ }
16+ if len(r.Raw()) != len(raw) {
17+ t.Fatal("Raw must return the parsed bytes")
18+ }
19+ _ = r.MajorVersion()
20+ _ = r.MinorVersion()
21+ })
22+}
A ge/replies_generated.go +24 -0
@@ -0,0 +1,24 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package ge
3+
4+import "encoding/binary"
5+
6+// QueryVersionReply is a zero-copy view over a QueryVersion reply; offsets count from reply byte 0.
7+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
8+type QueryVersionReply struct{ raw []byte }
9+
10+// ParseQueryVersionReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
11+func ParseQueryVersionReply(raw []byte) (QueryVersionReply, bool) {
12+ if len(raw) < 32 {
13+ return QueryVersionReply{}, false
14+ }
15+ r := QueryVersionReply{raw: raw}
16+ return r, true
17+}
18+func (r QueryVersionReply) Raw() []byte { return r.raw }
19+func (r QueryVersionReply) MajorVersion() uint16 {
20+ return uint16(binary.LittleEndian.Uint16(r.raw[8:]))
21+}
22+func (r QueryVersionReply) MinorVersion() uint16 {
23+ return uint16(binary.LittleEndian.Uint16(r.raw[10:]))
24+}
M internal/xtest/README.md +10 -1
@@ -15,7 +15,9 @@ The captured target is the root window; XFIXES's major opcode is negotiated.
15 Tests read target/opcode from the fixture, not a hard-coded server assignment.15 Tests read target/opcode from the fixture, not a hard-coded server assignment.
16 No runtime Conn is needed at this phase.16 No runtime Conn is needed at this phase.
17 17
18-Replies, events and runtime integration belong to their subsequent tasks.18+The same run also writes reply goldens (`<Name>.reply.hex`): the server's
19+response to each reference request, read back through the tee. Events and
20+runtime integration belong to their subsequent tasks.
19 21
20 T2.4 adds InternAtom, ChangeProperty and XRes QueryClientIds. A GetProperty22 T2.4 adds InternAtom, ChangeProperty and XRes QueryClientIds. A GetProperty
21 read-back validates the property contents. XRes's major opcode is negotiated;23 read-back validates the property contents. XRes's major opcode is negotiated;
@@ -29,3 +31,10 @@ values, and fences void requests with a successful reply (any preceding error
29 fails capture). RENDER's opcode and the root visual's picture format are queried31 fails capture). RENDER's opcode and the root visual's picture format are queried
30 from Xvfb. Tests reuse only server-assigned resource/format IDs from fixtures;32 from Xvfb. Tests reuse only server-assigned resource/format IDs from fixtures;
31 mask bits, values and ordering remain independent expected inputs.33 mask bits, values and ordering remain independent expected inputs.
34+
35+T2.6 keeps the replies to QueryExtension, xfixes QueryVersion, GetGeometry,
36+InternAtom, GetProperty, res QueryClientIds and render QueryPictFormats, and
37+adds GetWindowAttributes (child window), QueryTree (root), an unset WM_NAME
38+GetProperty (zero-length value) and ListExtensions (variable STR list). These
39+reply-only requests use opcodes outside the request-golden classifier.
40+Generated `FuzzParse*Reply` targets seed from every matching reply golden.
M internal/xtest/capture_test.go +52 -11
@@ -111,6 +111,17 @@ func TestCaptureRequests(t *testing.T) {
111 // Query XFIXES dynamically rather than assuming a server-specific major opcode.111 // Query XFIXES dynamically rather than assuming a server-specific major opcode.
112 query := []byte{98, 0, 4, 0, 6, 0, 0, 0, 'X', 'F', 'I', 'X', 'E', 'S', 0, 0}112 query := []byte{98, 0, 4, 0, 6, 0, 0, 0, 'X', 'F', 'I', 'X', 'E', 'S', 0, 0}
113 write(query)113 write(query)
114+ // Server bytes reach the client through the same tee; keep named replies as
115+ // reply goldens (reply byte 0 through 32+4*length).
116+ replies := map[string][]byte{}
117+ keep := func(name string, p []byte) []byte {
118+ t.Helper()
119+ if p[0] != 1 {
120+ t.Fatalf("%s: expected a reply, got %x", name, p)
121+ }
122+ replies[name] = p
123+ return p
124+ }
114 packet := func() []byte {125 packet := func() []byte {
115 t.Helper()126 t.Helper()
116 head := make([]byte, 32)127 head := make([]byte, 32)
@@ -122,7 +133,7 @@ func TestCaptureRequests(t *testing.T) {
122 }133 }
123 return head134 return head
124 }135 }
125- ext := packet()136+ ext := keep("xproto/QueryExtension", packet())
126 if ext[0] != 1 || ext[8] != 1 {137 if ext[0] != 1 || ext[8] != 1 {
127 t.Fatalf("XFIXES unavailable: %x", ext)138 t.Fatalf("XFIXES unavailable: %x", ext)
128 }139 }
@@ -132,7 +143,7 @@ func TestCaptureRequests(t *testing.T) {
132 version[2] = 3143 version[2] = 3
133 binary.LittleEndian.PutUint32(version[4:], 6)144 binary.LittleEndian.PutUint32(version[4:], 6)
134 write(version)145 write(version)
135- reply := packet()146+ reply := keep("xfixes/QueryVersion", packet())
136 if reply[0] != 1 {147 if reply[0] != 1 {
137 t.Fatalf("QueryVersion failed: %x", reply)148 t.Fatalf("QueryVersion failed: %x", reply)
138 }149 }
@@ -144,7 +155,7 @@ func TestCaptureRequests(t *testing.T) {
144 geometry := append([]byte(nil), mapReq...)155 geometry := append([]byte(nil), mapReq...)
145 geometry[0] = 14156 geometry[0] = 14
146 write(geometry)157 write(geometry)
147- geom := packet()158+ geom := keep("xproto/GetGeometry", packet())
148 if geom[0] != 1 || binary.LittleEndian.Uint16(geom[2:]) != 4 {159 if geom[0] != 1 || binary.LittleEndian.Uint16(geom[2:]) != 4 {
149 t.Fatalf("MapWindow/GetGeometry rejected: %x", geom)160 t.Fatalf("MapWindow/GetGeometry rejected: %x", geom)
150 }161 }
@@ -153,7 +164,7 @@ func TestCaptureRequests(t *testing.T) {
153 // byte expressions and fixed-struct lists with independently assembled bytes.164 // byte expressions and fixed-struct lists with independently assembled bytes.
154 intern := []byte{16, 0, 4, 0, 7, 0, 0, 0, 'X', 'T', 'U', 'R', 'B', 'O', '_', 0}165 intern := []byte{16, 0, 4, 0, 7, 0, 0, 0, 'X', 'T', 'U', 'R', 'B', 'O', '_', 0}
155 write(intern)166 write(intern)
156- atomReply := packet()167+ atomReply := keep("xproto/InternAtom", packet())
157 if atomReply[0] != 1 {168 if atomReply[0] != 1 {
158 t.Fatalf("InternAtom: %x", atomReply)169 t.Fatalf("InternAtom: %x", atomReply)
159 }170 }
@@ -176,7 +187,7 @@ func TestCaptureRequests(t *testing.T) {
176 binary.LittleEndian.PutUint32(get[8:], atom)187 binary.LittleEndian.PutUint32(get[8:], atom)
177 binary.LittleEndian.PutUint32(get[20:], 1)188 binary.LittleEndian.PutUint32(get[20:], 1)
178 write(get)189 write(get)
179- got := packet()190+ got := keep("xproto/GetProperty", packet())
180 if got[0] != 1 || string(got[32:35]) != "abc" {191 if got[0] != 1 || string(got[32:35]) != "abc" {
181 t.Fatalf("ChangeProperty: %x", got)192 t.Fatalf("ChangeProperty: %x", got)
182 }193 }
@@ -204,9 +215,7 @@ func TestCaptureRequests(t *testing.T) {
204 binary.LittleEndian.PutUint32(ids[4:], 1)215 binary.LittleEndian.PutUint32(ids[4:], 1)
205 binary.LittleEndian.PutUint32(ids[12:], 1)216 binary.LittleEndian.PutUint32(ids[12:], 1)
206 write(ids)217 write(ids)
207- if p := packet(); p[0] != 1 {218+ keep("res/QueryClientIds", packet())
208- t.Fatalf("QueryClientIds: %x", p)
209- }
210 // Value-list reference packets are assembled independently, including mask-bit219 // Value-list reference packets are assembled independently, including mask-bit
211 // ordering. Two sibling windows allow all seven ConfigureWindow bits to be220 // ordering. Two sibling windows allow all seven ConfigureWindow bits to be
212 // exercised legally. A final reply fences all void requests and exposes errors.221 // exercised legally. A final reply fences all void requests and exposes errors.
@@ -257,7 +266,7 @@ func TestCaptureRequests(t *testing.T) {
257 }266 }
258 renderMajor := renderExt[9]267 renderMajor := renderExt[9]
259 write([]byte{renderMajor, 1, 1, 0}) // QueryPictFormats268 write([]byte{renderMajor, 1, 1, 0}) // QueryPictFormats
260- formatsReply := packet()269+ formatsReply := keep("render/QueryPictFormats", packet())
261 if formatsReply[0] != 1 {270 if formatsReply[0] != 1 {
262 t.Fatalf("QueryPictFormats: %x", formatsReply)271 t.Fatalf("QueryPictFormats: %x", formatsReply)
263 }272 }
@@ -296,6 +305,28 @@ func TestCaptureRequests(t *testing.T) {
296 if p := packet(); p[0] != 1 {305 if p := packet(); p[0] != 1 {
297 t.Fatalf("CreatePicture rejected: %x", p)306 t.Fatalf("CreatePicture rejected: %x", p)
298 }307 }
308+ // Reply-only captures. Opcodes 3, 15, 20 and 99 are not request goldens, so
309+ // the request-stream classifier below is unaffected.
310+ attrs := make([]byte, 8)
311+ attrs[0], attrs[2] = 3, 2
312+ binary.LittleEndian.PutUint32(attrs[4:], child)
313+ write(attrs)
314+ keep("xproto/GetWindowAttributes", packet())
315+ tree := make([]byte, 8)
316+ tree[0], tree[2] = 15, 2
317+ binary.LittleEndian.PutUint32(tree[4:], root)
318+ write(tree)
319+ keep("xproto/QueryTree", packet())
320+ // WM_NAME is unset on a fresh Xvfb root: a zero-length GetProperty reply.
321+ empty := make([]byte, 24)
322+ empty[0], empty[2] = 20, 6
323+ binary.LittleEndian.PutUint32(empty[4:], root)
324+ binary.LittleEndian.PutUint32(empty[8:], 39)
325+ binary.LittleEndian.PutUint32(empty[20:], 1)
326+ write(empty)
327+ keep("xproto/GetPropertyEmpty", packet())
328+ write([]byte{99, 0, 1, 0}) // ListExtensions: variable STR iterator
329+ keep("xproto/ListExtensions", packet())
299 // Closing the client drains the request direction; wait before inspecting tee.330 // Closing the client drains the request direction; wait before inspecting tee.
300 client.Close()331 client.Close()
301 select {332 select {
@@ -348,8 +379,18 @@ func TestCaptureRequests(t *testing.T) {
348 }379 }
349 requests = requests[size:]380 requests = requests[size:]
350 }381 }
351- if len(captured) != 12 {382+ if len(captured) != 12 || len(replies) != 11 {
352- t.Fatal("missing captured requests")383+ t.Fatal("missing captured requests or replies")
384+ }
385+ for name, p := range replies {
386+ path := filepath.Join("../../testdata/golden", name+".reply.hex")
387+ if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
388+ t.Fatal(err)
389+ }
390+ text := "# source: Xvfb reply via raw socketpair tee to an independent reference request\n# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace\n" + hex.EncodeToString(p) + "\n"
391+ if err := os.WriteFile(path, []byte(text), 0644); err != nil {
392+ t.Fatal(err)
393+ }
353 }394 }
354 for name, p := range captured {395 for name, p := range captured {
355 path := filepath.Join("../../testdata/golden", name+".req.hex")396 path := filepath.Join("../../testdata/golden", name+".req.hex")
A render/replies_fuzz_test.go +135 -0
@@ -0,0 +1,135 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package render
3+
4+import "testing"
5+import "bnema.dev/xturbo/internal/xtest"
6+
7+func FuzzParseQueryVersionReply(f *testing.F) {
8+ f.Add(make([]byte, 32))
9+ f.Fuzz(func(t *testing.T, raw []byte) {
10+ r, ok := ParseQueryVersionReply(raw)
11+ if !ok {
12+ if len(raw) >= 32 {
13+ t.Fatal("rejected a complete fixed-size reply")
14+ }
15+ return
16+ }
17+ if len(r.Raw()) != len(raw) {
18+ t.Fatal("Raw must return the parsed bytes")
19+ }
20+ _ = r.MajorVersion()
21+ _ = r.MinorVersion()
22+ })
23+}
24+
25+func FuzzParseQueryPictFormatsReply(f *testing.F) {
26+ f.Add(make([]byte, 32))
27+ if raw, err := xtest.LoadHex("../testdata/golden/render/QueryPictFormats.reply.hex"); err != nil {
28+ f.Fatal(err)
29+ } else {
30+ f.Add(raw)
31+ }
32+ f.Fuzz(func(t *testing.T, raw []byte) {
33+ r, ok := ParseQueryPictFormatsReply(raw)
34+ if !ok {
35+ return
36+ }
37+ if len(r.Raw()) != len(raw) {
38+ t.Fatal("Raw must return the parsed bytes")
39+ }
40+ _ = r.NumFormats()
41+ _ = r.NumScreens()
42+ _ = r.NumDepths()
43+ _ = r.NumVisuals()
44+ _ = r.NumSubpixel()
45+ {
46+ l0 := r.Formats()
47+ for i := 0; i < l0.Len(); i++ {
48+ _ = l0.At(i)
49+ }
50+ }
51+ {
52+ it0 := r.Screens()
53+ for it0.Next() {
54+ _ = it0.Value().NumDepths()
55+ _ = it0.Value().Fallback()
56+ {
57+ it1 := it0.Value().Depths()
58+ for it1.Next() {
59+ _ = it1.Value().Depth()
60+ _ = it1.Value().NumVisuals()
61+ {
62+ l2 := it1.Value().Visuals()
63+ for i := 0; i < l2.Len(); i++ {
64+ _ = l2.At(i)
65+ }
66+ }
67+ }
68+ if it1.Err() {
69+ t.Fatal("iterator error after successful Parse")
70+ }
71+ }
72+ }
73+ if it0.Err() {
74+ t.Fatal("iterator error after successful Parse")
75+ }
76+ }
77+ {
78+ l0 := r.Subpixels()
79+ for i := 0; i < l0.Len(); i++ {
80+ _ = l0.At(i)
81+ }
82+ }
83+ })
84+}
85+
86+func FuzzParseQueryPictIndexValuesReply(f *testing.F) {
87+ f.Add(make([]byte, 32))
88+ f.Fuzz(func(t *testing.T, raw []byte) {
89+ r, ok := ParseQueryPictIndexValuesReply(raw)
90+ if !ok {
91+ return
92+ }
93+ if len(r.Raw()) != len(raw) {
94+ t.Fatal("Raw must return the parsed bytes")
95+ }
96+ _ = r.NumValues()
97+ {
98+ l0 := r.Values()
99+ for i := 0; i < l0.Len(); i++ {
100+ _ = l0.At(i)
101+ }
102+ }
103+ })
104+}
105+
106+func FuzzParseQueryFiltersReply(f *testing.F) {
107+ f.Add(make([]byte, 32))
108+ f.Fuzz(func(t *testing.T, raw []byte) {
109+ r, ok := ParseQueryFiltersReply(raw)
110+ if !ok {
111+ return
112+ }
113+ if len(r.Raw()) != len(raw) {
114+ t.Fatal("Raw must return the parsed bytes")
115+ }
116+ _ = r.NumAliases()
117+ _ = r.NumFilters()
118+ {
119+ l0 := r.Aliases()
120+ for i := 0; i < l0.Len(); i++ {
121+ _ = l0.At(i)
122+ }
123+ }
124+ {
125+ it0 := r.Filters()
126+ for it0.Next() {
127+ _ = it0.Value().NameLen()
128+ _ = len(it0.Value().Name())
129+ }
130+ if it0.Err() {
131+ t.Fatal("iterator error after successful Parse")
132+ }
133+ }
134+ })
135+}
A render/replies_generated.go +380 -0
@@ -0,0 +1,380 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package render
3+
4+import "bnema.dev/xturbo/xproto"
5+import "encoding/binary"
6+
7+// QueryVersionReply is a zero-copy view over a QueryVersion reply; offsets count from reply byte 0.
8+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
9+type QueryVersionReply struct{ raw []byte }
10+
11+// ParseQueryVersionReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
12+func ParseQueryVersionReply(raw []byte) (QueryVersionReply, bool) {
13+ if len(raw) < 32 {
14+ return QueryVersionReply{}, false
15+ }
16+ r := QueryVersionReply{raw: raw}
17+ return r, true
18+}
19+func (r QueryVersionReply) Raw() []byte { return r.raw }
20+func (r QueryVersionReply) MajorVersion() uint32 {
21+ return uint32(binary.LittleEndian.Uint32(r.raw[8:]))
22+}
23+func (r QueryVersionReply) MinorVersion() uint32 {
24+ return uint32(binary.LittleEndian.Uint32(r.raw[12:]))
25+}
26+
27+// QueryPictFormatsReply is a zero-copy view over a QueryPictFormats reply; offsets count from reply byte 0.
28+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
29+type QueryPictFormatsReply struct{ raw []byte }
30+
31+// ParseQueryPictFormatsReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
32+func ParseQueryPictFormatsReply(raw []byte) (QueryPictFormatsReply, bool) {
33+ if len(raw) < 32 {
34+ return QueryPictFormatsReply{}, false
35+ }
36+ r := QueryPictFormatsReply{raw: raw}
37+ off := 32
38+ {
39+ n := int(r.NumFormats())
40+ if n < 0 || n > len(r.raw) {
41+ return QueryPictFormatsReply{}, false
42+ }
43+ off += n * 28
44+ }
45+ if off > len(r.raw) {
46+ return QueryPictFormatsReply{}, false
47+ }
48+ {
49+ n := int(r.NumScreens())
50+ if off > len(r.raw) || n < 0 || n > len(r.raw) {
51+ return QueryPictFormatsReply{}, false
52+ }
53+ it := NewPictscreenIter(r.raw[off:], n)
54+ for it.Next() {
55+ off += it.Value().Size()
56+ }
57+ if it.Err() {
58+ return QueryPictFormatsReply{}, false
59+ }
60+ }
61+ {
62+ n := int(r.NumSubpixel())
63+ if n < 0 || n > len(r.raw) {
64+ return QueryPictFormatsReply{}, false
65+ }
66+ off += n * 4
67+ }
68+ if off > len(r.raw) {
69+ return QueryPictFormatsReply{}, false
70+ }
71+ if off > len(r.raw) {
72+ return QueryPictFormatsReply{}, false
73+ }
74+ return r, true
75+}
76+func (r QueryPictFormatsReply) Raw() []byte { return r.raw }
77+func (r QueryPictFormatsReply) NumFormats() uint32 {
78+ return uint32(binary.LittleEndian.Uint32(r.raw[8:]))
79+}
80+func (r QueryPictFormatsReply) NumScreens() uint32 {
81+ return uint32(binary.LittleEndian.Uint32(r.raw[12:]))
82+}
83+func (r QueryPictFormatsReply) NumDepths() uint32 {
84+ return uint32(binary.LittleEndian.Uint32(r.raw[16:]))
85+}
86+func (r QueryPictFormatsReply) NumVisuals() uint32 {
87+ return uint32(binary.LittleEndian.Uint32(r.raw[20:]))
88+}
89+func (r QueryPictFormatsReply) NumSubpixel() uint32 {
90+ return uint32(binary.LittleEndian.Uint32(r.raw[24:]))
91+}
92+func (r QueryPictFormatsReply) Formats() PictforminfoList {
93+ off := 32
94+ end := off + (int(r.NumFormats()))*28
95+ return PictforminfoList{b: r.raw[off:end:end]}
96+}
97+func (r QueryPictFormatsReply) Screens() PictscreenIter {
98+ off := 32
99+ {
100+ n := int(r.NumFormats())
101+ off += n * 28
102+ }
103+ return NewPictscreenIter(r.raw[off:], int(r.NumScreens()))
104+}
105+func (r QueryPictFormatsReply) Subpixels() SubPixelList {
106+ off := 32
107+ {
108+ n := int(r.NumFormats())
109+ off += n * 28
110+ }
111+ {
112+ n := int(r.NumScreens())
113+ it := NewPictscreenIter(r.raw[off:], n)
114+ for it.Next() {
115+ off += it.Value().Size()
116+ }
117+ }
118+ end := off + (int(r.NumSubpixel()))*4
119+ return SubPixelList{b: r.raw[off:end:end]}
120+}
121+
122+// QueryPictIndexValuesReply is a zero-copy view over a QueryPictIndexValues reply; offsets count from reply byte 0.
123+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
124+type QueryPictIndexValuesReply struct{ raw []byte }
125+
126+// ParseQueryPictIndexValuesReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
127+func ParseQueryPictIndexValuesReply(raw []byte) (QueryPictIndexValuesReply, bool) {
128+ if len(raw) < 32 {
129+ return QueryPictIndexValuesReply{}, false
130+ }
131+ r := QueryPictIndexValuesReply{raw: raw}
132+ off := 32
133+ {
134+ n := int(r.NumValues())
135+ if n < 0 || n > len(r.raw) {
136+ return QueryPictIndexValuesReply{}, false
137+ }
138+ off += n * 12
139+ }
140+ if off > len(r.raw) {
141+ return QueryPictIndexValuesReply{}, false
142+ }
143+ if off > len(r.raw) {
144+ return QueryPictIndexValuesReply{}, false
145+ }
146+ return r, true
147+}
148+func (r QueryPictIndexValuesReply) Raw() []byte { return r.raw }
149+func (r QueryPictIndexValuesReply) NumValues() uint32 {
150+ return uint32(binary.LittleEndian.Uint32(r.raw[8:]))
151+}
152+func (r QueryPictIndexValuesReply) Values() IndexvalueList {
153+ off := 32
154+ end := off + (int(r.NumValues()))*12
155+ return IndexvalueList{b: r.raw[off:end:end]}
156+}
157+
158+// QueryFiltersReply is a zero-copy view over a QueryFilters reply; offsets count from reply byte 0.
159+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
160+type QueryFiltersReply struct{ raw []byte }
161+
162+// ParseQueryFiltersReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
163+func ParseQueryFiltersReply(raw []byte) (QueryFiltersReply, bool) {
164+ if len(raw) < 32 {
165+ return QueryFiltersReply{}, false
166+ }
167+ r := QueryFiltersReply{raw: raw}
168+ off := 32
169+ {
170+ n := int(r.NumAliases())
171+ if n < 0 || n > len(r.raw) {
172+ return QueryFiltersReply{}, false
173+ }
174+ off += n * 2
175+ }
176+ if off > len(r.raw) {
177+ return QueryFiltersReply{}, false
178+ }
179+ {
180+ n := int(r.NumFilters())
181+ if off > len(r.raw) || n < 0 || n > len(r.raw) {
182+ return QueryFiltersReply{}, false
183+ }
184+ it := xproto.NewStrIter(r.raw[off:], n)
185+ for it.Next() {
186+ off += it.Value().Size()
187+ }
188+ if it.Err() {
189+ return QueryFiltersReply{}, false
190+ }
191+ }
192+ if off > len(r.raw) {
193+ return QueryFiltersReply{}, false
194+ }
195+ return r, true
196+}
197+func (r QueryFiltersReply) Raw() []byte { return r.raw }
198+func (r QueryFiltersReply) NumAliases() uint32 { return uint32(binary.LittleEndian.Uint32(r.raw[8:])) }
199+func (r QueryFiltersReply) NumFilters() uint32 { return uint32(binary.LittleEndian.Uint32(r.raw[12:])) }
200+func (r QueryFiltersReply) Aliases() Uint16List {
201+ off := 32
202+ end := off + (int(r.NumAliases()))*2
203+ return Uint16List{b: r.raw[off:end:end]}
204+}
205+func (r QueryFiltersReply) Filters() xproto.StrIter {
206+ off := 32
207+ {
208+ n := int(r.NumAliases())
209+ off += n * 2
210+ }
211+ return xproto.NewStrIter(r.raw[off:], int(r.NumFilters()))
212+}
213+
214+// PictdepthView is a zero-copy view over one wire-encoded Pictdepth, yielded by PictdepthIter.
215+// It shares the lifetime of the bytes it was taken from (for replies: until the Handler callback returns). Copy what you keep.
216+type PictdepthView struct{ raw []byte }
217+
218+func (v PictdepthView) Raw() []byte { return v.raw }
219+
220+// Size is the element's wire size, including trailing alignment.
221+func (v PictdepthView) Size() int { return len(v.raw) }
222+func sizePictdepth(p []byte) int {
223+ if len(p) < 8 {
224+ return -1
225+ }
226+ v := PictdepthView{raw: p}
227+ _ = v
228+ off := 8
229+ {
230+ n := int(v.NumVisuals())
231+ if n < 0 || n > len(v.raw) {
232+ return -1
233+ }
234+ off += n * 8
235+ }
236+ if off > len(v.raw) {
237+ return -1
238+ }
239+ if off > len(v.raw) {
240+ return -1
241+ }
242+ return off
243+}
244+func (v PictdepthView) Depth() uint8 { return uint8(v.raw[0:][0]) }
245+func (v PictdepthView) NumVisuals() uint16 { return uint16(binary.LittleEndian.Uint16(v.raw[2:])) }
246+func (v PictdepthView) Visuals() PictvisualList {
247+ off := 8
248+ end := off + (int(v.NumVisuals()))*8
249+ return PictvisualList{b: v.raw[off:end:end]}
250+}
251+
252+// PictdepthIter walks n consecutive Pictdepth elements; Err reports a malformed length.
253+// It shares the lifetime of the bytes it walks.
254+type PictdepthIter struct {
255+ b []byte
256+ n int
257+ v PictdepthView
258+ err bool
259+}
260+
261+// NewPictdepthIter iterates n elements encoded at the start of b.
262+func NewPictdepthIter(b []byte, n int) PictdepthIter { return PictdepthIter{b: b, n: n} }
263+func (it *PictdepthIter) Next() bool {
264+ if it.n <= 0 || it.err {
265+ return false
266+ }
267+ s := sizePictdepth(it.b)
268+ if s < 0 {
269+ it.err = true
270+ it.n = 0
271+ return false
272+ }
273+ it.v = PictdepthView{raw: it.b[:s:s]}
274+ it.b = it.b[s:]
275+ it.n--
276+ return true
277+}
278+func (it *PictdepthIter) Value() PictdepthView { return it.v }
279+func (it *PictdepthIter) Err() bool { return it.err }
280+
281+// PictscreenView is a zero-copy view over one wire-encoded Pictscreen, yielded by PictscreenIter.
282+// It shares the lifetime of the bytes it was taken from (for replies: until the Handler callback returns). Copy what you keep.
283+type PictscreenView struct{ raw []byte }
284+
285+func (v PictscreenView) Raw() []byte { return v.raw }
286+
287+// Size is the element's wire size, including trailing alignment.
288+func (v PictscreenView) Size() int { return len(v.raw) }
289+func sizePictscreen(p []byte) int {
290+ if len(p) < 8 {
291+ return -1
292+ }
293+ v := PictscreenView{raw: p}
294+ _ = v
295+ off := 8
296+ {
297+ n := int(v.NumDepths())
298+ if off > len(v.raw) || n < 0 || n > len(v.raw) {
299+ return -1
300+ }
301+ it := NewPictdepthIter(v.raw[off:], n)
302+ for it.Next() {
303+ off += it.Value().Size()
304+ }
305+ if it.Err() {
306+ return -1
307+ }
308+ }
309+ if off > len(v.raw) {
310+ return -1
311+ }
312+ return off
313+}
314+func (v PictscreenView) NumDepths() uint32 { return uint32(binary.LittleEndian.Uint32(v.raw[0:])) }
315+func (v PictscreenView) Fallback() Pictformat {
316+ return Pictformat(binary.LittleEndian.Uint32(v.raw[4:]))
317+}
318+func (v PictscreenView) Depths() PictdepthIter {
319+ off := 8
320+ return NewPictdepthIter(v.raw[off:], int(v.NumDepths()))
321+}
322+
323+// PictscreenIter walks n consecutive Pictscreen elements; Err reports a malformed length.
324+// It shares the lifetime of the bytes it walks.
325+type PictscreenIter struct {
326+ b []byte
327+ n int
328+ v PictscreenView
329+ err bool
330+}
331+
332+// NewPictscreenIter iterates n elements encoded at the start of b.
333+func NewPictscreenIter(b []byte, n int) PictscreenIter { return PictscreenIter{b: b, n: n} }
334+func (it *PictscreenIter) Next() bool {
335+ if it.n <= 0 || it.err {
336+ return false
337+ }
338+ s := sizePictscreen(it.b)
339+ if s < 0 {
340+ it.err = true
341+ it.n = 0
342+ return false
343+ }
344+ it.v = PictscreenView{raw: it.b[:s:s]}
345+ it.b = it.b[s:]
346+ it.n--
347+ return true
348+}
349+func (it *PictscreenIter) Value() PictscreenView { return it.v }
350+func (it *PictscreenIter) Err() bool { return it.err }
351+
352+// IndexvalueList is a borrowed view over a wire list of Indexvalue. It shares the lifetime of the bytes it was taken from; copy what you keep.
353+type IndexvalueList struct{ b []byte }
354+
355+func (l IndexvalueList) Len() int { return len(l.b) / 12 }
356+func (l IndexvalueList) At(i int) Indexvalue { return GetIndexvalue(l.b[i*12:]) }
357+
358+// PictforminfoList is a borrowed view over a wire list of Pictforminfo. It shares the lifetime of the bytes it was taken from; copy what you keep.
359+type PictforminfoList struct{ b []byte }
360+
361+func (l PictforminfoList) Len() int { return len(l.b) / 28 }
362+func (l PictforminfoList) At(i int) Pictforminfo { return GetPictforminfo(l.b[i*28:]) }
363+
364+// PictvisualList is a borrowed view over a wire list of Pictvisual. It shares the lifetime of the bytes it was taken from; copy what you keep.
365+type PictvisualList struct{ b []byte }
366+
367+func (l PictvisualList) Len() int { return len(l.b) / 8 }
368+func (l PictvisualList) At(i int) Pictvisual { return GetPictvisual(l.b[i*8:]) }
369+
370+// SubPixelList is a borrowed view over a wire list of SubPixel. It shares the lifetime of the bytes it was taken from; copy what you keep.
371+type SubPixelList struct{ b []byte }
372+
373+func (l SubPixelList) Len() int { return len(l.b) / 4 }
374+func (l SubPixelList) At(i int) SubPixel { return SubPixel(binary.LittleEndian.Uint32(l.b[i*4:])) }
375+
376+// Uint16List is a borrowed view over a wire list of uint16. It shares the lifetime of the bytes it was taken from; copy what you keep.
377+type Uint16List struct{ b []byte }
378+
379+func (l Uint16List) Len() int { return len(l.b) / 2 }
380+func (l Uint16List) At(i int) uint16 { return uint16(binary.LittleEndian.Uint16(l.b[i*2:])) }
A replies_test.go +304 -0
@@ -0,0 +1,304 @@
1+package xturbo_test
2+
3+import (
4+ "bytes"
5+ "encoding/binary"
6+ "testing"
7+
8+ "bnema.dev/xturbo/internal/xtest"
9+ "bnema.dev/xturbo/render"
10+ "bnema.dev/xturbo/res"
11+ "bnema.dev/xturbo/xfixes"
12+ "bnema.dev/xturbo/xproto"
13+)
14+
15+func loadReply(t testing.TB, name string) []byte {
16+ t.Helper()
17+ raw, err := xtest.LoadHex("testdata/golden/" + name + ".reply.hex")
18+ if err != nil {
19+ t.Fatal(err)
20+ }
21+ if raw[0] != 1 || len(raw) != 32+4*int(binary.LittleEndian.Uint32(raw[4:])) {
22+ t.Fatalf("%s: not a framed reply", name)
23+ }
24+ return raw
25+}
26+
27+// Expected values are independent of the generated code: they follow from the
28+// capture script (window geometry, written property, ConfigureWindow state)
29+// or are read from the raw golden at protocol-spec offsets.
30+func TestReplyGoldens(t *testing.T) {
31+ u32 := func(raw []byte, off int) uint32 { return binary.LittleEndian.Uint32(raw[off:]) }
32+ t.Run("xproto/GetGeometry", func(t *testing.T) {
33+ raw := loadReply(t, "xproto/GetGeometry")
34+ r, ok := xproto.ParseGetGeometryReply(raw)
35+ if !ok || r.Depth() != 24 || r.Root() != xproto.Window(u32(raw, 8)) || r.X() != 0 || r.Y() != 0 ||
36+ r.Width() != 800 || r.Height() != 600 || r.BorderWidth() != 0 || !bytes.Equal(r.Raw(), raw) {
37+ t.Fatalf("decoded %+v", r)
38+ }
39+ })
40+ t.Run("xproto/QueryTree", func(t *testing.T) {
41+ raw := loadReply(t, "xproto/QueryTree")
42+ r, ok := xproto.ParseQueryTreeReply(raw)
43+ if !ok || r.Root() != xproto.Window(u32(raw, 8)) || r.Parent() != xproto.WindowNone || r.ChildrenLen() != 2 {
44+ t.Fatal("fixed fields")
45+ }
46+ c := r.Children()
47+ // Stacking order bottom-to-top: the sibling was created before the child.
48+ if c.Len() != 2 || c.At(0)&0x1fffff != 2 || c.At(1)&0x1fffff != 1 || c.At(0)&^0x1fffff != c.At(1)&^0x1fffff {
49+ t.Fatalf("children %x %x", c.At(0), c.At(1))
50+ }
51+ })
52+ t.Run("xproto/GetWindowAttributes", func(t *testing.T) {
53+ raw := loadReply(t, "xproto/GetWindowAttributes")
54+ r, ok := xproto.ParseGetWindowAttributesReply(raw)
55+ if !ok || r.BackingStore() != xproto.BackingStoreNotUseful || r.Visual() != xproto.Visualid(u32(raw, 8)) ||
56+ r.Class() != xproto.WindowClassInputOutput || r.BitGravity() != xproto.GravityBitForget ||
57+ r.WinGravity() != xproto.GravityNorthWest || r.BackingPlanes() != 0xffffffff || r.BackingPixel() != 0 ||
58+ !r.SaveUnder() || !r.MapIsInstalled() || r.MapState() != xproto.MapStateUnmapped || r.OverrideRedirect() ||
59+ r.Colormap() != xproto.Colormap(u32(raw, 28)) || r.AllEventMasks() != 0 || r.YourEventMask() != 0 ||
60+ r.DoNotPropagateMask() != 0 {
61+ t.Fatal("decoded attributes")
62+ }
63+ })
64+ t.Run("xproto/InternAtom", func(t *testing.T) {
65+ raw := loadReply(t, "xproto/InternAtom")
66+ r, ok := xproto.ParseInternAtomReply(raw)
67+ if !ok || r.Atom() == xproto.AtomNone || r.Atom() != xproto.Atom(u32(raw, 8)) {
68+ t.Fatal("atom")
69+ }
70+ })
71+ t.Run("xproto/GetProperty", func(t *testing.T) {
72+ raw := loadReply(t, "xproto/GetProperty")
73+ r, ok := xproto.ParseGetPropertyReply(raw)
74+ if !ok || r.Format() != 8 || r.Type() != xproto.AtomString || r.BytesAfter() != 0 || r.ValueLen() != 3 ||
75+ string(r.Value()) != "abc" || cap(r.Value()) != 3 {
76+ t.Fatalf("value %q", r.Value())
77+ }
78+ })
79+ t.Run("xproto/GetPropertyEmpty", func(t *testing.T) {
80+ raw := loadReply(t, "xproto/GetPropertyEmpty")
81+ r, ok := xproto.ParseGetPropertyReply(raw)
82+ if !ok || r.Format() != 0 || r.Type() != xproto.AtomNone || r.ValueLen() != 0 || len(r.Value()) != 0 {
83+ t.Fatal("empty property")
84+ }
85+ })
86+ t.Run("xproto/QueryExtension", func(t *testing.T) {
87+ raw := loadReply(t, "xproto/QueryExtension")
88+ r, ok := xproto.ParseQueryExtensionReply(raw)
89+ if !ok || !r.Present() || r.MajorOpcode() < 128 || r.FirstEvent() != raw[10] || r.FirstError() != raw[11] {
90+ t.Fatal("XFIXES query")
91+ }
92+ })
93+ t.Run("xproto/ListExtensions", func(t *testing.T) {
94+ raw := loadReply(t, "xproto/ListExtensions")
95+ r, ok := xproto.ParseListExtensionsReply(raw)
96+ if !ok {
97+ t.Fatal("parse")
98+ }
99+ it := r.Names()
100+ var names []string
101+ total := 32
102+ for it.Next() {
103+ v := it.Value()
104+ if int(v.NameLen()) != len(v.Name()) || v.Size() != 1+len(v.Name()) {
105+ t.Fatal("STR view")
106+ }
107+ names = append(names, string(v.Name()))
108+ total += v.Size()
109+ }
110+ if it.Err() || len(names) != int(r.NamesLen()) || total > len(raw) || len(raw)-total > 3 {
111+ t.Fatalf("iterated %d of %d, %d/%d bytes", len(names), r.NamesLen(), total, len(raw))
112+ }
113+ found := map[string]bool{}
114+ for _, n := range names {
115+ found[n] = true
116+ }
117+ for _, want := range []string{"XFIXES", "RENDER", "BIG-REQUESTS", "Composite", "X-Resource"} {
118+ if !found[want] {
119+ t.Fatalf("missing %s in %q", want, names)
120+ }
121+ }
122+ })
123+ t.Run("xfixes/QueryVersion", func(t *testing.T) {
124+ r, ok := xfixes.ParseQueryVersionReply(loadReply(t, "xfixes/QueryVersion"))
125+ if !ok || r.MajorVersion() != 6 || r.MinorVersion() != 0 {
126+ t.Fatal("version")
127+ }
128+ })
129+ t.Run("res/QueryClientIds", func(t *testing.T) {
130+ r, ok := res.ParseQueryClientIdsReply(loadReply(t, "res/QueryClientIds"))
131+ if !ok || r.NumIds() != 2 {
132+ t.Fatal("parse")
133+ }
134+ // Spec client 0 asks for every client's XID: the server client and ours.
135+ it := r.Ids()
136+ var clients []uint32
137+ for it.Next() {
138+ v := it.Value()
139+ if v.Spec().Mask != res.ClientIdMaskClientXID || v.Length() != 0 || v.Value().Len() != 0 || v.Size() != 12 {
140+ t.Fatal("ClientIdValue view")
141+ }
142+ clients = append(clients, v.Spec().Client)
143+ }
144+ if it.Err() || len(clients) != 2 || clients[0] != 0 || clients[1] == 0 {
145+ t.Fatalf("clients %x", clients)
146+ }
147+ })
148+ t.Run("render/QueryPictFormats", func(t *testing.T) {
149+ raw := loadReply(t, "render/QueryPictFormats")
150+ r, ok := render.ParseQueryPictFormatsReply(raw)
151+ if !ok || r.NumScreens() != 1 || r.Formats().Len() != int(r.NumFormats()) || r.Subpixels().Len() != int(r.NumSubpixel()) {
152+ t.Fatal("counts")
153+ }
154+ formats := map[render.Pictformat]bool{}
155+ for i := range r.Formats().Len() {
156+ f := r.Formats().At(i)
157+ formats[f.Id] = true
158+ }
159+ screens := r.Screens()
160+ visuals := 0
161+ for screens.Next() {
162+ depths := screens.Value().Depths()
163+ for depths.Next() {
164+ vs := depths.Value().Visuals()
165+ for i := range vs.Len() {
166+ if !formats[vs.At(i).Format] {
167+ t.Fatal("visual references an unknown format")
168+ }
169+ visuals++
170+ }
171+ }
172+ if depths.Err() {
173+ t.Fatal("depth iterator")
174+ }
175+ }
176+ if screens.Err() || visuals == 0 {
177+ t.Fatal("screen iterator")
178+ }
179+ for i := range r.Subpixels().Len() {
180+ if r.Subpixels().At(i) != render.SubPixelUnknown {
181+ t.Fatal("Xvfb subpixel order")
182+ }
183+ }
184+ })
185+}
186+
187+func TestReplyParseRejects(t *testing.T) {
188+ raw := loadReply(t, "xproto/QueryTree")
189+ if _, ok := xproto.ParseQueryTreeReply(raw[:31]); ok {
190+ t.Fatal("short fixed prefix accepted")
191+ }
192+ if _, ok := xproto.ParseQueryTreeReply(raw[:len(raw)-1]); ok {
193+ t.Fatal("truncated list accepted")
194+ }
195+ raw = loadReply(t, "xproto/GetWindowAttributes")
196+ if _, ok := xproto.ParseGetWindowAttributesReply(raw[:43]); ok {
197+ t.Fatal("44-byte reply prefix not enforced")
198+ }
199+ raw = bytes.Clone(loadReply(t, "xproto/ListExtensions"))
200+ raw[1] = 255 // names_len: far more STRs than the reply holds
201+ if _, ok := xproto.ParseListExtensionsReply(raw); ok {
202+ t.Fatal("STR count beyond the reply accepted")
203+ }
204+ prop := bytes.Clone(loadReply(t, "xproto/GetProperty"))
205+ prop[1] = 32 // 3 items of 4 bytes overrun the 36-byte reply
206+ if _, ok := xproto.ParseGetPropertyReply(prop); ok {
207+ t.Fatal("format*value_len overrun accepted")
208+ }
209+}
210+
211+func TestReplyAllocs(t *testing.T) {
212+ prop := loadReply(t, "xproto/GetProperty")
213+ tree := loadReply(t, "xproto/QueryTree")
214+ exts := loadReply(t, "xproto/ListExtensions")
215+ formats := loadReply(t, "render/QueryPictFormats")
216+ ids := loadReply(t, "res/QueryClientIds")
217+ var sink uint64
218+ n := testing.AllocsPerRun(1000, func() {
219+ p, _ := xproto.ParseGetPropertyReply(prop)
220+ v := p.Value()
221+ sink += uint64(len(v)) + uint64(p.Type()) + uint64(p.Format())
222+ q, _ := xproto.ParseQueryTreeReply(tree)
223+ c := q.Children()
224+ for i := range c.Len() {
225+ sink += uint64(c.At(i))
226+ }
227+ sink += uint64(q.Root()) + uint64(q.Parent())
228+ l, _ := xproto.ParseListExtensionsReply(exts)
229+ it := l.Names()
230+ for it.Next() {
231+ sink += uint64(len(it.Value().Name()))
232+ }
233+ f, _ := render.ParseQueryPictFormatsReply(formats)
234+ fl := f.Formats()
235+ for i := range fl.Len() {
236+ sink += uint64(fl.At(i).Id)
237+ }
238+ s := f.Screens()
239+ for s.Next() {
240+ d := s.Value().Depths()
241+ for d.Next() {
242+ vs := d.Value().Visuals()
243+ for i := range vs.Len() {
244+ sink += uint64(vs.At(i).Visual)
245+ }
246+ }
247+ }
248+ sp := f.Subpixels()
249+ for i := range sp.Len() {
250+ sink += uint64(sp.At(i))
251+ }
252+ r, _ := res.ParseQueryClientIdsReply(ids)
253+ ri := r.Ids()
254+ for ri.Next() {
255+ sink += uint64(ri.Value().Value().Len())
256+ }
257+ })
258+ if n != 0 {
259+ t.Fatalf("reply parse/getters/iteration allocate %v times", n)
260+ }
261+ _ = sink
262+}
263+
264+func BenchmarkGetPropertyReply(b *testing.B) {
265+ raw := loadReply(b, "xproto/GetProperty")
266+ var sink int
267+ b.ReportAllocs()
268+ for b.Loop() {
269+ r, ok := xproto.ParseGetPropertyReply(raw)
270+ if !ok {
271+ b.Fatal("parse")
272+ }
273+ sink += len(r.Value()) + int(r.Type())
274+ }
275+ _ = sink
276+}
277+
278+func BenchmarkQueryTreeReply(b *testing.B) {
279+ raw := loadReply(b, "xproto/QueryTree")
280+ var sink xproto.Window
281+ b.ReportAllocs()
282+ for b.Loop() {
283+ r, _ := xproto.ParseQueryTreeReply(raw)
284+ c := r.Children()
285+ for i := range c.Len() {
286+ sink ^= c.At(i)
287+ }
288+ }
289+ _ = sink
290+}
291+
292+func BenchmarkListExtensionsReply(b *testing.B) {
293+ raw := loadReply(b, "xproto/ListExtensions")
294+ var sink int
295+ b.ReportAllocs()
296+ for b.Loop() {
297+ r, _ := xproto.ParseListExtensionsReply(raw)
298+ it := r.Names()
299+ for it.Next() {
300+ sink += len(it.Value().Name())
301+ }
302+ }
303+ _ = sink
304+}
A res/replies_fuzz_test.go +146 -0
@@ -0,0 +1,146 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package res
3+
4+import "testing"
5+import "bnema.dev/xturbo/internal/xtest"
6+
7+func FuzzParseQueryVersionReply(f *testing.F) {
8+ f.Add(make([]byte, 32))
9+ f.Fuzz(func(t *testing.T, raw []byte) {
10+ r, ok := ParseQueryVersionReply(raw)
11+ if !ok {
12+ if len(raw) >= 32 {
13+ t.Fatal("rejected a complete fixed-size reply")
14+ }
15+ return
16+ }
17+ if len(r.Raw()) != len(raw) {
18+ t.Fatal("Raw must return the parsed bytes")
19+ }
20+ _ = r.ServerMajor()
21+ _ = r.ServerMinor()
22+ })
23+}
24+
25+func FuzzParseQueryClientsReply(f *testing.F) {
26+ f.Add(make([]byte, 32))
27+ f.Fuzz(func(t *testing.T, raw []byte) {
28+ r, ok := ParseQueryClientsReply(raw)
29+ if !ok {
30+ return
31+ }
32+ if len(r.Raw()) != len(raw) {
33+ t.Fatal("Raw must return the parsed bytes")
34+ }
35+ _ = r.NumClients()
36+ {
37+ l0 := r.Clients()
38+ for i := 0; i < l0.Len(); i++ {
39+ _ = l0.At(i)
40+ }
41+ }
42+ })
43+}
44+
45+func FuzzParseQueryClientResourcesReply(f *testing.F) {
46+ f.Add(make([]byte, 32))
47+ f.Fuzz(func(t *testing.T, raw []byte) {
48+ r, ok := ParseQueryClientResourcesReply(raw)
49+ if !ok {
50+ return
51+ }
52+ if len(r.Raw()) != len(raw) {
53+ t.Fatal("Raw must return the parsed bytes")
54+ }
55+ _ = r.NumTypes()
56+ {
57+ l0 := r.Types()
58+ for i := 0; i < l0.Len(); i++ {
59+ _ = l0.At(i)
60+ }
61+ }
62+ })
63+}
64+
65+func FuzzParseQueryClientPixmapBytesReply(f *testing.F) {
66+ f.Add(make([]byte, 32))
67+ f.Fuzz(func(t *testing.T, raw []byte) {
68+ r, ok := ParseQueryClientPixmapBytesReply(raw)
69+ if !ok {
70+ if len(raw) >= 32 {
71+ t.Fatal("rejected a complete fixed-size reply")
72+ }
73+ return
74+ }
75+ if len(r.Raw()) != len(raw) {
76+ t.Fatal("Raw must return the parsed bytes")
77+ }
78+ _ = r.Bytes()
79+ _ = r.BytesOverflow()
80+ })
81+}
82+
83+func FuzzParseQueryClientIdsReply(f *testing.F) {
84+ f.Add(make([]byte, 32))
85+ if raw, err := xtest.LoadHex("../testdata/golden/res/QueryClientIds.reply.hex"); err != nil {
86+ f.Fatal(err)
87+ } else {
88+ f.Add(raw)
89+ }
90+ f.Fuzz(func(t *testing.T, raw []byte) {
91+ r, ok := ParseQueryClientIdsReply(raw)
92+ if !ok {
93+ return
94+ }
95+ if len(r.Raw()) != len(raw) {
96+ t.Fatal("Raw must return the parsed bytes")
97+ }
98+ _ = r.NumIds()
99+ {
100+ it0 := r.Ids()
101+ for it0.Next() {
102+ _ = it0.Value().Spec()
103+ _ = it0.Value().Length()
104+ {
105+ l1 := it0.Value().Value()
106+ for i := 0; i < l1.Len(); i++ {
107+ _ = l1.At(i)
108+ }
109+ }
110+ }
111+ if it0.Err() {
112+ t.Fatal("iterator error after successful Parse")
113+ }
114+ }
115+ })
116+}
117+
118+func FuzzParseQueryResourceBytesReply(f *testing.F) {
119+ f.Add(make([]byte, 32))
120+ f.Fuzz(func(t *testing.T, raw []byte) {
121+ r, ok := ParseQueryResourceBytesReply(raw)
122+ if !ok {
123+ return
124+ }
125+ if len(r.Raw()) != len(raw) {
126+ t.Fatal("Raw must return the parsed bytes")
127+ }
128+ _ = r.NumSizes()
129+ {
130+ it0 := r.Sizes()
131+ for it0.Next() {
132+ _ = it0.Value().Size_()
133+ _ = it0.Value().NumCrossReferences()
134+ {
135+ l1 := it0.Value().CrossReferences()
136+ for i := 0; i < l1.Len(); i++ {
137+ _ = l1.At(i)
138+ }
139+ }
140+ }
141+ if it0.Err() {
142+ t.Fatal("iterator error after successful Parse")
143+ }
144+ }
145+ })
146+}
A res/replies_generated.go +348 -0
@@ -0,0 +1,348 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package res
3+
4+import "encoding/binary"
5+
6+// QueryVersionReply is a zero-copy view over a QueryVersion reply; offsets count from reply byte 0.
7+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
8+type QueryVersionReply struct{ raw []byte }
9+
10+// ParseQueryVersionReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
11+func ParseQueryVersionReply(raw []byte) (QueryVersionReply, bool) {
12+ if len(raw) < 32 {
13+ return QueryVersionReply{}, false
14+ }
15+ r := QueryVersionReply{raw: raw}
16+ return r, true
17+}
18+func (r QueryVersionReply) Raw() []byte { return r.raw }
19+func (r QueryVersionReply) ServerMajor() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[8:])) }
20+func (r QueryVersionReply) ServerMinor() uint16 {
21+ return uint16(binary.LittleEndian.Uint16(r.raw[10:]))
22+}
23+
24+// QueryClientsReply is a zero-copy view over a QueryClients reply; offsets count from reply byte 0.
25+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
26+type QueryClientsReply struct{ raw []byte }
27+
28+// ParseQueryClientsReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
29+func ParseQueryClientsReply(raw []byte) (QueryClientsReply, bool) {
30+ if len(raw) < 32 {
31+ return QueryClientsReply{}, false
32+ }
33+ r := QueryClientsReply{raw: raw}
34+ off := 32
35+ {
36+ n := int(r.NumClients())
37+ if n < 0 || n > len(r.raw) {
38+ return QueryClientsReply{}, false
39+ }
40+ off += n * 8
41+ }
42+ if off > len(r.raw) {
43+ return QueryClientsReply{}, false
44+ }
45+ if off > len(r.raw) {
46+ return QueryClientsReply{}, false
47+ }
48+ return r, true
49+}
50+func (r QueryClientsReply) Raw() []byte { return r.raw }
51+func (r QueryClientsReply) NumClients() uint32 { return uint32(binary.LittleEndian.Uint32(r.raw[8:])) }
52+func (r QueryClientsReply) Clients() ClientList {
53+ off := 32
54+ end := off + (int(r.NumClients()))*8
55+ return ClientList{b: r.raw[off:end:end]}
56+}
57+
58+// QueryClientResourcesReply is a zero-copy view over a QueryClientResources reply; offsets count from reply byte 0.
59+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
60+type QueryClientResourcesReply struct{ raw []byte }
61+
62+// ParseQueryClientResourcesReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
63+func ParseQueryClientResourcesReply(raw []byte) (QueryClientResourcesReply, bool) {
64+ if len(raw) < 32 {
65+ return QueryClientResourcesReply{}, false
66+ }
67+ r := QueryClientResourcesReply{raw: raw}
68+ off := 32
69+ {
70+ n := int(r.NumTypes())
71+ if n < 0 || n > len(r.raw) {
72+ return QueryClientResourcesReply{}, false
73+ }
74+ off += n * 8
75+ }
76+ if off > len(r.raw) {
77+ return QueryClientResourcesReply{}, false
78+ }
79+ if off > len(r.raw) {
80+ return QueryClientResourcesReply{}, false
81+ }
82+ return r, true
83+}
84+func (r QueryClientResourcesReply) Raw() []byte { return r.raw }
85+func (r QueryClientResourcesReply) NumTypes() uint32 {
86+ return uint32(binary.LittleEndian.Uint32(r.raw[8:]))
87+}
88+func (r QueryClientResourcesReply) Types() TypeList {
89+ off := 32
90+ end := off + (int(r.NumTypes()))*8
91+ return TypeList{b: r.raw[off:end:end]}
92+}
93+
94+// QueryClientPixmapBytesReply is a zero-copy view over a QueryClientPixmapBytes reply; offsets count from reply byte 0.
95+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
96+type QueryClientPixmapBytesReply struct{ raw []byte }
97+
98+// ParseQueryClientPixmapBytesReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
99+func ParseQueryClientPixmapBytesReply(raw []byte) (QueryClientPixmapBytesReply, bool) {
100+ if len(raw) < 32 {
101+ return QueryClientPixmapBytesReply{}, false
102+ }
103+ r := QueryClientPixmapBytesReply{raw: raw}
104+ return r, true
105+}
106+func (r QueryClientPixmapBytesReply) Raw() []byte { return r.raw }
107+func (r QueryClientPixmapBytesReply) Bytes() uint32 {
108+ return uint32(binary.LittleEndian.Uint32(r.raw[8:]))
109+}
110+func (r QueryClientPixmapBytesReply) BytesOverflow() uint32 {
111+ return uint32(binary.LittleEndian.Uint32(r.raw[12:]))
112+}
113+
114+// QueryClientIdsReply is a zero-copy view over a QueryClientIds reply; offsets count from reply byte 0.
115+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
116+type QueryClientIdsReply struct{ raw []byte }
117+
118+// ParseQueryClientIdsReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
119+func ParseQueryClientIdsReply(raw []byte) (QueryClientIdsReply, bool) {
120+ if len(raw) < 32 {
121+ return QueryClientIdsReply{}, false
122+ }
123+ r := QueryClientIdsReply{raw: raw}
124+ off := 32
125+ {
126+ n := int(r.NumIds())
127+ if off > len(r.raw) || n < 0 || n > len(r.raw) {
128+ return QueryClientIdsReply{}, false
129+ }
130+ it := NewClientIdValueIter(r.raw[off:], n)
131+ for it.Next() {
132+ off += it.Value().Size()
133+ }
134+ if it.Err() {
135+ return QueryClientIdsReply{}, false
136+ }
137+ }
138+ if off > len(r.raw) {
139+ return QueryClientIdsReply{}, false
140+ }
141+ return r, true
142+}
143+func (r QueryClientIdsReply) Raw() []byte { return r.raw }
144+func (r QueryClientIdsReply) NumIds() uint32 { return uint32(binary.LittleEndian.Uint32(r.raw[8:])) }
145+func (r QueryClientIdsReply) Ids() ClientIdValueIter {
146+ off := 32
147+ return NewClientIdValueIter(r.raw[off:], int(r.NumIds()))
148+}
149+
150+// QueryResourceBytesReply is a zero-copy view over a QueryResourceBytes reply; offsets count from reply byte 0.
151+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
152+type QueryResourceBytesReply struct{ raw []byte }
153+
154+// ParseQueryResourceBytesReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
155+func ParseQueryResourceBytesReply(raw []byte) (QueryResourceBytesReply, bool) {
156+ if len(raw) < 32 {
157+ return QueryResourceBytesReply{}, false
158+ }
159+ r := QueryResourceBytesReply{raw: raw}
160+ off := 32
161+ {
162+ n := int(r.NumSizes())
163+ if off > len(r.raw) || n < 0 || n > len(r.raw) {
164+ return QueryResourceBytesReply{}, false
165+ }
166+ it := NewResourceSizeValueIter(r.raw[off:], n)
167+ for it.Next() {
168+ off += it.Value().Size()
169+ }
170+ if it.Err() {
171+ return QueryResourceBytesReply{}, false
172+ }
173+ }
174+ if off > len(r.raw) {
175+ return QueryResourceBytesReply{}, false
176+ }
177+ return r, true
178+}
179+func (r QueryResourceBytesReply) Raw() []byte { return r.raw }
180+func (r QueryResourceBytesReply) NumSizes() uint32 {
181+ return uint32(binary.LittleEndian.Uint32(r.raw[8:]))
182+}
183+func (r QueryResourceBytesReply) Sizes() ResourceSizeValueIter {
184+ off := 32
185+ return NewResourceSizeValueIter(r.raw[off:], int(r.NumSizes()))
186+}
187+
188+// ClientIdValueView is a zero-copy view over one wire-encoded ClientIdValue, yielded by ClientIdValueIter.
189+// It shares the lifetime of the bytes it was taken from (for replies: until the Handler callback returns). Copy what you keep.
190+type ClientIdValueView struct{ raw []byte }
191+
192+func (v ClientIdValueView) Raw() []byte { return v.raw }
193+
194+// Size is the element's wire size, including trailing alignment.
195+func (v ClientIdValueView) Size() int { return len(v.raw) }
196+func sizeClientIdValue(p []byte) int {
197+ if len(p) < 12 {
198+ return -1
199+ }
200+ v := ClientIdValueView{raw: p}
201+ _ = v
202+ off := 12
203+ {
204+ n := (int(v.Length()) / 4)
205+ if n < 0 || n > len(v.raw) {
206+ return -1
207+ }
208+ off += n * 4
209+ }
210+ if off > len(v.raw) {
211+ return -1
212+ }
213+ if off > len(v.raw) {
214+ return -1
215+ }
216+ return off
217+}
218+func (v ClientIdValueView) Spec() ClientIdSpec { return GetClientIdSpec(v.raw[0:]) }
219+func (v ClientIdValueView) Length() uint32 { return uint32(binary.LittleEndian.Uint32(v.raw[8:])) }
220+func (v ClientIdValueView) Value() Uint32List {
221+ off := 12
222+ end := off + (int(v.Length())/4)*4
223+ return Uint32List{b: v.raw[off:end:end]}
224+}
225+
226+// ClientIdValueIter walks n consecutive ClientIdValue elements; Err reports a malformed length.
227+// It shares the lifetime of the bytes it walks.
228+type ClientIdValueIter struct {
229+ b []byte
230+ n int
231+ v ClientIdValueView
232+ err bool
233+}
234+
235+// NewClientIdValueIter iterates n elements encoded at the start of b.
236+func NewClientIdValueIter(b []byte, n int) ClientIdValueIter { return ClientIdValueIter{b: b, n: n} }
237+func (it *ClientIdValueIter) Next() bool {
238+ if it.n <= 0 || it.err {
239+ return false
240+ }
241+ s := sizeClientIdValue(it.b)
242+ if s < 0 {
243+ it.err = true
244+ it.n = 0
245+ return false
246+ }
247+ it.v = ClientIdValueView{raw: it.b[:s:s]}
248+ it.b = it.b[s:]
249+ it.n--
250+ return true
251+}
252+func (it *ClientIdValueIter) Value() ClientIdValueView { return it.v }
253+func (it *ClientIdValueIter) Err() bool { return it.err }
254+
255+// ResourceSizeValueView is a zero-copy view over one wire-encoded ResourceSizeValue, yielded by ResourceSizeValueIter.
256+// It shares the lifetime of the bytes it was taken from (for replies: until the Handler callback returns). Copy what you keep.
257+type ResourceSizeValueView struct{ raw []byte }
258+
259+func (v ResourceSizeValueView) Raw() []byte { return v.raw }
260+
261+// Size is the element's wire size, including trailing alignment.
262+func (v ResourceSizeValueView) Size() int { return len(v.raw) }
263+func sizeResourceSizeValue(p []byte) int {
264+ if len(p) < 24 {
265+ return -1
266+ }
267+ v := ResourceSizeValueView{raw: p}
268+ _ = v
269+ off := 24
270+ {
271+ n := int(v.NumCrossReferences())
272+ if n < 0 || n > len(v.raw) {
273+ return -1
274+ }
275+ off += n * 20
276+ }
277+ if off > len(v.raw) {
278+ return -1
279+ }
280+ if off > len(v.raw) {
281+ return -1
282+ }
283+ return off
284+}
285+func (v ResourceSizeValueView) Size_() ResourceSizeSpec { return GetResourceSizeSpec(v.raw[0:]) }
286+func (v ResourceSizeValueView) NumCrossReferences() uint32 {
287+ return uint32(binary.LittleEndian.Uint32(v.raw[20:]))
288+}
289+func (v ResourceSizeValueView) CrossReferences() ResourceSizeSpecList {
290+ off := 24
291+ end := off + (int(v.NumCrossReferences()))*20
292+ return ResourceSizeSpecList{b: v.raw[off:end:end]}
293+}
294+
295+// ResourceSizeValueIter walks n consecutive ResourceSizeValue elements; Err reports a malformed length.
296+// It shares the lifetime of the bytes it walks.
297+type ResourceSizeValueIter struct {
298+ b []byte
299+ n int
300+ v ResourceSizeValueView
301+ err bool
302+}
303+
304+// NewResourceSizeValueIter iterates n elements encoded at the start of b.
305+func NewResourceSizeValueIter(b []byte, n int) ResourceSizeValueIter {
306+ return ResourceSizeValueIter{b: b, n: n}
307+}
308+func (it *ResourceSizeValueIter) Next() bool {
309+ if it.n <= 0 || it.err {
310+ return false
311+ }
312+ s := sizeResourceSizeValue(it.b)
313+ if s < 0 {
314+ it.err = true
315+ it.n = 0
316+ return false
317+ }
318+ it.v = ResourceSizeValueView{raw: it.b[:s:s]}
319+ it.b = it.b[s:]
320+ it.n--
321+ return true
322+}
323+func (it *ResourceSizeValueIter) Value() ResourceSizeValueView { return it.v }
324+func (it *ResourceSizeValueIter) Err() bool { return it.err }
325+
326+// ClientList is a borrowed view over a wire list of Client. It shares the lifetime of the bytes it was taken from; copy what you keep.
327+type ClientList struct{ b []byte }
328+
329+func (l ClientList) Len() int { return len(l.b) / 8 }
330+func (l ClientList) At(i int) Client { return GetClient(l.b[i*8:]) }
331+
332+// ResourceSizeSpecList is a borrowed view over a wire list of ResourceSizeSpec. It shares the lifetime of the bytes it was taken from; copy what you keep.
333+type ResourceSizeSpecList struct{ b []byte }
334+
335+func (l ResourceSizeSpecList) Len() int { return len(l.b) / 20 }
336+func (l ResourceSizeSpecList) At(i int) ResourceSizeSpec { return GetResourceSizeSpec(l.b[i*20:]) }
337+
338+// TypeList is a borrowed view over a wire list of Type. It shares the lifetime of the bytes it was taken from; copy what you keep.
339+type TypeList struct{ b []byte }
340+
341+func (l TypeList) Len() int { return len(l.b) / 8 }
342+func (l TypeList) At(i int) Type { return GetType(l.b[i*8:]) }
343+
344+// Uint32List is a borrowed view over a wire list of uint32. It shares the lifetime of the bytes it was taken from; copy what you keep.
345+type Uint32List struct{ b []byte }
346+
347+func (l Uint32List) Len() int { return len(l.b) / 4 }
348+func (l Uint32List) At(i int) uint32 { return uint32(binary.LittleEndian.Uint32(l.b[i*4:])) }
A shape/replies_fuzz_test.go +86 -0
@@ -0,0 +1,86 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package shape
3+
4+import "testing"
5+
6+func FuzzParseQueryVersionReply(f *testing.F) {
7+ f.Add(make([]byte, 32))
8+ f.Fuzz(func(t *testing.T, raw []byte) {
9+ r, ok := ParseQueryVersionReply(raw)
10+ if !ok {
11+ if len(raw) >= 32 {
12+ t.Fatal("rejected a complete fixed-size reply")
13+ }
14+ return
15+ }
16+ if len(r.Raw()) != len(raw) {
17+ t.Fatal("Raw must return the parsed bytes")
18+ }
19+ _ = r.MajorVersion()
20+ _ = r.MinorVersion()
21+ })
22+}
23+
24+func FuzzParseQueryExtentsReply(f *testing.F) {
25+ f.Add(make([]byte, 32))
26+ f.Fuzz(func(t *testing.T, raw []byte) {
27+ r, ok := ParseQueryExtentsReply(raw)
28+ if !ok {
29+ if len(raw) >= 32 {
30+ t.Fatal("rejected a complete fixed-size reply")
31+ }
32+ return
33+ }
34+ if len(r.Raw()) != len(raw) {
35+ t.Fatal("Raw must return the parsed bytes")
36+ }
37+ _ = r.BoundingShaped()
38+ _ = r.ClipShaped()
39+ _ = r.BoundingShapeExtentsX()
40+ _ = r.BoundingShapeExtentsY()
41+ _ = r.BoundingShapeExtentsWidth()
42+ _ = r.BoundingShapeExtentsHeight()
43+ _ = r.ClipShapeExtentsX()
44+ _ = r.ClipShapeExtentsY()
45+ _ = r.ClipShapeExtentsWidth()
46+ _ = r.ClipShapeExtentsHeight()
47+ })
48+}
49+
50+func FuzzParseInputSelectedReply(f *testing.F) {
51+ f.Add(make([]byte, 32))
52+ f.Fuzz(func(t *testing.T, raw []byte) {
53+ r, ok := ParseInputSelectedReply(raw)
54+ if !ok {
55+ if len(raw) >= 32 {
56+ t.Fatal("rejected a complete fixed-size reply")
57+ }
58+ return
59+ }
60+ if len(r.Raw()) != len(raw) {
61+ t.Fatal("Raw must return the parsed bytes")
62+ }
63+ _ = r.Enabled()
64+ })
65+}
66+
67+func FuzzParseGetRectanglesReply(f *testing.F) {
68+ f.Add(make([]byte, 32))
69+ f.Fuzz(func(t *testing.T, raw []byte) {
70+ r, ok := ParseGetRectanglesReply(raw)
71+ if !ok {
72+ return
73+ }
74+ if len(r.Raw()) != len(raw) {
75+ t.Fatal("Raw must return the parsed bytes")
76+ }
77+ _ = r.Ordering()
78+ _ = r.RectanglesLen()
79+ {
80+ l0 := r.Rectangles()
81+ for i := 0; i < l0.Len(); i++ {
82+ _ = l0.At(i)
83+ }
84+ }
85+ })
86+}
A shape/replies_generated.go +123 -0
@@ -0,0 +1,123 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package shape
3+
4+import "bnema.dev/xturbo/xproto"
5+import "encoding/binary"
6+
7+// QueryVersionReply is a zero-copy view over a QueryVersion reply; offsets count from reply byte 0.
8+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
9+type QueryVersionReply struct{ raw []byte }
10+
11+// ParseQueryVersionReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
12+func ParseQueryVersionReply(raw []byte) (QueryVersionReply, bool) {
13+ if len(raw) < 32 {
14+ return QueryVersionReply{}, false
15+ }
16+ r := QueryVersionReply{raw: raw}
17+ return r, true
18+}
19+func (r QueryVersionReply) Raw() []byte { return r.raw }
20+func (r QueryVersionReply) MajorVersion() uint16 {
21+ return uint16(binary.LittleEndian.Uint16(r.raw[8:]))
22+}
23+func (r QueryVersionReply) MinorVersion() uint16 {
24+ return uint16(binary.LittleEndian.Uint16(r.raw[10:]))
25+}
26+
27+// QueryExtentsReply is a zero-copy view over a QueryExtents reply; offsets count from reply byte 0.
28+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
29+type QueryExtentsReply struct{ raw []byte }
30+
31+// ParseQueryExtentsReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
32+func ParseQueryExtentsReply(raw []byte) (QueryExtentsReply, bool) {
33+ if len(raw) < 32 {
34+ return QueryExtentsReply{}, false
35+ }
36+ r := QueryExtentsReply{raw: raw}
37+ return r, true
38+}
39+func (r QueryExtentsReply) Raw() []byte { return r.raw }
40+func (r QueryExtentsReply) BoundingShaped() bool { return (r.raw[8:][0] != 0) }
41+func (r QueryExtentsReply) ClipShaped() bool { return (r.raw[9:][0] != 0) }
42+func (r QueryExtentsReply) BoundingShapeExtentsX() int16 {
43+ return int16(binary.LittleEndian.Uint16(r.raw[12:]))
44+}
45+func (r QueryExtentsReply) BoundingShapeExtentsY() int16 {
46+ return int16(binary.LittleEndian.Uint16(r.raw[14:]))
47+}
48+func (r QueryExtentsReply) BoundingShapeExtentsWidth() uint16 {
49+ return uint16(binary.LittleEndian.Uint16(r.raw[16:]))
50+}
51+func (r QueryExtentsReply) BoundingShapeExtentsHeight() uint16 {
52+ return uint16(binary.LittleEndian.Uint16(r.raw[18:]))
53+}
54+func (r QueryExtentsReply) ClipShapeExtentsX() int16 {
55+ return int16(binary.LittleEndian.Uint16(r.raw[20:]))
56+}
57+func (r QueryExtentsReply) ClipShapeExtentsY() int16 {
58+ return int16(binary.LittleEndian.Uint16(r.raw[22:]))
59+}
60+func (r QueryExtentsReply) ClipShapeExtentsWidth() uint16 {
61+ return uint16(binary.LittleEndian.Uint16(r.raw[24:]))
62+}
63+func (r QueryExtentsReply) ClipShapeExtentsHeight() uint16 {
64+ return uint16(binary.LittleEndian.Uint16(r.raw[26:]))
65+}
66+
67+// InputSelectedReply is a zero-copy view over a InputSelected reply; offsets count from reply byte 0.
68+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
69+type InputSelectedReply struct{ raw []byte }
70+
71+// ParseInputSelectedReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
72+func ParseInputSelectedReply(raw []byte) (InputSelectedReply, bool) {
73+ if len(raw) < 32 {
74+ return InputSelectedReply{}, false
75+ }
76+ r := InputSelectedReply{raw: raw}
77+ return r, true
78+}
79+func (r InputSelectedReply) Raw() []byte { return r.raw }
80+func (r InputSelectedReply) Enabled() bool { return (r.raw[1:][0] != 0) }
81+
82+// GetRectanglesReply is a zero-copy view over a GetRectangles reply; offsets count from reply byte 0.
83+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
84+type GetRectanglesReply struct{ raw []byte }
85+
86+// ParseGetRectanglesReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
87+func ParseGetRectanglesReply(raw []byte) (GetRectanglesReply, bool) {
88+ if len(raw) < 32 {
89+ return GetRectanglesReply{}, false
90+ }
91+ r := GetRectanglesReply{raw: raw}
92+ off := 32
93+ {
94+ n := int(r.RectanglesLen())
95+ if n < 0 || n > len(r.raw) {
96+ return GetRectanglesReply{}, false
97+ }
98+ off += n * 8
99+ }
100+ if off > len(r.raw) {
101+ return GetRectanglesReply{}, false
102+ }
103+ if off > len(r.raw) {
104+ return GetRectanglesReply{}, false
105+ }
106+ return r, true
107+}
108+func (r GetRectanglesReply) Raw() []byte { return r.raw }
109+func (r GetRectanglesReply) Ordering() xproto.ClipOrdering { return xproto.ClipOrdering(r.raw[1:][0]) }
110+func (r GetRectanglesReply) RectanglesLen() uint32 {
111+ return uint32(binary.LittleEndian.Uint32(r.raw[8:]))
112+}
113+func (r GetRectanglesReply) Rectangles() RectangleList {
114+ off := 32
115+ end := off + (int(r.RectanglesLen()))*8
116+ return RectangleList{b: r.raw[off:end:end]}
117+}
118+
119+// RectangleList is a borrowed view over a wire list of xproto.Rectangle. It shares the lifetime of the bytes it was taken from; copy what you keep.
120+type RectangleList struct{ b []byte }
121+
122+func (l RectangleList) Len() int { return len(l.b) / 8 }
123+func (l RectangleList) At(i int) xproto.Rectangle { return xproto.GetRectangle(l.b[i*8:]) }
A testdata/golden/render/QueryPictFormats.reply.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb reply via raw socketpair tee to an independent reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+01001200cf0200001700000001000000060000001001000000000000000000002300000001010000000000000000000000000000000001000000000024000000010800000000000000000000000000000000ff000000000025000000012000001000ff000800ff000000ff001800ff000000000026000000012000001000ff000800ff000000ff00000000000000000027000000012000000800ff001000ff001800ff000000ff000000000028000000012000000800ff001000ff001800ff00000000000000000029000000011800001000ff000800ff000000ff0000000000000000002a000000011800000000ff000800ff001000ff0000000000000000002b0000000110000008000f0004000f0000000f0000000000000000002c0000000110000000000f0004000f0008000f0000000000000000002d000000011000000a001f0005001f0000001f0000000000000000002e0000000110000000001f0005001f000a001f0000000000000000002f000000011000000a001f0005001f0000001f000f00010000000000300000000110000000001f0005001f000a001f000f0001000000000031000000011000000b001f0005003f0000001f000000000000000000320000000110000000001f0005003f000b001f000000000000000000330000000110000008000f0004000f0000000f000c000f0000000000340000000110000000000f0004000f0008000f000c000f000000000035000000012000000000ff000800ff001000ff00000000000000000036000000012000001400ff030a00ff030000ff031e0003000000000037000000012000001400ff030a00ff030000ff03000000000000000038000000012000000000ff030a00ff031400ff031e0003000000000039000000012000000000ff030a00ff031400ff03000000000000000006000000230000001800000100000000210000002900000022000000290000009102000029000000920200002900000093020000290000009402000029000000950200002900000096020000290000009702000029000000980200002900000099020000290000009a020000290000009b020000290000009c020000290000009d020000290000009e020000290000009f02000029000000a002000029000000a102000029000000a202000029000000a302000029000000a402000029000000a502000029000000a602000029000000a702000029000000a802000029000000a902000029000000aa02000029000000ab02000029000000ac02000029000000ad02000029000000ae02000029000000af02000029000000b002000029000000b102000029000000b202000029000000b302000029000000b402000029000000b502000029000000b602000029000000b702000029000000b802000029000000b902000029000000ba02000029000000bb02000029000000bc02000029000000bd02000029000000be02000029000000bf02000029000000c002000029000000c102000029000000c202000029000000c302000029000000c402000029000000c502000029000000c602000029000000c702000029000000c802000029000000c902000029000000ca02000029000000cb02000029000000cc02000029000000cd02000029000000ce02000029000000cf02000029000000d002000029000000d102000029000000d202000029000000d302000029000000d402000029000000d502000029000000d602000029000000d702000029000000d802000029000000d902000029000000da02000029000000db02000029000000dc02000029000000dd02000029000000de02000029000000df02000029000000e002000029000000e102000029000000e202000029000000e302000029000000e402000029000000e502000029000000e602000029000000e702000029000000e802000029000000e902000029000000ea02000029000000eb02000029000000ec02000029000000ed02000029000000ee02000029000000ef02000029000000f002000029000000f102000029000000f202000029000000f302000029000000f402000029000000f502000029000000f602000029000000f702000029000000f802000029000000f902000029000000fa02000029000000fb02000029000000fc02000029000000fd02000029000000fe02000029000000ff0200002900000000030000290000000103000029000000020300002900000003030000290000000403000029000000050300002900000006030000290000000703000029000000080300002900000009030000290000000a030000290000000b030000290000000c030000290000000d030000290000000e030000290000000f0300002900000010030000290000001103000029000000120300002900000013030000290000001403000029000000150300002900000016030000290000001703000029000000180300002900000019030000290000001a030000290000001b030000290000001c030000290000001d030000290000001e030000290000001f0300002900000020030000290000002103000029000000220300002900000023030000290000002403000029000000250300002900000026030000290000002703000029000000280300002900000029030000290000002a030000290000002b030000290000002c030000290000002d030000290000002e030000290000002f0300002900000030030000290000003103000029000000320300002900000033030000290000003403000029000000350300002900000036030000290000003703000029000000380300002900000039030000290000003a030000290000003b030000290000003c030000290000003d030000290000003e030000290000003f0300002900000040030000290000004103000029000000420300002900000043030000290000004403000029000000450300002900000046030000290000004703000029000000480300002900000049030000290000004a030000290000004b030000290000004c030000290000004d030000290000004e030000290000004f0300002900000050030000290000005103000029000000520300002900000053030000290000005403000029000000550300002900000056030000290000005703000029000000580300002900000059030000290000005a030000290000005b030000290000005c030000290000005d030000290000005e030000290000005f0300002900000060030000290000006103000029000000620300002900000063030000290000006403000029000000650300002900000066030000290000006703000029000000680300002900000069030000290000006a030000290000006b030000290000006c030000290000006d030000290000006e030000290000006f0300002900000070030000290000007103000029000000720300002900000073030000290000007403000029000000750300002900000076030000290000007703000029000000780300002900000079030000290000007a030000290000007b030000290000007c030000290000007d030000290000007e030000290000007f0300002900000080030000290000008103000029000000820300002900000083030000290000008403000029000000850300002900000086030000290000008703000029000000880300002900000089030000290000008a030000290000008b030000290000008c030000290000008d030000290000008e030000290000000100000000000000040000000000000008000000000000001000000000000000200010000000000040000000250000008f0300002500000090030000250000009103000025000000920300002500000093030000250000009403000025000000950300002500000096030000250000009703000025000000980300002500000099030000250000009a030000250000009b030000250000009c030000250000009d03000025000000
A testdata/golden/res/QueryClientIds.reply.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb reply via raw socketpair tee to an independent reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+01000a0006000000020000000000000000000000000000000000000000000000000000000100000000000000000020000100000000000000
A testdata/golden/xfixes/QueryVersion.reply.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb reply via raw socketpair tee to an independent reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+0100020000000000060000000000000000000000000000000000000000000000
A testdata/golden/xproto/GetGeometry.reply.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb reply via raw socketpair tee to an independent reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+01180400000000009f0300000000000020035802000000000000000000000000
A testdata/golden/xproto/GetProperty.reply.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb reply via raw socketpair tee to an independent reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+01080700010000001f000000000000000300000000000000000000000000000061626300
A testdata/golden/xproto/GetPropertyEmpty.reply.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb reply via raw socketpair tee to an independent reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+0100170000000000000000000000000000000000000000000000000000000000
A testdata/golden/xproto/GetWindowAttributes.reply.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb reply via raw socketpair tee to an independent reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+01001500030000002100000001000001ffffffff000000000101000020000000000000000000000000000000
A testdata/golden/xproto/InternAtom.reply.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb reply via raw socketpair tee to an independent reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+0100050000000000e70000000000000000000000000000000000000000000000
A testdata/golden/xproto/ListExtensions.reply.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb reply via raw socketpair tee to an independent reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+01171800370000000000000000000000000000000000000000000000000000001747656e65726963204576656e7420457874656e73696f6e055348415045074d49542d53484d0f58496e707574457874656e73696f6e0558544553540c4249472d52455155455354530453594e4309584b4559424f4152440758432d4d495343085345435552495459065846495845530652454e4445520552414e44520858494e4552414d4109436f6d706f736974650644414d414745104d49542d53435245454e2d53415645520d444f55424c452d425546464552065245434f52440750726573656e740a582d5265736f757263650658566964656f03474c5800
A testdata/golden/xproto/QueryExtension.reply.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb reply via raw socketpair tee to an independent reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+0100010000000000018a578c0000000000000000000000000000000000000000
A testdata/golden/xproto/QueryTree.reply.hex +3 -0
@@ -0,0 +1,3 @@
1+# source: Xvfb reply via raw socketpair tee to an independent reference request
2+# xtrace fallback: local xtrace is glibc's execution tracer, not X11 xtrace
3+01001600020000009f03000000000000020000000000000000000000000000000200200001002000
A xcmisc/replies_fuzz_test.go +60 -0
@@ -0,0 +1,60 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package xcmisc
3+
4+import "testing"
5+
6+func FuzzParseGetVersionReply(f *testing.F) {
7+ f.Add(make([]byte, 32))
8+ f.Fuzz(func(t *testing.T, raw []byte) {
9+ r, ok := ParseGetVersionReply(raw)
10+ if !ok {
11+ if len(raw) >= 32 {
12+ t.Fatal("rejected a complete fixed-size reply")
13+ }
14+ return
15+ }
16+ if len(r.Raw()) != len(raw) {
17+ t.Fatal("Raw must return the parsed bytes")
18+ }
19+ _ = r.ServerMajorVersion()
20+ _ = r.ServerMinorVersion()
21+ })
22+}
23+
24+func FuzzParseGetXIDRangeReply(f *testing.F) {
25+ f.Add(make([]byte, 32))
26+ f.Fuzz(func(t *testing.T, raw []byte) {
27+ r, ok := ParseGetXIDRangeReply(raw)
28+ if !ok {
29+ if len(raw) >= 32 {
30+ t.Fatal("rejected a complete fixed-size reply")
31+ }
32+ return
33+ }
34+ if len(r.Raw()) != len(raw) {
35+ t.Fatal("Raw must return the parsed bytes")
36+ }
37+ _ = r.StartId()
38+ _ = r.Count()
39+ })
40+}
41+
42+func FuzzParseGetXIDListReply(f *testing.F) {
43+ f.Add(make([]byte, 32))
44+ f.Fuzz(func(t *testing.T, raw []byte) {
45+ r, ok := ParseGetXIDListReply(raw)
46+ if !ok {
47+ return
48+ }
49+ if len(r.Raw()) != len(raw) {
50+ t.Fatal("Raw must return the parsed bytes")
51+ }
52+ _ = r.IdsLen()
53+ {
54+ l0 := r.Ids()
55+ for i := 0; i < l0.Len(); i++ {
56+ _ = l0.At(i)
57+ }
58+ }
59+ })
60+}
A xcmisc/replies_generated.go +80 -0
@@ -0,0 +1,80 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package xcmisc
3+
4+import "encoding/binary"
5+
6+// GetVersionReply is a zero-copy view over a GetVersion reply; offsets count from reply byte 0.
7+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
8+type GetVersionReply struct{ raw []byte }
9+
10+// ParseGetVersionReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
11+func ParseGetVersionReply(raw []byte) (GetVersionReply, bool) {
12+ if len(raw) < 32 {
13+ return GetVersionReply{}, false
14+ }
15+ r := GetVersionReply{raw: raw}
16+ return r, true
17+}
18+func (r GetVersionReply) Raw() []byte { return r.raw }
19+func (r GetVersionReply) ServerMajorVersion() uint16 {
20+ return uint16(binary.LittleEndian.Uint16(r.raw[8:]))
21+}
22+func (r GetVersionReply) ServerMinorVersion() uint16 {
23+ return uint16(binary.LittleEndian.Uint16(r.raw[10:]))
24+}
25+
26+// GetXIDRangeReply is a zero-copy view over a GetXIDRange reply; offsets count from reply byte 0.
27+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
28+type GetXIDRangeReply struct{ raw []byte }
29+
30+// ParseGetXIDRangeReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
31+func ParseGetXIDRangeReply(raw []byte) (GetXIDRangeReply, bool) {
32+ if len(raw) < 32 {
33+ return GetXIDRangeReply{}, false
34+ }
35+ r := GetXIDRangeReply{raw: raw}
36+ return r, true
37+}
38+func (r GetXIDRangeReply) Raw() []byte { return r.raw }
39+func (r GetXIDRangeReply) StartId() uint32 { return uint32(binary.LittleEndian.Uint32(r.raw[8:])) }
40+func (r GetXIDRangeReply) Count() uint32 { return uint32(binary.LittleEndian.Uint32(r.raw[12:])) }
41+
42+// GetXIDListReply is a zero-copy view over a GetXIDList reply; offsets count from reply byte 0.
43+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
44+type GetXIDListReply struct{ raw []byte }
45+
46+// ParseGetXIDListReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
47+func ParseGetXIDListReply(raw []byte) (GetXIDListReply, bool) {
48+ if len(raw) < 32 {
49+ return GetXIDListReply{}, false
50+ }
51+ r := GetXIDListReply{raw: raw}
52+ off := 32
53+ {
54+ n := int(r.IdsLen())
55+ if n < 0 || n > len(r.raw) {
56+ return GetXIDListReply{}, false
57+ }
58+ off += n * 4
59+ }
60+ if off > len(r.raw) {
61+ return GetXIDListReply{}, false
62+ }
63+ if off > len(r.raw) {
64+ return GetXIDListReply{}, false
65+ }
66+ return r, true
67+}
68+func (r GetXIDListReply) Raw() []byte { return r.raw }
69+func (r GetXIDListReply) IdsLen() uint32 { return uint32(binary.LittleEndian.Uint32(r.raw[8:])) }
70+func (r GetXIDListReply) Ids() Uint32List {
71+ off := 32
72+ end := off + (int(r.IdsLen()))*4
73+ return Uint32List{b: r.raw[off:end:end]}
74+}
75+
76+// Uint32List is a borrowed view over a wire list of uint32. It shares the lifetime of the bytes it was taken from; copy what you keep.
77+type Uint32List struct{ b []byte }
78+
79+func (l Uint32List) Len() int { return len(l.b) / 4 }
80+func (l Uint32List) At(i int) uint32 { return uint32(binary.LittleEndian.Uint32(l.b[i*4:])) }
A xfixes/replies_fuzz_test.go +136 -0
@@ -0,0 +1,136 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package xfixes
3+
4+import "testing"
5+import "bnema.dev/xturbo/internal/xtest"
6+
7+func FuzzParseQueryVersionReply(f *testing.F) {
8+ f.Add(make([]byte, 32))
9+ if raw, err := xtest.LoadHex("../testdata/golden/xfixes/QueryVersion.reply.hex"); err != nil {
10+ f.Fatal(err)
11+ } else {
12+ f.Add(raw)
13+ }
14+ f.Fuzz(func(t *testing.T, raw []byte) {
15+ r, ok := ParseQueryVersionReply(raw)
16+ if !ok {
17+ if len(raw) >= 32 {
18+ t.Fatal("rejected a complete fixed-size reply")
19+ }
20+ return
21+ }
22+ if len(r.Raw()) != len(raw) {
23+ t.Fatal("Raw must return the parsed bytes")
24+ }
25+ _ = r.MajorVersion()
26+ _ = r.MinorVersion()
27+ })
28+}
29+
30+func FuzzParseGetCursorImageReply(f *testing.F) {
31+ f.Add(make([]byte, 32))
32+ f.Fuzz(func(t *testing.T, raw []byte) {
33+ r, ok := ParseGetCursorImageReply(raw)
34+ if !ok {
35+ return
36+ }
37+ if len(r.Raw()) != len(raw) {
38+ t.Fatal("Raw must return the parsed bytes")
39+ }
40+ _ = r.X()
41+ _ = r.Y()
42+ _ = r.Width()
43+ _ = r.Height()
44+ _ = r.Xhot()
45+ _ = r.Yhot()
46+ _ = r.CursorSerial()
47+ {
48+ l0 := r.CursorImage()
49+ for i := 0; i < l0.Len(); i++ {
50+ _ = l0.At(i)
51+ }
52+ }
53+ })
54+}
55+
56+func FuzzParseFetchRegionReply(f *testing.F) {
57+ f.Add(make([]byte, 32))
58+ f.Fuzz(func(t *testing.T, raw []byte) {
59+ r, ok := ParseFetchRegionReply(raw)
60+ if !ok {
61+ return
62+ }
63+ if len(r.Raw()) != len(raw) {
64+ t.Fatal("Raw must return the parsed bytes")
65+ }
66+ _ = r.Extents()
67+ {
68+ l0 := r.Rectangles()
69+ for i := 0; i < l0.Len(); i++ {
70+ _ = l0.At(i)
71+ }
72+ }
73+ })
74+}
75+
76+func FuzzParseGetCursorNameReply(f *testing.F) {
77+ f.Add(make([]byte, 32))
78+ f.Fuzz(func(t *testing.T, raw []byte) {
79+ r, ok := ParseGetCursorNameReply(raw)
80+ if !ok {
81+ return
82+ }
83+ if len(r.Raw()) != len(raw) {
84+ t.Fatal("Raw must return the parsed bytes")
85+ }
86+ _ = r.Atom()
87+ _ = r.Nbytes()
88+ _ = len(r.Name())
89+ })
90+}
91+
92+func FuzzParseGetCursorImageAndNameReply(f *testing.F) {
93+ f.Add(make([]byte, 32))
94+ f.Fuzz(func(t *testing.T, raw []byte) {
95+ r, ok := ParseGetCursorImageAndNameReply(raw)
96+ if !ok {
97+ return
98+ }
99+ if len(r.Raw()) != len(raw) {
100+ t.Fatal("Raw must return the parsed bytes")
101+ }
102+ _ = r.X()
103+ _ = r.Y()
104+ _ = r.Width()
105+ _ = r.Height()
106+ _ = r.Xhot()
107+ _ = r.Yhot()
108+ _ = r.CursorSerial()
109+ _ = r.CursorAtom()
110+ _ = r.Nbytes()
111+ {
112+ l0 := r.CursorImage()
113+ for i := 0; i < l0.Len(); i++ {
114+ _ = l0.At(i)
115+ }
116+ }
117+ _ = len(r.Name())
118+ })
119+}
120+
121+func FuzzParseGetClientDisconnectModeReply(f *testing.F) {
122+ f.Add(make([]byte, 32))
123+ f.Fuzz(func(t *testing.T, raw []byte) {
124+ r, ok := ParseGetClientDisconnectModeReply(raw)
125+ if !ok {
126+ if len(raw) >= 32 {
127+ t.Fatal("rejected a complete fixed-size reply")
128+ }
129+ return
130+ }
131+ if len(r.Raw()) != len(raw) {
132+ t.Fatal("Raw must return the parsed bytes")
133+ }
134+ _ = r.DisconnectMode()
135+ })
136+}
A xfixes/replies_generated.go +242 -0
@@ -0,0 +1,242 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package xfixes
3+
4+import "bnema.dev/xturbo/xproto"
5+import "encoding/binary"
6+
7+// QueryVersionReply is a zero-copy view over a QueryVersion reply; offsets count from reply byte 0.
8+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
9+type QueryVersionReply struct{ raw []byte }
10+
11+// ParseQueryVersionReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
12+func ParseQueryVersionReply(raw []byte) (QueryVersionReply, bool) {
13+ if len(raw) < 32 {
14+ return QueryVersionReply{}, false
15+ }
16+ r := QueryVersionReply{raw: raw}
17+ return r, true
18+}
19+func (r QueryVersionReply) Raw() []byte { return r.raw }
20+func (r QueryVersionReply) MajorVersion() uint32 {
21+ return uint32(binary.LittleEndian.Uint32(r.raw[8:]))
22+}
23+func (r QueryVersionReply) MinorVersion() uint32 {
24+ return uint32(binary.LittleEndian.Uint32(r.raw[12:]))
25+}
26+
27+// GetCursorImageReply is a zero-copy view over a GetCursorImage reply; offsets count from reply byte 0.
28+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
29+type GetCursorImageReply struct{ raw []byte }
30+
31+// ParseGetCursorImageReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
32+func ParseGetCursorImageReply(raw []byte) (GetCursorImageReply, bool) {
33+ if len(raw) < 32 {
34+ return GetCursorImageReply{}, false
35+ }
36+ r := GetCursorImageReply{raw: raw}
37+ off := 32
38+ {
39+ n := (int(r.Width()) * int(r.Height()))
40+ if n < 0 || n > len(r.raw) {
41+ return GetCursorImageReply{}, false
42+ }
43+ off += n * 4
44+ }
45+ if off > len(r.raw) {
46+ return GetCursorImageReply{}, false
47+ }
48+ if off > len(r.raw) {
49+ return GetCursorImageReply{}, false
50+ }
51+ return r, true
52+}
53+func (r GetCursorImageReply) Raw() []byte { return r.raw }
54+func (r GetCursorImageReply) X() int16 { return int16(binary.LittleEndian.Uint16(r.raw[8:])) }
55+func (r GetCursorImageReply) Y() int16 { return int16(binary.LittleEndian.Uint16(r.raw[10:])) }
56+func (r GetCursorImageReply) Width() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[12:])) }
57+func (r GetCursorImageReply) Height() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[14:])) }
58+func (r GetCursorImageReply) Xhot() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[16:])) }
59+func (r GetCursorImageReply) Yhot() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[18:])) }
60+func (r GetCursorImageReply) CursorSerial() uint32 {
61+ return uint32(binary.LittleEndian.Uint32(r.raw[20:]))
62+}
63+func (r GetCursorImageReply) CursorImage() Uint32List {
64+ off := 32
65+ end := off + (int(r.Width())*int(r.Height()))*4
66+ return Uint32List{b: r.raw[off:end:end]}
67+}
68+
69+// FetchRegionReply is a zero-copy view over a FetchRegion reply; offsets count from reply byte 0.
70+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
71+type FetchRegionReply struct{ raw []byte }
72+
73+// ParseFetchRegionReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
74+func ParseFetchRegionReply(raw []byte) (FetchRegionReply, bool) {
75+ if len(raw) < 32 {
76+ return FetchRegionReply{}, false
77+ }
78+ r := FetchRegionReply{raw: raw}
79+ off := 32
80+ {
81+ n := (int(binary.LittleEndian.Uint32(r.raw[4:])) / 2)
82+ if n < 0 || n > len(r.raw) {
83+ return FetchRegionReply{}, false
84+ }
85+ off += n * 8
86+ }
87+ if off > len(r.raw) {
88+ return FetchRegionReply{}, false
89+ }
90+ if off > len(r.raw) {
91+ return FetchRegionReply{}, false
92+ }
93+ return r, true
94+}
95+func (r FetchRegionReply) Raw() []byte { return r.raw }
96+func (r FetchRegionReply) Extents() xproto.Rectangle { return xproto.GetRectangle(r.raw[8:]) }
97+func (r FetchRegionReply) Rectangles() RectangleList {
98+ off := 32
99+ end := off + (int(binary.LittleEndian.Uint32(r.raw[4:]))/2)*8
100+ return RectangleList{b: r.raw[off:end:end]}
101+}
102+
103+// GetCursorNameReply is a zero-copy view over a GetCursorName reply; offsets count from reply byte 0.
104+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
105+type GetCursorNameReply struct{ raw []byte }
106+
107+// ParseGetCursorNameReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
108+func ParseGetCursorNameReply(raw []byte) (GetCursorNameReply, bool) {
109+ if len(raw) < 32 {
110+ return GetCursorNameReply{}, false
111+ }
112+ r := GetCursorNameReply{raw: raw}
113+ off := 32
114+ {
115+ n := int(r.Nbytes())
116+ if n < 0 || n > len(r.raw) {
117+ return GetCursorNameReply{}, false
118+ }
119+ off += n * 1
120+ }
121+ if off > len(r.raw) {
122+ return GetCursorNameReply{}, false
123+ }
124+ if off > len(r.raw) {
125+ return GetCursorNameReply{}, false
126+ }
127+ return r, true
128+}
129+func (r GetCursorNameReply) Raw() []byte { return r.raw }
130+func (r GetCursorNameReply) Atom() xproto.Atom {
131+ return xproto.Atom(binary.LittleEndian.Uint32(r.raw[8:]))
132+}
133+func (r GetCursorNameReply) Nbytes() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[12:])) }
134+func (r GetCursorNameReply) Name() []byte {
135+ off := 32
136+ end := off + int(r.Nbytes())
137+ return r.raw[off:end:end]
138+}
139+
140+// GetCursorImageAndNameReply is a zero-copy view over a GetCursorImageAndName reply; offsets count from reply byte 0.
141+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
142+type GetCursorImageAndNameReply struct{ raw []byte }
143+
144+// ParseGetCursorImageAndNameReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
145+func ParseGetCursorImageAndNameReply(raw []byte) (GetCursorImageAndNameReply, bool) {
146+ if len(raw) < 32 {
147+ return GetCursorImageAndNameReply{}, false
148+ }
149+ r := GetCursorImageAndNameReply{raw: raw}
150+ off := 32
151+ {
152+ n := (int(r.Width()) * int(r.Height()))
153+ if n < 0 || n > len(r.raw) {
154+ return GetCursorImageAndNameReply{}, false
155+ }
156+ off += n * 4
157+ }
158+ if off > len(r.raw) {
159+ return GetCursorImageAndNameReply{}, false
160+ }
161+ {
162+ n := int(r.Nbytes())
163+ if n < 0 || n > len(r.raw) {
164+ return GetCursorImageAndNameReply{}, false
165+ }
166+ off += n * 1
167+ }
168+ if off > len(r.raw) {
169+ return GetCursorImageAndNameReply{}, false
170+ }
171+ if off > len(r.raw) {
172+ return GetCursorImageAndNameReply{}, false
173+ }
174+ return r, true
175+}
176+func (r GetCursorImageAndNameReply) Raw() []byte { return r.raw }
177+func (r GetCursorImageAndNameReply) X() int16 { return int16(binary.LittleEndian.Uint16(r.raw[8:])) }
178+func (r GetCursorImageAndNameReply) Y() int16 { return int16(binary.LittleEndian.Uint16(r.raw[10:])) }
179+func (r GetCursorImageAndNameReply) Width() uint16 {
180+ return uint16(binary.LittleEndian.Uint16(r.raw[12:]))
181+}
182+func (r GetCursorImageAndNameReply) Height() uint16 {
183+ return uint16(binary.LittleEndian.Uint16(r.raw[14:]))
184+}
185+func (r GetCursorImageAndNameReply) Xhot() uint16 {
186+ return uint16(binary.LittleEndian.Uint16(r.raw[16:]))
187+}
188+func (r GetCursorImageAndNameReply) Yhot() uint16 {
189+ return uint16(binary.LittleEndian.Uint16(r.raw[18:]))
190+}
191+func (r GetCursorImageAndNameReply) CursorSerial() uint32 {
192+ return uint32(binary.LittleEndian.Uint32(r.raw[20:]))
193+}
194+func (r GetCursorImageAndNameReply) CursorAtom() xproto.Atom {
195+ return xproto.Atom(binary.LittleEndian.Uint32(r.raw[24:]))
196+}
197+func (r GetCursorImageAndNameReply) Nbytes() uint16 {
198+ return uint16(binary.LittleEndian.Uint16(r.raw[28:]))
199+}
200+func (r GetCursorImageAndNameReply) CursorImage() Uint32List {
201+ off := 32
202+ end := off + (int(r.Width())*int(r.Height()))*4
203+ return Uint32List{b: r.raw[off:end:end]}
204+}
205+func (r GetCursorImageAndNameReply) Name() []byte {
206+ off := 32
207+ {
208+ n := (int(r.Width()) * int(r.Height()))
209+ off += n * 4
210+ }
211+ end := off + int(r.Nbytes())
212+ return r.raw[off:end:end]
213+}
214+
215+// GetClientDisconnectModeReply is a zero-copy view over a GetClientDisconnectMode reply; offsets count from reply byte 0.
216+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
217+type GetClientDisconnectModeReply struct{ raw []byte }
218+
219+// ParseGetClientDisconnectModeReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
220+func ParseGetClientDisconnectModeReply(raw []byte) (GetClientDisconnectModeReply, bool) {
221+ if len(raw) < 32 {
222+ return GetClientDisconnectModeReply{}, false
223+ }
224+ r := GetClientDisconnectModeReply{raw: raw}
225+ return r, true
226+}
227+func (r GetClientDisconnectModeReply) Raw() []byte { return r.raw }
228+func (r GetClientDisconnectModeReply) DisconnectMode() ClientDisconnectFlags {
229+ return ClientDisconnectFlags(binary.LittleEndian.Uint32(r.raw[8:]))
230+}
231+
232+// RectangleList is a borrowed view over a wire list of xproto.Rectangle. It shares the lifetime of the bytes it was taken from; copy what you keep.
233+type RectangleList struct{ b []byte }
234+
235+func (l RectangleList) Len() int { return len(l.b) / 8 }
236+func (l RectangleList) At(i int) xproto.Rectangle { return xproto.GetRectangle(l.b[i*8:]) }
237+
238+// Uint32List is a borrowed view over a wire list of uint32. It shares the lifetime of the bytes it was taken from; copy what you keep.
239+type Uint32List struct{ b []byte }
240+
241+func (l Uint32List) Len() int { return len(l.b) / 4 }
242+func (l Uint32List) At(i int) uint32 { return uint32(binary.LittleEndian.Uint32(l.b[i*4:])) }
A xproto/replies_fuzz_test.go +895 -0
@@ -0,0 +1,895 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package xproto
3+
4+import "testing"
5+import "bnema.dev/xturbo/internal/xtest"
6+
7+func FuzzParseGetWindowAttributesReply(f *testing.F) {
8+ f.Add(make([]byte, 44))
9+ if raw, err := xtest.LoadHex("../testdata/golden/xproto/GetWindowAttributes.reply.hex"); err != nil {
10+ f.Fatal(err)
11+ } else {
12+ f.Add(raw)
13+ }
14+ f.Fuzz(func(t *testing.T, raw []byte) {
15+ r, ok := ParseGetWindowAttributesReply(raw)
16+ if !ok {
17+ if len(raw) >= 44 {
18+ t.Fatal("rejected a complete fixed-size reply")
19+ }
20+ return
21+ }
22+ if len(r.Raw()) != len(raw) {
23+ t.Fatal("Raw must return the parsed bytes")
24+ }
25+ _ = r.BackingStore()
26+ _ = r.Visual()
27+ _ = r.Class()
28+ _ = r.BitGravity()
29+ _ = r.WinGravity()
30+ _ = r.BackingPlanes()
31+ _ = r.BackingPixel()
32+ _ = r.SaveUnder()
33+ _ = r.MapIsInstalled()
34+ _ = r.MapState()
35+ _ = r.OverrideRedirect()
36+ _ = r.Colormap()
37+ _ = r.AllEventMasks()
38+ _ = r.YourEventMask()
39+ _ = r.DoNotPropagateMask()
40+ })
41+}
42+
43+func FuzzParseGetGeometryReply(f *testing.F) {
44+ f.Add(make([]byte, 32))
45+ if raw, err := xtest.LoadHex("../testdata/golden/xproto/GetGeometry.reply.hex"); err != nil {
46+ f.Fatal(err)
47+ } else {
48+ f.Add(raw)
49+ }
50+ f.Fuzz(func(t *testing.T, raw []byte) {
51+ r, ok := ParseGetGeometryReply(raw)
52+ if !ok {
53+ if len(raw) >= 32 {
54+ t.Fatal("rejected a complete fixed-size reply")
55+ }
56+ return
57+ }
58+ if len(r.Raw()) != len(raw) {
59+ t.Fatal("Raw must return the parsed bytes")
60+ }
61+ _ = r.Depth()
62+ _ = r.Root()
63+ _ = r.X()
64+ _ = r.Y()
65+ _ = r.Width()
66+ _ = r.Height()
67+ _ = r.BorderWidth()
68+ })
69+}
70+
71+func FuzzParseQueryTreeReply(f *testing.F) {
72+ f.Add(make([]byte, 32))
73+ if raw, err := xtest.LoadHex("../testdata/golden/xproto/QueryTree.reply.hex"); err != nil {
74+ f.Fatal(err)
75+ } else {
76+ f.Add(raw)
77+ }
78+ f.Fuzz(func(t *testing.T, raw []byte) {
79+ r, ok := ParseQueryTreeReply(raw)
80+ if !ok {
81+ return
82+ }
83+ if len(r.Raw()) != len(raw) {
84+ t.Fatal("Raw must return the parsed bytes")
85+ }
86+ _ = r.Root()
87+ _ = r.Parent()
88+ _ = r.ChildrenLen()
89+ {
90+ l0 := r.Children()
91+ for i := 0; i < l0.Len(); i++ {
92+ _ = l0.At(i)
93+ }
94+ }
95+ })
96+}
97+
98+func FuzzParseInternAtomReply(f *testing.F) {
99+ f.Add(make([]byte, 32))
100+ if raw, err := xtest.LoadHex("../testdata/golden/xproto/InternAtom.reply.hex"); err != nil {
101+ f.Fatal(err)
102+ } else {
103+ f.Add(raw)
104+ }
105+ f.Fuzz(func(t *testing.T, raw []byte) {
106+ r, ok := ParseInternAtomReply(raw)
107+ if !ok {
108+ if len(raw) >= 32 {
109+ t.Fatal("rejected a complete fixed-size reply")
110+ }
111+ return
112+ }
113+ if len(r.Raw()) != len(raw) {
114+ t.Fatal("Raw must return the parsed bytes")
115+ }
116+ _ = r.Atom()
117+ })
118+}
119+
120+func FuzzParseGetAtomNameReply(f *testing.F) {
121+ f.Add(make([]byte, 32))
122+ f.Fuzz(func(t *testing.T, raw []byte) {
123+ r, ok := ParseGetAtomNameReply(raw)
124+ if !ok {
125+ return
126+ }
127+ if len(r.Raw()) != len(raw) {
128+ t.Fatal("Raw must return the parsed bytes")
129+ }
130+ _ = r.NameLen()
131+ _ = len(r.Name())
132+ })
133+}
134+
135+func FuzzParseGetPropertyReply(f *testing.F) {
136+ f.Add(make([]byte, 32))
137+ if raw, err := xtest.LoadHex("../testdata/golden/xproto/GetProperty.reply.hex"); err != nil {
138+ f.Fatal(err)
139+ } else {
140+ f.Add(raw)
141+ }
142+ if raw, err := xtest.LoadHex("../testdata/golden/xproto/GetPropertyEmpty.reply.hex"); err != nil {
143+ f.Fatal(err)
144+ } else {
145+ f.Add(raw)
146+ }
147+ f.Fuzz(func(t *testing.T, raw []byte) {
148+ r, ok := ParseGetPropertyReply(raw)
149+ if !ok {
150+ return
151+ }
152+ if len(r.Raw()) != len(raw) {
153+ t.Fatal("Raw must return the parsed bytes")
154+ }
155+ _ = r.Format()
156+ _ = r.Type()
157+ _ = r.BytesAfter()
158+ _ = r.ValueLen()
159+ _ = len(r.Value())
160+ })
161+}
162+
163+func FuzzParseListPropertiesReply(f *testing.F) {
164+ f.Add(make([]byte, 32))
165+ f.Fuzz(func(t *testing.T, raw []byte) {
166+ r, ok := ParseListPropertiesReply(raw)
167+ if !ok {
168+ return
169+ }
170+ if len(r.Raw()) != len(raw) {
171+ t.Fatal("Raw must return the parsed bytes")
172+ }
173+ _ = r.AtomsLen()
174+ {
175+ l0 := r.Atoms()
176+ for i := 0; i < l0.Len(); i++ {
177+ _ = l0.At(i)
178+ }
179+ }
180+ })
181+}
182+
183+func FuzzParseGetSelectionOwnerReply(f *testing.F) {
184+ f.Add(make([]byte, 32))
185+ f.Fuzz(func(t *testing.T, raw []byte) {
186+ r, ok := ParseGetSelectionOwnerReply(raw)
187+ if !ok {
188+ if len(raw) >= 32 {
189+ t.Fatal("rejected a complete fixed-size reply")
190+ }
191+ return
192+ }
193+ if len(r.Raw()) != len(raw) {
194+ t.Fatal("Raw must return the parsed bytes")
195+ }
196+ _ = r.Owner()
197+ })
198+}
199+
200+func FuzzParseGrabPointerReply(f *testing.F) {
201+ f.Add(make([]byte, 32))
202+ f.Fuzz(func(t *testing.T, raw []byte) {
203+ r, ok := ParseGrabPointerReply(raw)
204+ if !ok {
205+ if len(raw) >= 32 {
206+ t.Fatal("rejected a complete fixed-size reply")
207+ }
208+ return
209+ }
210+ if len(r.Raw()) != len(raw) {
211+ t.Fatal("Raw must return the parsed bytes")
212+ }
213+ _ = r.Status()
214+ })
215+}
216+
217+func FuzzParseGrabKeyboardReply(f *testing.F) {
218+ f.Add(make([]byte, 32))
219+ f.Fuzz(func(t *testing.T, raw []byte) {
220+ r, ok := ParseGrabKeyboardReply(raw)
221+ if !ok {
222+ if len(raw) >= 32 {
223+ t.Fatal("rejected a complete fixed-size reply")
224+ }
225+ return
226+ }
227+ if len(r.Raw()) != len(raw) {
228+ t.Fatal("Raw must return the parsed bytes")
229+ }
230+ _ = r.Status()
231+ })
232+}
233+
234+func FuzzParseQueryPointerReply(f *testing.F) {
235+ f.Add(make([]byte, 32))
236+ f.Fuzz(func(t *testing.T, raw []byte) {
237+ r, ok := ParseQueryPointerReply(raw)
238+ if !ok {
239+ if len(raw) >= 32 {
240+ t.Fatal("rejected a complete fixed-size reply")
241+ }
242+ return
243+ }
244+ if len(r.Raw()) != len(raw) {
245+ t.Fatal("Raw must return the parsed bytes")
246+ }
247+ _ = r.SameScreen()
248+ _ = r.Root()
249+ _ = r.Child()
250+ _ = r.RootX()
251+ _ = r.RootY()
252+ _ = r.WinX()
253+ _ = r.WinY()
254+ _ = r.Mask()
255+ })
256+}
257+
258+func FuzzParseGetMotionEventsReply(f *testing.F) {
259+ f.Add(make([]byte, 32))
260+ f.Fuzz(func(t *testing.T, raw []byte) {
261+ r, ok := ParseGetMotionEventsReply(raw)
262+ if !ok {
263+ return
264+ }
265+ if len(r.Raw()) != len(raw) {
266+ t.Fatal("Raw must return the parsed bytes")
267+ }
268+ _ = r.EventsLen()
269+ {
270+ l0 := r.Events()
271+ for i := 0; i < l0.Len(); i++ {
272+ _ = l0.At(i)
273+ }
274+ }
275+ })
276+}
277+
278+func FuzzParseTranslateCoordinatesReply(f *testing.F) {
279+ f.Add(make([]byte, 32))
280+ f.Fuzz(func(t *testing.T, raw []byte) {
281+ r, ok := ParseTranslateCoordinatesReply(raw)
282+ if !ok {
283+ if len(raw) >= 32 {
284+ t.Fatal("rejected a complete fixed-size reply")
285+ }
286+ return
287+ }
288+ if len(r.Raw()) != len(raw) {
289+ t.Fatal("Raw must return the parsed bytes")
290+ }
291+ _ = r.SameScreen()
292+ _ = r.Child()
293+ _ = r.DstX()
294+ _ = r.DstY()
295+ })
296+}
297+
298+func FuzzParseGetInputFocusReply(f *testing.F) {
299+ f.Add(make([]byte, 32))
300+ f.Fuzz(func(t *testing.T, raw []byte) {
301+ r, ok := ParseGetInputFocusReply(raw)
302+ if !ok {
303+ if len(raw) >= 32 {
304+ t.Fatal("rejected a complete fixed-size reply")
305+ }
306+ return
307+ }
308+ if len(r.Raw()) != len(raw) {
309+ t.Fatal("Raw must return the parsed bytes")
310+ }
311+ _ = r.RevertTo()
312+ _ = r.Focus()
313+ })
314+}
315+
316+func FuzzParseQueryKeymapReply(f *testing.F) {
317+ f.Add(make([]byte, 40))
318+ f.Fuzz(func(t *testing.T, raw []byte) {
319+ r, ok := ParseQueryKeymapReply(raw)
320+ if !ok {
321+ if len(raw) >= 40 {
322+ t.Fatal("rejected a complete fixed-size reply")
323+ }
324+ return
325+ }
326+ if len(r.Raw()) != len(raw) {
327+ t.Fatal("Raw must return the parsed bytes")
328+ }
329+ _ = len(r.Keys())
330+ })
331+}
332+
333+func FuzzParseQueryFontReply(f *testing.F) {
334+ f.Add(make([]byte, 60))
335+ f.Fuzz(func(t *testing.T, raw []byte) {
336+ r, ok := ParseQueryFontReply(raw)
337+ if !ok {
338+ return
339+ }
340+ if len(r.Raw()) != len(raw) {
341+ t.Fatal("Raw must return the parsed bytes")
342+ }
343+ _ = r.MinBounds()
344+ _ = r.MaxBounds()
345+ _ = r.MinCharOrByte2()
346+ _ = r.MaxCharOrByte2()
347+ _ = r.DefaultChar()
348+ _ = r.PropertiesLen()
349+ _ = r.DrawDirection()
350+ _ = r.MinByte1()
351+ _ = r.MaxByte1()
352+ _ = r.AllCharsExist()
353+ _ = r.FontAscent()
354+ _ = r.FontDescent()
355+ _ = r.CharInfosLen()
356+ {
357+ l0 := r.Properties()
358+ for i := 0; i < l0.Len(); i++ {
359+ _ = l0.At(i)
360+ }
361+ }
362+ {
363+ l0 := r.CharInfos()
364+ for i := 0; i < l0.Len(); i++ {
365+ _ = l0.At(i)
366+ }
367+ }
368+ })
369+}
370+
371+func FuzzParseQueryTextExtentsReply(f *testing.F) {
372+ f.Add(make([]byte, 32))
373+ f.Fuzz(func(t *testing.T, raw []byte) {
374+ r, ok := ParseQueryTextExtentsReply(raw)
375+ if !ok {
376+ if len(raw) >= 32 {
377+ t.Fatal("rejected a complete fixed-size reply")
378+ }
379+ return
380+ }
381+ if len(r.Raw()) != len(raw) {
382+ t.Fatal("Raw must return the parsed bytes")
383+ }
384+ _ = r.DrawDirection()
385+ _ = r.FontAscent()
386+ _ = r.FontDescent()
387+ _ = r.OverallAscent()
388+ _ = r.OverallDescent()
389+ _ = r.OverallWidth()
390+ _ = r.OverallLeft()
391+ _ = r.OverallRight()
392+ })
393+}
394+
395+func FuzzParseListFontsReply(f *testing.F) {
396+ f.Add(make([]byte, 32))
397+ f.Fuzz(func(t *testing.T, raw []byte) {
398+ r, ok := ParseListFontsReply(raw)
399+ if !ok {
400+ return
401+ }
402+ if len(r.Raw()) != len(raw) {
403+ t.Fatal("Raw must return the parsed bytes")
404+ }
405+ _ = r.NamesLen()
406+ {
407+ it0 := r.Names()
408+ for it0.Next() {
409+ _ = it0.Value().NameLen()
410+ _ = len(it0.Value().Name())
411+ }
412+ if it0.Err() {
413+ t.Fatal("iterator error after successful Parse")
414+ }
415+ }
416+ })
417+}
418+
419+func FuzzParseListFontsWithInfoReply(f *testing.F) {
420+ f.Add(make([]byte, 60))
421+ f.Fuzz(func(t *testing.T, raw []byte) {
422+ r, ok := ParseListFontsWithInfoReply(raw)
423+ if !ok {
424+ return
425+ }
426+ if len(r.Raw()) != len(raw) {
427+ t.Fatal("Raw must return the parsed bytes")
428+ }
429+ _ = r.NameLen()
430+ _ = r.MinBounds()
431+ _ = r.MaxBounds()
432+ _ = r.MinCharOrByte2()
433+ _ = r.MaxCharOrByte2()
434+ _ = r.DefaultChar()
435+ _ = r.PropertiesLen()
436+ _ = r.DrawDirection()
437+ _ = r.MinByte1()
438+ _ = r.MaxByte1()
439+ _ = r.AllCharsExist()
440+ _ = r.FontAscent()
441+ _ = r.FontDescent()
442+ _ = r.RepliesHint()
443+ {
444+ l0 := r.Properties()
445+ for i := 0; i < l0.Len(); i++ {
446+ _ = l0.At(i)
447+ }
448+ }
449+ _ = len(r.Name())
450+ })
451+}
452+
453+func FuzzParseGetFontPathReply(f *testing.F) {
454+ f.Add(make([]byte, 32))
455+ f.Fuzz(func(t *testing.T, raw []byte) {
456+ r, ok := ParseGetFontPathReply(raw)
457+ if !ok {
458+ return
459+ }
460+ if len(r.Raw()) != len(raw) {
461+ t.Fatal("Raw must return the parsed bytes")
462+ }
463+ _ = r.PathLen()
464+ {
465+ it0 := r.Path()
466+ for it0.Next() {
467+ _ = it0.Value().NameLen()
468+ _ = len(it0.Value().Name())
469+ }
470+ if it0.Err() {
471+ t.Fatal("iterator error after successful Parse")
472+ }
473+ }
474+ })
475+}
476+
477+func FuzzParseGetImageReply(f *testing.F) {
478+ f.Add(make([]byte, 32))
479+ f.Fuzz(func(t *testing.T, raw []byte) {
480+ r, ok := ParseGetImageReply(raw)
481+ if !ok {
482+ return
483+ }
484+ if len(r.Raw()) != len(raw) {
485+ t.Fatal("Raw must return the parsed bytes")
486+ }
487+ _ = r.Depth()
488+ _ = r.Visual()
489+ _ = len(r.Data())
490+ })
491+}
492+
493+func FuzzParseListInstalledColormapsReply(f *testing.F) {
494+ f.Add(make([]byte, 32))
495+ f.Fuzz(func(t *testing.T, raw []byte) {
496+ r, ok := ParseListInstalledColormapsReply(raw)
497+ if !ok {
498+ return
499+ }
500+ if len(r.Raw()) != len(raw) {
501+ t.Fatal("Raw must return the parsed bytes")
502+ }
503+ _ = r.CmapsLen()
504+ {
505+ l0 := r.Cmaps()
506+ for i := 0; i < l0.Len(); i++ {
507+ _ = l0.At(i)
508+ }
509+ }
510+ })
511+}
512+
513+func FuzzParseAllocColorReply(f *testing.F) {
514+ f.Add(make([]byte, 32))
515+ f.Fuzz(func(t *testing.T, raw []byte) {
516+ r, ok := ParseAllocColorReply(raw)
517+ if !ok {
518+ if len(raw) >= 32 {
519+ t.Fatal("rejected a complete fixed-size reply")
520+ }
521+ return
522+ }
523+ if len(r.Raw()) != len(raw) {
524+ t.Fatal("Raw must return the parsed bytes")
525+ }
526+ _ = r.Red()
527+ _ = r.Green()
528+ _ = r.Blue()
529+ _ = r.Pixel()
530+ })
531+}
532+
533+func FuzzParseAllocNamedColorReply(f *testing.F) {
534+ f.Add(make([]byte, 32))
535+ f.Fuzz(func(t *testing.T, raw []byte) {
536+ r, ok := ParseAllocNamedColorReply(raw)
537+ if !ok {
538+ if len(raw) >= 32 {
539+ t.Fatal("rejected a complete fixed-size reply")
540+ }
541+ return
542+ }
543+ if len(r.Raw()) != len(raw) {
544+ t.Fatal("Raw must return the parsed bytes")
545+ }
546+ _ = r.Pixel()
547+ _ = r.ExactRed()
548+ _ = r.ExactGreen()
549+ _ = r.ExactBlue()
550+ _ = r.VisualRed()
551+ _ = r.VisualGreen()
552+ _ = r.VisualBlue()
553+ })
554+}
555+
556+func FuzzParseAllocColorCellsReply(f *testing.F) {
557+ f.Add(make([]byte, 32))
558+ f.Fuzz(func(t *testing.T, raw []byte) {
559+ r, ok := ParseAllocColorCellsReply(raw)
560+ if !ok {
561+ return
562+ }
563+ if len(r.Raw()) != len(raw) {
564+ t.Fatal("Raw must return the parsed bytes")
565+ }
566+ _ = r.PixelsLen()
567+ _ = r.MasksLen()
568+ {
569+ l0 := r.Pixels()
570+ for i := 0; i < l0.Len(); i++ {
571+ _ = l0.At(i)
572+ }
573+ }
574+ {
575+ l0 := r.Masks()
576+ for i := 0; i < l0.Len(); i++ {
577+ _ = l0.At(i)
578+ }
579+ }
580+ })
581+}
582+
583+func FuzzParseAllocColorPlanesReply(f *testing.F) {
584+ f.Add(make([]byte, 32))
585+ f.Fuzz(func(t *testing.T, raw []byte) {
586+ r, ok := ParseAllocColorPlanesReply(raw)
587+ if !ok {
588+ return
589+ }
590+ if len(r.Raw()) != len(raw) {
591+ t.Fatal("Raw must return the parsed bytes")
592+ }
593+ _ = r.PixelsLen()
594+ _ = r.RedMask()
595+ _ = r.GreenMask()
596+ _ = r.BlueMask()
597+ {
598+ l0 := r.Pixels()
599+ for i := 0; i < l0.Len(); i++ {
600+ _ = l0.At(i)
601+ }
602+ }
603+ })
604+}
605+
606+func FuzzParseQueryColorsReply(f *testing.F) {
607+ f.Add(make([]byte, 32))
608+ f.Fuzz(func(t *testing.T, raw []byte) {
609+ r, ok := ParseQueryColorsReply(raw)
610+ if !ok {
611+ return
612+ }
613+ if len(r.Raw()) != len(raw) {
614+ t.Fatal("Raw must return the parsed bytes")
615+ }
616+ _ = r.ColorsLen()
617+ {
618+ l0 := r.Colors()
619+ for i := 0; i < l0.Len(); i++ {
620+ _ = l0.At(i)
621+ }
622+ }
623+ })
624+}
625+
626+func FuzzParseLookupColorReply(f *testing.F) {
627+ f.Add(make([]byte, 32))
628+ f.Fuzz(func(t *testing.T, raw []byte) {
629+ r, ok := ParseLookupColorReply(raw)
630+ if !ok {
631+ if len(raw) >= 32 {
632+ t.Fatal("rejected a complete fixed-size reply")
633+ }
634+ return
635+ }
636+ if len(r.Raw()) != len(raw) {
637+ t.Fatal("Raw must return the parsed bytes")
638+ }
639+ _ = r.ExactRed()
640+ _ = r.ExactGreen()
641+ _ = r.ExactBlue()
642+ _ = r.VisualRed()
643+ _ = r.VisualGreen()
644+ _ = r.VisualBlue()
645+ })
646+}
647+
648+func FuzzParseQueryBestSizeReply(f *testing.F) {
649+ f.Add(make([]byte, 32))
650+ f.Fuzz(func(t *testing.T, raw []byte) {
651+ r, ok := ParseQueryBestSizeReply(raw)
652+ if !ok {
653+ if len(raw) >= 32 {
654+ t.Fatal("rejected a complete fixed-size reply")
655+ }
656+ return
657+ }
658+ if len(r.Raw()) != len(raw) {
659+ t.Fatal("Raw must return the parsed bytes")
660+ }
661+ _ = r.Width()
662+ _ = r.Height()
663+ })
664+}
665+
666+func FuzzParseQueryExtensionReply(f *testing.F) {
667+ f.Add(make([]byte, 32))
668+ if raw, err := xtest.LoadHex("../testdata/golden/xproto/QueryExtension.reply.hex"); err != nil {
669+ f.Fatal(err)
670+ } else {
671+ f.Add(raw)
672+ }
673+ f.Fuzz(func(t *testing.T, raw []byte) {
674+ r, ok := ParseQueryExtensionReply(raw)
675+ if !ok {
676+ if len(raw) >= 32 {
677+ t.Fatal("rejected a complete fixed-size reply")
678+ }
679+ return
680+ }
681+ if len(r.Raw()) != len(raw) {
682+ t.Fatal("Raw must return the parsed bytes")
683+ }
684+ _ = r.Present()
685+ _ = r.MajorOpcode()
686+ _ = r.FirstEvent()
687+ _ = r.FirstError()
688+ })
689+}
690+
691+func FuzzParseListExtensionsReply(f *testing.F) {
692+ f.Add(make([]byte, 32))
693+ if raw, err := xtest.LoadHex("../testdata/golden/xproto/ListExtensions.reply.hex"); err != nil {
694+ f.Fatal(err)
695+ } else {
696+ f.Add(raw)
697+ }
698+ f.Fuzz(func(t *testing.T, raw []byte) {
699+ r, ok := ParseListExtensionsReply(raw)
700+ if !ok {
701+ return
702+ }
703+ if len(r.Raw()) != len(raw) {
704+ t.Fatal("Raw must return the parsed bytes")
705+ }
706+ _ = r.NamesLen()
707+ {
708+ it0 := r.Names()
709+ for it0.Next() {
710+ _ = it0.Value().NameLen()
711+ _ = len(it0.Value().Name())
712+ }
713+ if it0.Err() {
714+ t.Fatal("iterator error after successful Parse")
715+ }
716+ }
717+ })
718+}
719+
720+func FuzzParseGetKeyboardMappingReply(f *testing.F) {
721+ f.Add(make([]byte, 32))
722+ f.Fuzz(func(t *testing.T, raw []byte) {
723+ r, ok := ParseGetKeyboardMappingReply(raw)
724+ if !ok {
725+ return
726+ }
727+ if len(r.Raw()) != len(raw) {
728+ t.Fatal("Raw must return the parsed bytes")
729+ }
730+ _ = r.KeysymsPerKeycode()
731+ {
732+ l0 := r.Keysyms()
733+ for i := 0; i < l0.Len(); i++ {
734+ _ = l0.At(i)
735+ }
736+ }
737+ })
738+}
739+
740+func FuzzParseGetKeyboardControlReply(f *testing.F) {
741+ f.Add(make([]byte, 52))
742+ f.Fuzz(func(t *testing.T, raw []byte) {
743+ r, ok := ParseGetKeyboardControlReply(raw)
744+ if !ok {
745+ if len(raw) >= 52 {
746+ t.Fatal("rejected a complete fixed-size reply")
747+ }
748+ return
749+ }
750+ if len(r.Raw()) != len(raw) {
751+ t.Fatal("Raw must return the parsed bytes")
752+ }
753+ _ = r.GlobalAutoRepeat()
754+ _ = r.LedMask()
755+ _ = r.KeyClickPercent()
756+ _ = r.BellPercent()
757+ _ = r.BellPitch()
758+ _ = r.BellDuration()
759+ _ = len(r.AutoRepeats())
760+ })
761+}
762+
763+func FuzzParseGetPointerControlReply(f *testing.F) {
764+ f.Add(make([]byte, 32))
765+ f.Fuzz(func(t *testing.T, raw []byte) {
766+ r, ok := ParseGetPointerControlReply(raw)
767+ if !ok {
768+ if len(raw) >= 32 {
769+ t.Fatal("rejected a complete fixed-size reply")
770+ }
771+ return
772+ }
773+ if len(r.Raw()) != len(raw) {
774+ t.Fatal("Raw must return the parsed bytes")
775+ }
776+ _ = r.AccelerationNumerator()
777+ _ = r.AccelerationDenominator()
778+ _ = r.Threshold()
779+ })
780+}
781+
782+func FuzzParseGetScreenSaverReply(f *testing.F) {
783+ f.Add(make([]byte, 32))
784+ f.Fuzz(func(t *testing.T, raw []byte) {
785+ r, ok := ParseGetScreenSaverReply(raw)
786+ if !ok {
787+ if len(raw) >= 32 {
788+ t.Fatal("rejected a complete fixed-size reply")
789+ }
790+ return
791+ }
792+ if len(r.Raw()) != len(raw) {
793+ t.Fatal("Raw must return the parsed bytes")
794+ }
795+ _ = r.Timeout()
796+ _ = r.Interval()
797+ _ = r.PreferBlanking()
798+ _ = r.AllowExposures()
799+ })
800+}
801+
802+func FuzzParseListHostsReply(f *testing.F) {
803+ f.Add(make([]byte, 32))
804+ f.Fuzz(func(t *testing.T, raw []byte) {
805+ r, ok := ParseListHostsReply(raw)
806+ if !ok {
807+ return
808+ }
809+ if len(r.Raw()) != len(raw) {
810+ t.Fatal("Raw must return the parsed bytes")
811+ }
812+ _ = r.Mode()
813+ _ = r.HostsLen()
814+ {
815+ it0 := r.Hosts()
816+ for it0.Next() {
817+ _ = it0.Value().Family()
818+ _ = it0.Value().AddressLen()
819+ _ = len(it0.Value().Address())
820+ }
821+ if it0.Err() {
822+ t.Fatal("iterator error after successful Parse")
823+ }
824+ }
825+ })
826+}
827+
828+func FuzzParseSetPointerMappingReply(f *testing.F) {
829+ f.Add(make([]byte, 32))
830+ f.Fuzz(func(t *testing.T, raw []byte) {
831+ r, ok := ParseSetPointerMappingReply(raw)
832+ if !ok {
833+ if len(raw) >= 32 {
834+ t.Fatal("rejected a complete fixed-size reply")
835+ }
836+ return
837+ }
838+ if len(r.Raw()) != len(raw) {
839+ t.Fatal("Raw must return the parsed bytes")
840+ }
841+ _ = r.Status()
842+ })
843+}
844+
845+func FuzzParseGetPointerMappingReply(f *testing.F) {
846+ f.Add(make([]byte, 32))
847+ f.Fuzz(func(t *testing.T, raw []byte) {
848+ r, ok := ParseGetPointerMappingReply(raw)
849+ if !ok {
850+ return
851+ }
852+ if len(r.Raw()) != len(raw) {
853+ t.Fatal("Raw must return the parsed bytes")
854+ }
855+ _ = r.MapLen()
856+ _ = len(r.Map())
857+ })
858+}
859+
860+func FuzzParseSetModifierMappingReply(f *testing.F) {
861+ f.Add(make([]byte, 32))
862+ f.Fuzz(func(t *testing.T, raw []byte) {
863+ r, ok := ParseSetModifierMappingReply(raw)
864+ if !ok {
865+ if len(raw) >= 32 {
866+ t.Fatal("rejected a complete fixed-size reply")
867+ }
868+ return
869+ }
870+ if len(r.Raw()) != len(raw) {
871+ t.Fatal("Raw must return the parsed bytes")
872+ }
873+ _ = r.Status()
874+ })
875+}
876+
877+func FuzzParseGetModifierMappingReply(f *testing.F) {
878+ f.Add(make([]byte, 32))
879+ f.Fuzz(func(t *testing.T, raw []byte) {
880+ r, ok := ParseGetModifierMappingReply(raw)
881+ if !ok {
882+ return
883+ }
884+ if len(r.Raw()) != len(raw) {
885+ t.Fatal("Raw must return the parsed bytes")
886+ }
887+ _ = r.KeycodesPerModifier()
888+ {
889+ l0 := r.Keycodes()
890+ for i := 0; i < l0.Len(); i++ {
891+ _ = l0.At(i)
892+ }
893+ }
894+ })
895+}
A xproto/replies_generated.go +1964 -0
@@ -0,0 +1,1964 @@
1+// Code generated by xtgen; DO NOT EDIT.
2+package xproto
3+
4+import "encoding/binary"
5+
6+// GetWindowAttributesReply is a zero-copy view over a GetWindowAttributes reply; offsets count from reply byte 0.
7+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
8+type GetWindowAttributesReply struct{ raw []byte }
9+
10+// ParseGetWindowAttributesReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
11+func ParseGetWindowAttributesReply(raw []byte) (GetWindowAttributesReply, bool) {
12+ if len(raw) < 44 {
13+ return GetWindowAttributesReply{}, false
14+ }
15+ r := GetWindowAttributesReply{raw: raw}
16+ return r, true
17+}
18+func (r GetWindowAttributesReply) Raw() []byte { return r.raw }
19+func (r GetWindowAttributesReply) BackingStore() BackingStore { return BackingStore(r.raw[1:][0]) }
20+func (r GetWindowAttributesReply) Visual() Visualid {
21+ return Visualid(binary.LittleEndian.Uint32(r.raw[8:]))
22+}
23+func (r GetWindowAttributesReply) Class() WindowClass {
24+ return WindowClass(binary.LittleEndian.Uint16(r.raw[12:]))
25+}
26+func (r GetWindowAttributesReply) BitGravity() Gravity { return Gravity(r.raw[14:][0]) }
27+func (r GetWindowAttributesReply) WinGravity() Gravity { return Gravity(r.raw[15:][0]) }
28+func (r GetWindowAttributesReply) BackingPlanes() uint32 {
29+ return uint32(binary.LittleEndian.Uint32(r.raw[16:]))
30+}
31+func (r GetWindowAttributesReply) BackingPixel() uint32 {
32+ return uint32(binary.LittleEndian.Uint32(r.raw[20:]))
33+}
34+func (r GetWindowAttributesReply) SaveUnder() bool { return (r.raw[24:][0] != 0) }
35+func (r GetWindowAttributesReply) MapIsInstalled() bool { return (r.raw[25:][0] != 0) }
36+func (r GetWindowAttributesReply) MapState() MapState { return MapState(r.raw[26:][0]) }
37+func (r GetWindowAttributesReply) OverrideRedirect() bool { return (r.raw[27:][0] != 0) }
38+func (r GetWindowAttributesReply) Colormap() Colormap {
39+ return Colormap(binary.LittleEndian.Uint32(r.raw[28:]))
40+}
41+func (r GetWindowAttributesReply) AllEventMasks() EventMask {
42+ return EventMask(binary.LittleEndian.Uint32(r.raw[32:]))
43+}
44+func (r GetWindowAttributesReply) YourEventMask() EventMask {
45+ return EventMask(binary.LittleEndian.Uint32(r.raw[36:]))
46+}
47+func (r GetWindowAttributesReply) DoNotPropagateMask() EventMask {
48+ return EventMask(binary.LittleEndian.Uint16(r.raw[40:]))
49+}
50+
51+// GetGeometryReply is a zero-copy view over a GetGeometry reply; offsets count from reply byte 0.
52+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
53+type GetGeometryReply struct{ raw []byte }
54+
55+// ParseGetGeometryReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
56+func ParseGetGeometryReply(raw []byte) (GetGeometryReply, bool) {
57+ if len(raw) < 32 {
58+ return GetGeometryReply{}, false
59+ }
60+ r := GetGeometryReply{raw: raw}
61+ return r, true
62+}
63+func (r GetGeometryReply) Raw() []byte { return r.raw }
64+func (r GetGeometryReply) Depth() uint8 { return uint8(r.raw[1:][0]) }
65+func (r GetGeometryReply) Root() Window { return Window(binary.LittleEndian.Uint32(r.raw[8:])) }
66+func (r GetGeometryReply) X() int16 { return int16(binary.LittleEndian.Uint16(r.raw[12:])) }
67+func (r GetGeometryReply) Y() int16 { return int16(binary.LittleEndian.Uint16(r.raw[14:])) }
68+func (r GetGeometryReply) Width() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[16:])) }
69+func (r GetGeometryReply) Height() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[18:])) }
70+func (r GetGeometryReply) BorderWidth() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[20:])) }
71+
72+// QueryTreeReply is a zero-copy view over a QueryTree reply; offsets count from reply byte 0.
73+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
74+type QueryTreeReply struct{ raw []byte }
75+
76+// ParseQueryTreeReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
77+func ParseQueryTreeReply(raw []byte) (QueryTreeReply, bool) {
78+ if len(raw) < 32 {
79+ return QueryTreeReply{}, false
80+ }
81+ r := QueryTreeReply{raw: raw}
82+ off := 32
83+ {
84+ n := int(r.ChildrenLen())
85+ if n < 0 || n > len(r.raw) {
86+ return QueryTreeReply{}, false
87+ }
88+ off += n * 4
89+ }
90+ if off > len(r.raw) {
91+ return QueryTreeReply{}, false
92+ }
93+ if off > len(r.raw) {
94+ return QueryTreeReply{}, false
95+ }
96+ return r, true
97+}
98+func (r QueryTreeReply) Raw() []byte { return r.raw }
99+func (r QueryTreeReply) Root() Window { return Window(binary.LittleEndian.Uint32(r.raw[8:])) }
100+func (r QueryTreeReply) Parent() Window { return Window(binary.LittleEndian.Uint32(r.raw[12:])) }
101+func (r QueryTreeReply) ChildrenLen() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[16:])) }
102+func (r QueryTreeReply) Children() WindowList {
103+ off := 32
104+ end := off + (int(r.ChildrenLen()))*4
105+ return WindowList{b: r.raw[off:end:end]}
106+}
107+
108+// InternAtomReply is a zero-copy view over a InternAtom reply; offsets count from reply byte 0.
109+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
110+type InternAtomReply struct{ raw []byte }
111+
112+// ParseInternAtomReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
113+func ParseInternAtomReply(raw []byte) (InternAtomReply, bool) {
114+ if len(raw) < 32 {
115+ return InternAtomReply{}, false
116+ }
117+ r := InternAtomReply{raw: raw}
118+ return r, true
119+}
120+func (r InternAtomReply) Raw() []byte { return r.raw }
121+func (r InternAtomReply) Atom() Atom { return Atom(binary.LittleEndian.Uint32(r.raw[8:])) }
122+
123+// GetAtomNameReply is a zero-copy view over a GetAtomName reply; offsets count from reply byte 0.
124+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
125+type GetAtomNameReply struct{ raw []byte }
126+
127+// ParseGetAtomNameReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
128+func ParseGetAtomNameReply(raw []byte) (GetAtomNameReply, bool) {
129+ if len(raw) < 32 {
130+ return GetAtomNameReply{}, false
131+ }
132+ r := GetAtomNameReply{raw: raw}
133+ off := 32
134+ {
135+ n := int(r.NameLen())
136+ if n < 0 || n > len(r.raw) {
137+ return GetAtomNameReply{}, false
138+ }
139+ off += n * 1
140+ }
141+ if off > len(r.raw) {
142+ return GetAtomNameReply{}, false
143+ }
144+ if off > len(r.raw) {
145+ return GetAtomNameReply{}, false
146+ }
147+ return r, true
148+}
149+func (r GetAtomNameReply) Raw() []byte { return r.raw }
150+func (r GetAtomNameReply) NameLen() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[8:])) }
151+func (r GetAtomNameReply) Name() []byte {
152+ off := 32
153+ end := off + int(r.NameLen())
154+ return r.raw[off:end:end]
155+}
156+
157+// GetPropertyReply is a zero-copy view over a GetProperty reply; offsets count from reply byte 0.
158+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
159+type GetPropertyReply struct{ raw []byte }
160+
161+// ParseGetPropertyReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
162+func ParseGetPropertyReply(raw []byte) (GetPropertyReply, bool) {
163+ if len(raw) < 32 {
164+ return GetPropertyReply{}, false
165+ }
166+ r := GetPropertyReply{raw: raw}
167+ off := 32
168+ {
169+ n := (int(r.ValueLen()) * (int(r.Format()) / 8))
170+ if n < 0 || n > len(r.raw) {
171+ return GetPropertyReply{}, false
172+ }
173+ off += n * 1
174+ }
175+ if off > len(r.raw) {
176+ return GetPropertyReply{}, false
177+ }
178+ if off > len(r.raw) {
179+ return GetPropertyReply{}, false
180+ }
181+ return r, true
182+}
183+func (r GetPropertyReply) Raw() []byte { return r.raw }
184+func (r GetPropertyReply) Format() uint8 { return uint8(r.raw[1:][0]) }
185+func (r GetPropertyReply) Type() Atom { return Atom(binary.LittleEndian.Uint32(r.raw[8:])) }
186+func (r GetPropertyReply) BytesAfter() uint32 { return uint32(binary.LittleEndian.Uint32(r.raw[12:])) }
187+func (r GetPropertyReply) ValueLen() uint32 { return uint32(binary.LittleEndian.Uint32(r.raw[16:])) }
188+func (r GetPropertyReply) Value() []byte {
189+ off := 32
190+ end := off + (int(r.ValueLen()) * (int(r.Format()) / 8))
191+ return r.raw[off:end:end]
192+}
193+
194+// ListPropertiesReply is a zero-copy view over a ListProperties reply; offsets count from reply byte 0.
195+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
196+type ListPropertiesReply struct{ raw []byte }
197+
198+// ParseListPropertiesReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
199+func ParseListPropertiesReply(raw []byte) (ListPropertiesReply, bool) {
200+ if len(raw) < 32 {
201+ return ListPropertiesReply{}, false
202+ }
203+ r := ListPropertiesReply{raw: raw}
204+ off := 32
205+ {
206+ n := int(r.AtomsLen())
207+ if n < 0 || n > len(r.raw) {
208+ return ListPropertiesReply{}, false
209+ }
210+ off += n * 4
211+ }
212+ if off > len(r.raw) {
213+ return ListPropertiesReply{}, false
214+ }
215+ if off > len(r.raw) {
216+ return ListPropertiesReply{}, false
217+ }
218+ return r, true
219+}
220+func (r ListPropertiesReply) Raw() []byte { return r.raw }
221+func (r ListPropertiesReply) AtomsLen() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[8:])) }
222+func (r ListPropertiesReply) Atoms() AtomList {
223+ off := 32
224+ end := off + (int(r.AtomsLen()))*4
225+ return AtomList{b: r.raw[off:end:end]}
226+}
227+
228+// GetSelectionOwnerReply is a zero-copy view over a GetSelectionOwner reply; offsets count from reply byte 0.
229+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
230+type GetSelectionOwnerReply struct{ raw []byte }
231+
232+// ParseGetSelectionOwnerReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
233+func ParseGetSelectionOwnerReply(raw []byte) (GetSelectionOwnerReply, bool) {
234+ if len(raw) < 32 {
235+ return GetSelectionOwnerReply{}, false
236+ }
237+ r := GetSelectionOwnerReply{raw: raw}
238+ return r, true
239+}
240+func (r GetSelectionOwnerReply) Raw() []byte { return r.raw }
241+func (r GetSelectionOwnerReply) Owner() Window { return Window(binary.LittleEndian.Uint32(r.raw[8:])) }
242+
243+// GrabPointerReply is a zero-copy view over a GrabPointer reply; offsets count from reply byte 0.
244+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
245+type GrabPointerReply struct{ raw []byte }
246+
247+// ParseGrabPointerReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
248+func ParseGrabPointerReply(raw []byte) (GrabPointerReply, bool) {
249+ if len(raw) < 32 {
250+ return GrabPointerReply{}, false
251+ }
252+ r := GrabPointerReply{raw: raw}
253+ return r, true
254+}
255+func (r GrabPointerReply) Raw() []byte { return r.raw }
256+func (r GrabPointerReply) Status() GrabStatus { return GrabStatus(r.raw[1:][0]) }
257+
258+// GrabKeyboardReply is a zero-copy view over a GrabKeyboard reply; offsets count from reply byte 0.
259+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
260+type GrabKeyboardReply struct{ raw []byte }
261+
262+// ParseGrabKeyboardReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
263+func ParseGrabKeyboardReply(raw []byte) (GrabKeyboardReply, bool) {
264+ if len(raw) < 32 {
265+ return GrabKeyboardReply{}, false
266+ }
267+ r := GrabKeyboardReply{raw: raw}
268+ return r, true
269+}
270+func (r GrabKeyboardReply) Raw() []byte { return r.raw }
271+func (r GrabKeyboardReply) Status() GrabStatus { return GrabStatus(r.raw[1:][0]) }
272+
273+// QueryPointerReply is a zero-copy view over a QueryPointer reply; offsets count from reply byte 0.
274+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
275+type QueryPointerReply struct{ raw []byte }
276+
277+// ParseQueryPointerReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
278+func ParseQueryPointerReply(raw []byte) (QueryPointerReply, bool) {
279+ if len(raw) < 32 {
280+ return QueryPointerReply{}, false
281+ }
282+ r := QueryPointerReply{raw: raw}
283+ return r, true
284+}
285+func (r QueryPointerReply) Raw() []byte { return r.raw }
286+func (r QueryPointerReply) SameScreen() bool { return (r.raw[1:][0] != 0) }
287+func (r QueryPointerReply) Root() Window { return Window(binary.LittleEndian.Uint32(r.raw[8:])) }
288+func (r QueryPointerReply) Child() Window { return Window(binary.LittleEndian.Uint32(r.raw[12:])) }
289+func (r QueryPointerReply) RootX() int16 { return int16(binary.LittleEndian.Uint16(r.raw[16:])) }
290+func (r QueryPointerReply) RootY() int16 { return int16(binary.LittleEndian.Uint16(r.raw[18:])) }
291+func (r QueryPointerReply) WinX() int16 { return int16(binary.LittleEndian.Uint16(r.raw[20:])) }
292+func (r QueryPointerReply) WinY() int16 { return int16(binary.LittleEndian.Uint16(r.raw[22:])) }
293+func (r QueryPointerReply) Mask() KeyButMask {
294+ return KeyButMask(binary.LittleEndian.Uint16(r.raw[24:]))
295+}
296+
297+// GetMotionEventsReply is a zero-copy view over a GetMotionEvents reply; offsets count from reply byte 0.
298+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
299+type GetMotionEventsReply struct{ raw []byte }
300+
301+// ParseGetMotionEventsReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
302+func ParseGetMotionEventsReply(raw []byte) (GetMotionEventsReply, bool) {
303+ if len(raw) < 32 {
304+ return GetMotionEventsReply{}, false
305+ }
306+ r := GetMotionEventsReply{raw: raw}
307+ off := 32
308+ {
309+ n := int(r.EventsLen())
310+ if n < 0 || n > len(r.raw) {
311+ return GetMotionEventsReply{}, false
312+ }
313+ off += n * 8
314+ }
315+ if off > len(r.raw) {
316+ return GetMotionEventsReply{}, false
317+ }
318+ if off > len(r.raw) {
319+ return GetMotionEventsReply{}, false
320+ }
321+ return r, true
322+}
323+func (r GetMotionEventsReply) Raw() []byte { return r.raw }
324+func (r GetMotionEventsReply) EventsLen() uint32 {
325+ return uint32(binary.LittleEndian.Uint32(r.raw[8:]))
326+}
327+func (r GetMotionEventsReply) Events() TimecoordList {
328+ off := 32
329+ end := off + (int(r.EventsLen()))*8
330+ return TimecoordList{b: r.raw[off:end:end]}
331+}
332+
333+// TranslateCoordinatesReply is a zero-copy view over a TranslateCoordinates reply; offsets count from reply byte 0.
334+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
335+type TranslateCoordinatesReply struct{ raw []byte }
336+
337+// ParseTranslateCoordinatesReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
338+func ParseTranslateCoordinatesReply(raw []byte) (TranslateCoordinatesReply, bool) {
339+ if len(raw) < 32 {
340+ return TranslateCoordinatesReply{}, false
341+ }
342+ r := TranslateCoordinatesReply{raw: raw}
343+ return r, true
344+}
345+func (r TranslateCoordinatesReply) Raw() []byte { return r.raw }
346+func (r TranslateCoordinatesReply) SameScreen() bool { return (r.raw[1:][0] != 0) }
347+func (r TranslateCoordinatesReply) Child() Window {
348+ return Window(binary.LittleEndian.Uint32(r.raw[8:]))
349+}
350+func (r TranslateCoordinatesReply) DstX() int16 { return int16(binary.LittleEndian.Uint16(r.raw[12:])) }
351+func (r TranslateCoordinatesReply) DstY() int16 { return int16(binary.LittleEndian.Uint16(r.raw[14:])) }
352+
353+// GetInputFocusReply is a zero-copy view over a GetInputFocus reply; offsets count from reply byte 0.
354+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
355+type GetInputFocusReply struct{ raw []byte }
356+
357+// ParseGetInputFocusReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
358+func ParseGetInputFocusReply(raw []byte) (GetInputFocusReply, bool) {
359+ if len(raw) < 32 {
360+ return GetInputFocusReply{}, false
361+ }
362+ r := GetInputFocusReply{raw: raw}
363+ return r, true
364+}
365+func (r GetInputFocusReply) Raw() []byte { return r.raw }
366+func (r GetInputFocusReply) RevertTo() InputFocus { return InputFocus(r.raw[1:][0]) }
367+func (r GetInputFocusReply) Focus() Window { return Window(binary.LittleEndian.Uint32(r.raw[8:])) }
368+
369+// QueryKeymapReply is a zero-copy view over a QueryKeymap reply; offsets count from reply byte 0.
370+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
371+type QueryKeymapReply struct{ raw []byte }
372+
373+// ParseQueryKeymapReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
374+func ParseQueryKeymapReply(raw []byte) (QueryKeymapReply, bool) {
375+ if len(raw) < 40 {
376+ return QueryKeymapReply{}, false
377+ }
378+ r := QueryKeymapReply{raw: raw}
379+ return r, true
380+}
381+func (r QueryKeymapReply) Raw() []byte { return r.raw }
382+func (r QueryKeymapReply) Keys() []byte { off := 8; end := off + 32; return r.raw[off:end:end] }
383+
384+// QueryFontReply is a zero-copy view over a QueryFont reply; offsets count from reply byte 0.
385+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
386+type QueryFontReply struct{ raw []byte }
387+
388+// ParseQueryFontReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
389+func ParseQueryFontReply(raw []byte) (QueryFontReply, bool) {
390+ if len(raw) < 60 {
391+ return QueryFontReply{}, false
392+ }
393+ r := QueryFontReply{raw: raw}
394+ off := 60
395+ {
396+ n := int(r.PropertiesLen())
397+ if n < 0 || n > len(r.raw) {
398+ return QueryFontReply{}, false
399+ }
400+ off += n * 8
401+ }
402+ if off > len(r.raw) {
403+ return QueryFontReply{}, false
404+ }
405+ {
406+ n := int(r.CharInfosLen())
407+ if n < 0 || n > len(r.raw) {
408+ return QueryFontReply{}, false
409+ }
410+ off += n * 12
411+ }
412+ if off > len(r.raw) {
413+ return QueryFontReply{}, false
414+ }
415+ if off > len(r.raw) {
416+ return QueryFontReply{}, false
417+ }
418+ return r, true
419+}
420+func (r QueryFontReply) Raw() []byte { return r.raw }
421+func (r QueryFontReply) MinBounds() Charinfo { return GetCharinfo(r.raw[8:]) }
422+func (r QueryFontReply) MaxBounds() Charinfo { return GetCharinfo(r.raw[24:]) }
423+func (r QueryFontReply) MinCharOrByte2() uint16 {
424+ return uint16(binary.LittleEndian.Uint16(r.raw[40:]))
425+}
426+func (r QueryFontReply) MaxCharOrByte2() uint16 {
427+ return uint16(binary.LittleEndian.Uint16(r.raw[42:]))
428+}
429+func (r QueryFontReply) DefaultChar() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[44:])) }
430+func (r QueryFontReply) PropertiesLen() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[46:])) }
431+func (r QueryFontReply) DrawDirection() FontDraw { return FontDraw(r.raw[48:][0]) }
432+func (r QueryFontReply) MinByte1() uint8 { return uint8(r.raw[49:][0]) }
433+func (r QueryFontReply) MaxByte1() uint8 { return uint8(r.raw[50:][0]) }
434+func (r QueryFontReply) AllCharsExist() bool { return (r.raw[51:][0] != 0) }
435+func (r QueryFontReply) FontAscent() int16 { return int16(binary.LittleEndian.Uint16(r.raw[52:])) }
436+func (r QueryFontReply) FontDescent() int16 { return int16(binary.LittleEndian.Uint16(r.raw[54:])) }
437+func (r QueryFontReply) CharInfosLen() uint32 { return uint32(binary.LittleEndian.Uint32(r.raw[56:])) }
438+func (r QueryFontReply) Properties() FontpropList {
439+ off := 60
440+ end := off + (int(r.PropertiesLen()))*8
441+ return FontpropList{b: r.raw[off:end:end]}
442+}
443+func (r QueryFontReply) CharInfos() CharinfoList {
444+ off := 60
445+ {
446+ n := int(r.PropertiesLen())
447+ off += n * 8
448+ }
449+ end := off + (int(r.CharInfosLen()))*12
450+ return CharinfoList{b: r.raw[off:end:end]}
451+}
452+
453+// QueryTextExtentsReply is a zero-copy view over a QueryTextExtents reply; offsets count from reply byte 0.
454+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
455+type QueryTextExtentsReply struct{ raw []byte }
456+
457+// ParseQueryTextExtentsReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
458+func ParseQueryTextExtentsReply(raw []byte) (QueryTextExtentsReply, bool) {
459+ if len(raw) < 32 {
460+ return QueryTextExtentsReply{}, false
461+ }
462+ r := QueryTextExtentsReply{raw: raw}
463+ return r, true
464+}
465+func (r QueryTextExtentsReply) Raw() []byte { return r.raw }
466+func (r QueryTextExtentsReply) DrawDirection() FontDraw { return FontDraw(r.raw[1:][0]) }
467+func (r QueryTextExtentsReply) FontAscent() int16 {
468+ return int16(binary.LittleEndian.Uint16(r.raw[8:]))
469+}
470+func (r QueryTextExtentsReply) FontDescent() int16 {
471+ return int16(binary.LittleEndian.Uint16(r.raw[10:]))
472+}
473+func (r QueryTextExtentsReply) OverallAscent() int16 {
474+ return int16(binary.LittleEndian.Uint16(r.raw[12:]))
475+}
476+func (r QueryTextExtentsReply) OverallDescent() int16 {
477+ return int16(binary.LittleEndian.Uint16(r.raw[14:]))
478+}
479+func (r QueryTextExtentsReply) OverallWidth() int32 {
480+ return int32(binary.LittleEndian.Uint32(r.raw[16:]))
481+}
482+func (r QueryTextExtentsReply) OverallLeft() int32 {
483+ return int32(binary.LittleEndian.Uint32(r.raw[20:]))
484+}
485+func (r QueryTextExtentsReply) OverallRight() int32 {
486+ return int32(binary.LittleEndian.Uint32(r.raw[24:]))
487+}
488+
489+// ListFontsReply is a zero-copy view over a ListFonts reply; offsets count from reply byte 0.
490+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
491+type ListFontsReply struct{ raw []byte }
492+
493+// ParseListFontsReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
494+func ParseListFontsReply(raw []byte) (ListFontsReply, bool) {
495+ if len(raw) < 32 {
496+ return ListFontsReply{}, false
497+ }
498+ r := ListFontsReply{raw: raw}
499+ off := 32
500+ {
501+ n := int(r.NamesLen())
502+ if off > len(r.raw) || n < 0 || n > len(r.raw) {
503+ return ListFontsReply{}, false
504+ }
505+ it := NewStrIter(r.raw[off:], n)
506+ for it.Next() {
507+ off += it.Value().Size()
508+ }
509+ if it.Err() {
510+ return ListFontsReply{}, false
511+ }
512+ }
513+ if off > len(r.raw) {
514+ return ListFontsReply{}, false
515+ }
516+ return r, true
517+}
518+func (r ListFontsReply) Raw() []byte { return r.raw }
519+func (r ListFontsReply) NamesLen() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[8:])) }
520+func (r ListFontsReply) Names() StrIter { off := 32; return NewStrIter(r.raw[off:], int(r.NamesLen())) }
521+
522+// ListFontsWithInfoReply is a zero-copy view over a ListFontsWithInfo reply; offsets count from reply byte 0.
523+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
524+type ListFontsWithInfoReply struct{ raw []byte }
525+
526+// ParseListFontsWithInfoReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
527+func ParseListFontsWithInfoReply(raw []byte) (ListFontsWithInfoReply, bool) {
528+ if len(raw) < 60 {
529+ return ListFontsWithInfoReply{}, false
530+ }
531+ r := ListFontsWithInfoReply{raw: raw}
532+ off := 60
533+ {
534+ n := int(r.PropertiesLen())
535+ if n < 0 || n > len(r.raw) {
536+ return ListFontsWithInfoReply{}, false
537+ }
538+ off += n * 8
539+ }
540+ if off > len(r.raw) {
541+ return ListFontsWithInfoReply{}, false
542+ }
543+ {
544+ n := int(r.NameLen())
545+ if n < 0 || n > len(r.raw) {
546+ return ListFontsWithInfoReply{}, false
547+ }
548+ off += n * 1
549+ }
550+ if off > len(r.raw) {
551+ return ListFontsWithInfoReply{}, false
552+ }
553+ if off > len(r.raw) {
554+ return ListFontsWithInfoReply{}, false
555+ }
556+ return r, true
557+}
558+func (r ListFontsWithInfoReply) Raw() []byte { return r.raw }
559+func (r ListFontsWithInfoReply) NameLen() uint8 { return uint8(r.raw[1:][0]) }
560+func (r ListFontsWithInfoReply) MinBounds() Charinfo { return GetCharinfo(r.raw[8:]) }
561+func (r ListFontsWithInfoReply) MaxBounds() Charinfo { return GetCharinfo(r.raw[24:]) }
562+func (r ListFontsWithInfoReply) MinCharOrByte2() uint16 {
563+ return uint16(binary.LittleEndian.Uint16(r.raw[40:]))
564+}
565+func (r ListFontsWithInfoReply) MaxCharOrByte2() uint16 {
566+ return uint16(binary.LittleEndian.Uint16(r.raw[42:]))
567+}
568+func (r ListFontsWithInfoReply) DefaultChar() uint16 {
569+ return uint16(binary.LittleEndian.Uint16(r.raw[44:]))
570+}
571+func (r ListFontsWithInfoReply) PropertiesLen() uint16 {
572+ return uint16(binary.LittleEndian.Uint16(r.raw[46:]))
573+}
574+func (r ListFontsWithInfoReply) DrawDirection() FontDraw { return FontDraw(r.raw[48:][0]) }
575+func (r ListFontsWithInfoReply) MinByte1() uint8 { return uint8(r.raw[49:][0]) }
576+func (r ListFontsWithInfoReply) MaxByte1() uint8 { return uint8(r.raw[50:][0]) }
577+func (r ListFontsWithInfoReply) AllCharsExist() bool { return (r.raw[51:][0] != 0) }
578+func (r ListFontsWithInfoReply) FontAscent() int16 {
579+ return int16(binary.LittleEndian.Uint16(r.raw[52:]))
580+}
581+func (r ListFontsWithInfoReply) FontDescent() int16 {
582+ return int16(binary.LittleEndian.Uint16(r.raw[54:]))
583+}
584+func (r ListFontsWithInfoReply) RepliesHint() uint32 {
585+ return uint32(binary.LittleEndian.Uint32(r.raw[56:]))
586+}
587+func (r ListFontsWithInfoReply) Properties() FontpropList {
588+ off := 60
589+ end := off + (int(r.PropertiesLen()))*8
590+ return FontpropList{b: r.raw[off:end:end]}
591+}
592+func (r ListFontsWithInfoReply) Name() []byte {
593+ off := 60
594+ {
595+ n := int(r.PropertiesLen())
596+ off += n * 8
597+ }
598+ end := off + int(r.NameLen())
599+ return r.raw[off:end:end]
600+}
601+
602+// GetFontPathReply is a zero-copy view over a GetFontPath reply; offsets count from reply byte 0.
603+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
604+type GetFontPathReply struct{ raw []byte }
605+
606+// ParseGetFontPathReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
607+func ParseGetFontPathReply(raw []byte) (GetFontPathReply, bool) {
608+ if len(raw) < 32 {
609+ return GetFontPathReply{}, false
610+ }
611+ r := GetFontPathReply{raw: raw}
612+ off := 32
613+ {
614+ n := int(r.PathLen())
615+ if off > len(r.raw) || n < 0 || n > len(r.raw) {
616+ return GetFontPathReply{}, false
617+ }
618+ it := NewStrIter(r.raw[off:], n)
619+ for it.Next() {
620+ off += it.Value().Size()
621+ }
622+ if it.Err() {
623+ return GetFontPathReply{}, false
624+ }
625+ }
626+ if off > len(r.raw) {
627+ return GetFontPathReply{}, false
628+ }
629+ return r, true
630+}
631+func (r GetFontPathReply) Raw() []byte { return r.raw }
632+func (r GetFontPathReply) PathLen() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[8:])) }
633+func (r GetFontPathReply) Path() StrIter { off := 32; return NewStrIter(r.raw[off:], int(r.PathLen())) }
634+
635+// GetImageReply is a zero-copy view over a GetImage reply; offsets count from reply byte 0.
636+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
637+type GetImageReply struct{ raw []byte }
638+
639+// ParseGetImageReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
640+func ParseGetImageReply(raw []byte) (GetImageReply, bool) {
641+ if len(raw) < 32 {
642+ return GetImageReply{}, false
643+ }
644+ r := GetImageReply{raw: raw}
645+ off := 32
646+ {
647+ n := (int(binary.LittleEndian.Uint32(r.raw[4:])) * 4)
648+ if n < 0 || n > len(r.raw) {
649+ return GetImageReply{}, false
650+ }
651+ off += n * 1
652+ }
653+ if off > len(r.raw) {
654+ return GetImageReply{}, false
655+ }
656+ if off > len(r.raw) {
657+ return GetImageReply{}, false
658+ }
659+ return r, true
660+}
661+func (r GetImageReply) Raw() []byte { return r.raw }
662+func (r GetImageReply) Depth() uint8 { return uint8(r.raw[1:][0]) }
663+func (r GetImageReply) Visual() Visualid { return Visualid(binary.LittleEndian.Uint32(r.raw[8:])) }
664+func (r GetImageReply) Data() []byte {
665+ off := 32
666+ end := off + (int(binary.LittleEndian.Uint32(r.raw[4:])) * 4)
667+ return r.raw[off:end:end]
668+}
669+
670+// ListInstalledColormapsReply is a zero-copy view over a ListInstalledColormaps reply; offsets count from reply byte 0.
671+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
672+type ListInstalledColormapsReply struct{ raw []byte }
673+
674+// ParseListInstalledColormapsReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
675+func ParseListInstalledColormapsReply(raw []byte) (ListInstalledColormapsReply, bool) {
676+ if len(raw) < 32 {
677+ return ListInstalledColormapsReply{}, false
678+ }
679+ r := ListInstalledColormapsReply{raw: raw}
680+ off := 32
681+ {
682+ n := int(r.CmapsLen())
683+ if n < 0 || n > len(r.raw) {
684+ return ListInstalledColormapsReply{}, false
685+ }
686+ off += n * 4
687+ }
688+ if off > len(r.raw) {
689+ return ListInstalledColormapsReply{}, false
690+ }
691+ if off > len(r.raw) {
692+ return ListInstalledColormapsReply{}, false
693+ }
694+ return r, true
695+}
696+func (r ListInstalledColormapsReply) Raw() []byte { return r.raw }
697+func (r ListInstalledColormapsReply) CmapsLen() uint16 {
698+ return uint16(binary.LittleEndian.Uint16(r.raw[8:]))
699+}
700+func (r ListInstalledColormapsReply) Cmaps() ColormapList {
701+ off := 32
702+ end := off + (int(r.CmapsLen()))*4
703+ return ColormapList{b: r.raw[off:end:end]}
704+}
705+
706+// AllocColorReply is a zero-copy view over a AllocColor reply; offsets count from reply byte 0.
707+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
708+type AllocColorReply struct{ raw []byte }
709+
710+// ParseAllocColorReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
711+func ParseAllocColorReply(raw []byte) (AllocColorReply, bool) {
712+ if len(raw) < 32 {
713+ return AllocColorReply{}, false
714+ }
715+ r := AllocColorReply{raw: raw}
716+ return r, true
717+}
718+func (r AllocColorReply) Raw() []byte { return r.raw }
719+func (r AllocColorReply) Red() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[8:])) }
720+func (r AllocColorReply) Green() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[10:])) }
721+func (r AllocColorReply) Blue() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[12:])) }
722+func (r AllocColorReply) Pixel() uint32 { return uint32(binary.LittleEndian.Uint32(r.raw[16:])) }
723+
724+// AllocNamedColorReply is a zero-copy view over a AllocNamedColor reply; offsets count from reply byte 0.
725+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
726+type AllocNamedColorReply struct{ raw []byte }
727+
728+// ParseAllocNamedColorReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
729+func ParseAllocNamedColorReply(raw []byte) (AllocNamedColorReply, bool) {
730+ if len(raw) < 32 {
731+ return AllocNamedColorReply{}, false
732+ }
733+ r := AllocNamedColorReply{raw: raw}
734+ return r, true
735+}
736+func (r AllocNamedColorReply) Raw() []byte { return r.raw }
737+func (r AllocNamedColorReply) Pixel() uint32 { return uint32(binary.LittleEndian.Uint32(r.raw[8:])) }
738+func (r AllocNamedColorReply) ExactRed() uint16 {
739+ return uint16(binary.LittleEndian.Uint16(r.raw[12:]))
740+}
741+func (r AllocNamedColorReply) ExactGreen() uint16 {
742+ return uint16(binary.LittleEndian.Uint16(r.raw[14:]))
743+}
744+func (r AllocNamedColorReply) ExactBlue() uint16 {
745+ return uint16(binary.LittleEndian.Uint16(r.raw[16:]))
746+}
747+func (r AllocNamedColorReply) VisualRed() uint16 {
748+ return uint16(binary.LittleEndian.Uint16(r.raw[18:]))
749+}
750+func (r AllocNamedColorReply) VisualGreen() uint16 {
751+ return uint16(binary.LittleEndian.Uint16(r.raw[20:]))
752+}
753+func (r AllocNamedColorReply) VisualBlue() uint16 {
754+ return uint16(binary.LittleEndian.Uint16(r.raw[22:]))
755+}
756+
757+// AllocColorCellsReply is a zero-copy view over a AllocColorCells reply; offsets count from reply byte 0.
758+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
759+type AllocColorCellsReply struct{ raw []byte }
760+
761+// ParseAllocColorCellsReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
762+func ParseAllocColorCellsReply(raw []byte) (AllocColorCellsReply, bool) {
763+ if len(raw) < 32 {
764+ return AllocColorCellsReply{}, false
765+ }
766+ r := AllocColorCellsReply{raw: raw}
767+ off := 32
768+ {
769+ n := int(r.PixelsLen())
770+ if n < 0 || n > len(r.raw) {
771+ return AllocColorCellsReply{}, false
772+ }
773+ off += n * 4
774+ }
775+ if off > len(r.raw) {
776+ return AllocColorCellsReply{}, false
777+ }
778+ {
779+ n := int(r.MasksLen())
780+ if n < 0 || n > len(r.raw) {
781+ return AllocColorCellsReply{}, false
782+ }
783+ off += n * 4
784+ }
785+ if off > len(r.raw) {
786+ return AllocColorCellsReply{}, false
787+ }
788+ if off > len(r.raw) {
789+ return AllocColorCellsReply{}, false
790+ }
791+ return r, true
792+}
793+func (r AllocColorCellsReply) Raw() []byte { return r.raw }
794+func (r AllocColorCellsReply) PixelsLen() uint16 {
795+ return uint16(binary.LittleEndian.Uint16(r.raw[8:]))
796+}
797+func (r AllocColorCellsReply) MasksLen() uint16 {
798+ return uint16(binary.LittleEndian.Uint16(r.raw[10:]))
799+}
800+func (r AllocColorCellsReply) Pixels() Uint32List {
801+ off := 32
802+ end := off + (int(r.PixelsLen()))*4
803+ return Uint32List{b: r.raw[off:end:end]}
804+}
805+func (r AllocColorCellsReply) Masks() Uint32List {
806+ off := 32
807+ {
808+ n := int(r.PixelsLen())
809+ off += n * 4
810+ }
811+ end := off + (int(r.MasksLen()))*4
812+ return Uint32List{b: r.raw[off:end:end]}
813+}
814+
815+// AllocColorPlanesReply is a zero-copy view over a AllocColorPlanes reply; offsets count from reply byte 0.
816+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
817+type AllocColorPlanesReply struct{ raw []byte }
818+
819+// ParseAllocColorPlanesReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
820+func ParseAllocColorPlanesReply(raw []byte) (AllocColorPlanesReply, bool) {
821+ if len(raw) < 32 {
822+ return AllocColorPlanesReply{}, false
823+ }
824+ r := AllocColorPlanesReply{raw: raw}
825+ off := 32
826+ {
827+ n := int(r.PixelsLen())
828+ if n < 0 || n > len(r.raw) {
829+ return AllocColorPlanesReply{}, false
830+ }
831+ off += n * 4
832+ }
833+ if off > len(r.raw) {
834+ return AllocColorPlanesReply{}, false
835+ }
836+ if off > len(r.raw) {
837+ return AllocColorPlanesReply{}, false
838+ }
839+ return r, true
840+}
841+func (r AllocColorPlanesReply) Raw() []byte { return r.raw }
842+func (r AllocColorPlanesReply) PixelsLen() uint16 {
843+ return uint16(binary.LittleEndian.Uint16(r.raw[8:]))
844+}
845+func (r AllocColorPlanesReply) RedMask() uint32 {
846+ return uint32(binary.LittleEndian.Uint32(r.raw[12:]))
847+}
848+func (r AllocColorPlanesReply) GreenMask() uint32 {
849+ return uint32(binary.LittleEndian.Uint32(r.raw[16:]))
850+}
851+func (r AllocColorPlanesReply) BlueMask() uint32 {
852+ return uint32(binary.LittleEndian.Uint32(r.raw[20:]))
853+}
854+func (r AllocColorPlanesReply) Pixels() Uint32List {
855+ off := 32
856+ end := off + (int(r.PixelsLen()))*4
857+ return Uint32List{b: r.raw[off:end:end]}
858+}
859+
860+// QueryColorsReply is a zero-copy view over a QueryColors reply; offsets count from reply byte 0.
861+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
862+type QueryColorsReply struct{ raw []byte }
863+
864+// ParseQueryColorsReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
865+func ParseQueryColorsReply(raw []byte) (QueryColorsReply, bool) {
866+ if len(raw) < 32 {
867+ return QueryColorsReply{}, false
868+ }
869+ r := QueryColorsReply{raw: raw}
870+ off := 32
871+ {
872+ n := int(r.ColorsLen())
873+ if n < 0 || n > len(r.raw) {
874+ return QueryColorsReply{}, false
875+ }
876+ off += n * 8
877+ }
878+ if off > len(r.raw) {
879+ return QueryColorsReply{}, false
880+ }
881+ if off > len(r.raw) {
882+ return QueryColorsReply{}, false
883+ }
884+ return r, true
885+}
886+func (r QueryColorsReply) Raw() []byte { return r.raw }
887+func (r QueryColorsReply) ColorsLen() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[8:])) }
888+func (r QueryColorsReply) Colors() RgbList {
889+ off := 32
890+ end := off + (int(r.ColorsLen()))*8
891+ return RgbList{b: r.raw[off:end:end]}
892+}
893+
894+// LookupColorReply is a zero-copy view over a LookupColor reply; offsets count from reply byte 0.
895+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
896+type LookupColorReply struct{ raw []byte }
897+
898+// ParseLookupColorReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
899+func ParseLookupColorReply(raw []byte) (LookupColorReply, bool) {
900+ if len(raw) < 32 {
901+ return LookupColorReply{}, false
902+ }
903+ r := LookupColorReply{raw: raw}
904+ return r, true
905+}
906+func (r LookupColorReply) Raw() []byte { return r.raw }
907+func (r LookupColorReply) ExactRed() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[8:])) }
908+func (r LookupColorReply) ExactGreen() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[10:])) }
909+func (r LookupColorReply) ExactBlue() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[12:])) }
910+func (r LookupColorReply) VisualRed() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[14:])) }
911+func (r LookupColorReply) VisualGreen() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[16:])) }
912+func (r LookupColorReply) VisualBlue() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[18:])) }
913+
914+// QueryBestSizeReply is a zero-copy view over a QueryBestSize reply; offsets count from reply byte 0.
915+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
916+type QueryBestSizeReply struct{ raw []byte }
917+
918+// ParseQueryBestSizeReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
919+func ParseQueryBestSizeReply(raw []byte) (QueryBestSizeReply, bool) {
920+ if len(raw) < 32 {
921+ return QueryBestSizeReply{}, false
922+ }
923+ r := QueryBestSizeReply{raw: raw}
924+ return r, true
925+}
926+func (r QueryBestSizeReply) Raw() []byte { return r.raw }
927+func (r QueryBestSizeReply) Width() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[8:])) }
928+func (r QueryBestSizeReply) Height() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[10:])) }
929+
930+// QueryExtensionReply is a zero-copy view over a QueryExtension reply; offsets count from reply byte 0.
931+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
932+type QueryExtensionReply struct{ raw []byte }
933+
934+// ParseQueryExtensionReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
935+func ParseQueryExtensionReply(raw []byte) (QueryExtensionReply, bool) {
936+ if len(raw) < 32 {
937+ return QueryExtensionReply{}, false
938+ }
939+ r := QueryExtensionReply{raw: raw}
940+ return r, true
941+}
942+func (r QueryExtensionReply) Raw() []byte { return r.raw }
943+func (r QueryExtensionReply) Present() bool { return (r.raw[8:][0] != 0) }
944+func (r QueryExtensionReply) MajorOpcode() uint8 { return uint8(r.raw[9:][0]) }
945+func (r QueryExtensionReply) FirstEvent() uint8 { return uint8(r.raw[10:][0]) }
946+func (r QueryExtensionReply) FirstError() uint8 { return uint8(r.raw[11:][0]) }
947+
948+// ListExtensionsReply is a zero-copy view over a ListExtensions reply; offsets count from reply byte 0.
949+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
950+type ListExtensionsReply struct{ raw []byte }
951+
952+// ParseListExtensionsReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
953+func ParseListExtensionsReply(raw []byte) (ListExtensionsReply, bool) {
954+ if len(raw) < 32 {
955+ return ListExtensionsReply{}, false
956+ }
957+ r := ListExtensionsReply{raw: raw}
958+ off := 32
959+ {
960+ n := int(r.NamesLen())
961+ if off > len(r.raw) || n < 0 || n > len(r.raw) {
962+ return ListExtensionsReply{}, false
963+ }
964+ it := NewStrIter(r.raw[off:], n)
965+ for it.Next() {
966+ off += it.Value().Size()
967+ }
968+ if it.Err() {
969+ return ListExtensionsReply{}, false
970+ }
971+ }
972+ if off > len(r.raw) {
973+ return ListExtensionsReply{}, false
974+ }
975+ return r, true
976+}
977+func (r ListExtensionsReply) Raw() []byte { return r.raw }
978+func (r ListExtensionsReply) NamesLen() uint8 { return uint8(r.raw[1:][0]) }
979+func (r ListExtensionsReply) Names() StrIter {
980+ off := 32
981+ return NewStrIter(r.raw[off:], int(r.NamesLen()))
982+}
983+
984+// GetKeyboardMappingReply is a zero-copy view over a GetKeyboardMapping reply; offsets count from reply byte 0.
985+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
986+type GetKeyboardMappingReply struct{ raw []byte }
987+
988+// ParseGetKeyboardMappingReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
989+func ParseGetKeyboardMappingReply(raw []byte) (GetKeyboardMappingReply, bool) {
990+ if len(raw) < 32 {
991+ return GetKeyboardMappingReply{}, false
992+ }
993+ r := GetKeyboardMappingReply{raw: raw}
994+ off := 32
995+ {
996+ n := int(binary.LittleEndian.Uint32(r.raw[4:]))
997+ if n < 0 || n > len(r.raw) {
998+ return GetKeyboardMappingReply{}, false
999+ }
1000+ off += n * 4
1001+ }
1002+ if off > len(r.raw) {
1003+ return GetKeyboardMappingReply{}, false
1004+ }
1005+ if off > len(r.raw) {
1006+ return GetKeyboardMappingReply{}, false
1007+ }
1008+ return r, true
1009+}
1010+func (r GetKeyboardMappingReply) Raw() []byte { return r.raw }
1011+func (r GetKeyboardMappingReply) KeysymsPerKeycode() byte { return byte(r.raw[1:][0]) }
1012+func (r GetKeyboardMappingReply) Keysyms() KeysymList {
1013+ off := 32
1014+ end := off + (int(binary.LittleEndian.Uint32(r.raw[4:])))*4
1015+ return KeysymList{b: r.raw[off:end:end]}
1016+}
1017+
1018+// GetKeyboardControlReply is a zero-copy view over a GetKeyboardControl reply; offsets count from reply byte 0.
1019+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
1020+type GetKeyboardControlReply struct{ raw []byte }
1021+
1022+// ParseGetKeyboardControlReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
1023+func ParseGetKeyboardControlReply(raw []byte) (GetKeyboardControlReply, bool) {
1024+ if len(raw) < 52 {
1025+ return GetKeyboardControlReply{}, false
1026+ }
1027+ r := GetKeyboardControlReply{raw: raw}
1028+ return r, true
1029+}
1030+func (r GetKeyboardControlReply) Raw() []byte { return r.raw }
1031+func (r GetKeyboardControlReply) GlobalAutoRepeat() AutoRepeatMode {
1032+ return AutoRepeatMode(r.raw[1:][0])
1033+}
1034+func (r GetKeyboardControlReply) LedMask() uint32 {
1035+ return uint32(binary.LittleEndian.Uint32(r.raw[8:]))
1036+}
1037+func (r GetKeyboardControlReply) KeyClickPercent() uint8 { return uint8(r.raw[12:][0]) }
1038+func (r GetKeyboardControlReply) BellPercent() uint8 { return uint8(r.raw[13:][0]) }
1039+func (r GetKeyboardControlReply) BellPitch() uint16 {
1040+ return uint16(binary.LittleEndian.Uint16(r.raw[14:]))
1041+}
1042+func (r GetKeyboardControlReply) BellDuration() uint16 {
1043+ return uint16(binary.LittleEndian.Uint16(r.raw[16:]))
1044+}
1045+func (r GetKeyboardControlReply) AutoRepeats() []byte {
1046+ off := 20
1047+ end := off + 32
1048+ return r.raw[off:end:end]
1049+}
1050+
1051+// GetPointerControlReply is a zero-copy view over a GetPointerControl reply; offsets count from reply byte 0.
1052+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
1053+type GetPointerControlReply struct{ raw []byte }
1054+
1055+// ParseGetPointerControlReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
1056+func ParseGetPointerControlReply(raw []byte) (GetPointerControlReply, bool) {
1057+ if len(raw) < 32 {
1058+ return GetPointerControlReply{}, false
1059+ }
1060+ r := GetPointerControlReply{raw: raw}
1061+ return r, true
1062+}
1063+func (r GetPointerControlReply) Raw() []byte { return r.raw }
1064+func (r GetPointerControlReply) AccelerationNumerator() uint16 {
1065+ return uint16(binary.LittleEndian.Uint16(r.raw[8:]))
1066+}
1067+func (r GetPointerControlReply) AccelerationDenominator() uint16 {
1068+ return uint16(binary.LittleEndian.Uint16(r.raw[10:]))
1069+}
1070+func (r GetPointerControlReply) Threshold() uint16 {
1071+ return uint16(binary.LittleEndian.Uint16(r.raw[12:]))
1072+}
1073+
1074+// GetScreenSaverReply is a zero-copy view over a GetScreenSaver reply; offsets count from reply byte 0.
1075+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
1076+type GetScreenSaverReply struct{ raw []byte }
1077+
1078+// ParseGetScreenSaverReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
1079+func ParseGetScreenSaverReply(raw []byte) (GetScreenSaverReply, bool) {
1080+ if len(raw) < 32 {
1081+ return GetScreenSaverReply{}, false
1082+ }
1083+ r := GetScreenSaverReply{raw: raw}
1084+ return r, true
1085+}
1086+func (r GetScreenSaverReply) Raw() []byte { return r.raw }
1087+func (r GetScreenSaverReply) Timeout() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[8:])) }
1088+func (r GetScreenSaverReply) Interval() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[10:])) }
1089+func (r GetScreenSaverReply) PreferBlanking() Blanking { return Blanking(r.raw[12:][0]) }
1090+func (r GetScreenSaverReply) AllowExposures() Exposures { return Exposures(r.raw[13:][0]) }
1091+
1092+// ListHostsReply is a zero-copy view over a ListHosts reply; offsets count from reply byte 0.
1093+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
1094+type ListHostsReply struct{ raw []byte }
1095+
1096+// ParseListHostsReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
1097+func ParseListHostsReply(raw []byte) (ListHostsReply, bool) {
1098+ if len(raw) < 32 {
1099+ return ListHostsReply{}, false
1100+ }
1101+ r := ListHostsReply{raw: raw}
1102+ off := 32
1103+ {
1104+ n := int(r.HostsLen())
1105+ if off > len(r.raw) || n < 0 || n > len(r.raw) {
1106+ return ListHostsReply{}, false
1107+ }
1108+ it := NewHostIter(r.raw[off:], n)
1109+ for it.Next() {
1110+ off += it.Value().Size()
1111+ }
1112+ if it.Err() {
1113+ return ListHostsReply{}, false
1114+ }
1115+ }
1116+ if off > len(r.raw) {
1117+ return ListHostsReply{}, false
1118+ }
1119+ return r, true
1120+}
1121+func (r ListHostsReply) Raw() []byte { return r.raw }
1122+func (r ListHostsReply) Mode() AccessControl { return AccessControl(r.raw[1:][0]) }
1123+func (r ListHostsReply) HostsLen() uint16 { return uint16(binary.LittleEndian.Uint16(r.raw[8:])) }
1124+func (r ListHostsReply) Hosts() HostIter {
1125+ off := 32
1126+ return NewHostIter(r.raw[off:], int(r.HostsLen()))
1127+}
1128+
1129+// SetPointerMappingReply is a zero-copy view over a SetPointerMapping reply; offsets count from reply byte 0.
1130+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
1131+type SetPointerMappingReply struct{ raw []byte }
1132+
1133+// ParseSetPointerMappingReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
1134+func ParseSetPointerMappingReply(raw []byte) (SetPointerMappingReply, bool) {
1135+ if len(raw) < 32 {
1136+ return SetPointerMappingReply{}, false
1137+ }
1138+ r := SetPointerMappingReply{raw: raw}
1139+ return r, true
1140+}
1141+func (r SetPointerMappingReply) Raw() []byte { return r.raw }
1142+func (r SetPointerMappingReply) Status() MappingStatus { return MappingStatus(r.raw[1:][0]) }
1143+
1144+// GetPointerMappingReply is a zero-copy view over a GetPointerMapping reply; offsets count from reply byte 0.
1145+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
1146+type GetPointerMappingReply struct{ raw []byte }
1147+
1148+// ParseGetPointerMappingReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
1149+func ParseGetPointerMappingReply(raw []byte) (GetPointerMappingReply, bool) {
1150+ if len(raw) < 32 {
1151+ return GetPointerMappingReply{}, false
1152+ }
1153+ r := GetPointerMappingReply{raw: raw}
1154+ off := 32
1155+ {
1156+ n := int(r.MapLen())
1157+ if n < 0 || n > len(r.raw) {
1158+ return GetPointerMappingReply{}, false
1159+ }
1160+ off += n * 1
1161+ }
1162+ if off > len(r.raw) {
1163+ return GetPointerMappingReply{}, false
1164+ }
1165+ if off > len(r.raw) {
1166+ return GetPointerMappingReply{}, false
1167+ }
1168+ return r, true
1169+}
1170+func (r GetPointerMappingReply) Raw() []byte { return r.raw }
1171+func (r GetPointerMappingReply) MapLen() uint8 { return uint8(r.raw[1:][0]) }
1172+func (r GetPointerMappingReply) Map() []byte {
1173+ off := 32
1174+ end := off + int(r.MapLen())
1175+ return r.raw[off:end:end]
1176+}
1177+
1178+// SetModifierMappingReply is a zero-copy view over a SetModifierMapping reply; offsets count from reply byte 0.
1179+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
1180+type SetModifierMappingReply struct{ raw []byte }
1181+
1182+// ParseSetModifierMappingReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
1183+func ParseSetModifierMappingReply(raw []byte) (SetModifierMappingReply, bool) {
1184+ if len(raw) < 32 {
1185+ return SetModifierMappingReply{}, false
1186+ }
1187+ r := SetModifierMappingReply{raw: raw}
1188+ return r, true
1189+}
1190+func (r SetModifierMappingReply) Raw() []byte { return r.raw }
1191+func (r SetModifierMappingReply) Status() MappingStatus { return MappingStatus(r.raw[1:][0]) }
1192+
1193+// GetModifierMappingReply is a zero-copy view over a GetModifierMapping reply; offsets count from reply byte 0.
1194+// It borrows the reply bytes: valid only until the Handler callback returns, because the read ring is reused. Copy what you keep.
1195+type GetModifierMappingReply struct{ raw []byte }
1196+
1197+// ParseGetModifierMappingReply validates raw (the whole reply, 32+4*length bytes) once; getters do not re-check.
1198+func ParseGetModifierMappingReply(raw []byte) (GetModifierMappingReply, bool) {
1199+ if len(raw) < 32 {
1200+ return GetModifierMappingReply{}, false
1201+ }
1202+ r := GetModifierMappingReply{raw: raw}
1203+ off := 32
1204+ {
1205+ n := (int(r.KeycodesPerModifier()) * 8)
1206+ if n < 0 || n > len(r.raw) {
1207+ return GetModifierMappingReply{}, false
1208+ }
1209+ off += n * 1
1210+ }
1211+ if off > len(r.raw) {
1212+ return GetModifierMappingReply{}, false
1213+ }
1214+ if off > len(r.raw) {
1215+ return GetModifierMappingReply{}, false
1216+ }
1217+ return r, true
1218+}
1219+func (r GetModifierMappingReply) Raw() []byte { return r.raw }
1220+func (r GetModifierMappingReply) KeycodesPerModifier() uint8 { return uint8(r.raw[1:][0]) }
1221+func (r GetModifierMappingReply) Keycodes() KeycodeList {
1222+ off := 32
1223+ end := off + (int(r.KeycodesPerModifier())*8)*1
1224+ return KeycodeList{b: r.raw[off:end:end]}
1225+}
1226+
1227+// DepthView is a zero-copy view over one wire-encoded Depth, yielded by DepthIter.
1228+// It shares the lifetime of the bytes it was taken from (for replies: until the Handler callback returns). Copy what you keep.
1229+type DepthView struct{ raw []byte }
1230+
1231+func (v DepthView) Raw() []byte { return v.raw }
1232+
1233+// Size is the element's wire size, including trailing alignment.
1234+func (v DepthView) Size() int { return len(v.raw) }
1235+func sizeDepth(p []byte) int {
1236+ if len(p) < 8 {
1237+ return -1
1238+ }
1239+ v := DepthView{raw: p}
1240+ _ = v
1241+ off := 8
1242+ {
1243+ n := int(v.VisualsLen())
1244+ if n < 0 || n > len(v.raw) {
1245+ return -1
1246+ }
1247+ off += n * 24
1248+ }
1249+ if off > len(v.raw) {
1250+ return -1
1251+ }
1252+ if off > len(v.raw) {
1253+ return -1
1254+ }
1255+ return off
1256+}
1257+func (v DepthView) Depth() uint8 { return uint8(v.raw[0:][0]) }
1258+func (v DepthView) VisualsLen() uint16 { return uint16(binary.LittleEndian.Uint16(v.raw[2:])) }
1259+func (v DepthView) Visuals() VisualtypeList {
1260+ off := 8
1261+ end := off + (int(v.VisualsLen()))*24
1262+ return VisualtypeList{b: v.raw[off:end:end]}
1263+}
1264+
1265+// DepthIter walks n consecutive Depth elements; Err reports a malformed length.
1266+// It shares the lifetime of the bytes it walks.
1267+type DepthIter struct {
1268+ b []byte
1269+ n int
1270+ v DepthView
1271+ err bool
1272+}
1273+
1274+// NewDepthIter iterates n elements encoded at the start of b.
1275+func NewDepthIter(b []byte, n int) DepthIter { return DepthIter{b: b, n: n} }
1276+func (it *DepthIter) Next() bool {
1277+ if it.n <= 0 || it.err {
1278+ return false
1279+ }
1280+ s := sizeDepth(it.b)
1281+ if s < 0 {
1282+ it.err = true
1283+ it.n = 0
1284+ return false
1285+ }
1286+ it.v = DepthView{raw: it.b[:s:s]}
1287+ it.b = it.b[s:]
1288+ it.n--
1289+ return true
1290+}
1291+func (it *DepthIter) Value() DepthView { return it.v }
1292+func (it *DepthIter) Err() bool { return it.err }
1293+
1294+// ScreenView is a zero-copy view over one wire-encoded Screen, yielded by ScreenIter.
1295+// It shares the lifetime of the bytes it was taken from (for replies: until the Handler callback returns). Copy what you keep.
1296+type ScreenView struct{ raw []byte }
1297+
1298+func (v ScreenView) Raw() []byte { return v.raw }
1299+
1300+// Size is the element's wire size, including trailing alignment.
1301+func (v ScreenView) Size() int { return len(v.raw) }
1302+func sizeScreen(p []byte) int {
1303+ if len(p) < 40 {
1304+ return -1
1305+ }
1306+ v := ScreenView{raw: p}
1307+ _ = v
1308+ off := 40
1309+ {
1310+ n := int(v.AllowedDepthsLen())
1311+ if off > len(v.raw) || n < 0 || n > len(v.raw) {
1312+ return -1
1313+ }
1314+ it := NewDepthIter(v.raw[off:], n)
1315+ for it.Next() {
1316+ off += it.Value().Size()
1317+ }
1318+ if it.Err() {
1319+ return -1
1320+ }
1321+ }
1322+ if off > len(v.raw) {
1323+ return -1
1324+ }
1325+ return off
1326+}
1327+func (v ScreenView) Root() Window { return Window(binary.LittleEndian.Uint32(v.raw[0:])) }
1328+func (v ScreenView) DefaultColormap() Colormap {
1329+ return Colormap(binary.LittleEndian.Uint32(v.raw[4:]))
1330+}
1331+func (v ScreenView) WhitePixel() uint32 { return uint32(binary.LittleEndian.Uint32(v.raw[8:])) }
1332+func (v ScreenView) BlackPixel() uint32 { return uint32(binary.LittleEndian.Uint32(v.raw[12:])) }
1333+func (v ScreenView) CurrentInputMasks() EventMask {
1334+ return EventMask(binary.LittleEndian.Uint32(v.raw[16:]))
1335+}
1336+func (v ScreenView) WidthInPixels() uint16 { return uint16(binary.LittleEndian.Uint16(v.raw[20:])) }
1337+func (v ScreenView) HeightInPixels() uint16 { return uint16(binary.LittleEndian.Uint16(v.raw[22:])) }
1338+func (v ScreenView) WidthInMillimeters() uint16 {
1339+ return uint16(binary.LittleEndian.Uint16(v.raw[24:]))
1340+}
1341+func (v ScreenView) HeightInMillimeters() uint16 {
1342+ return uint16(binary.LittleEndian.Uint16(v.raw[26:]))
1343+}
1344+func (v ScreenView) MinInstalledMaps() uint16 { return uint16(binary.LittleEndian.Uint16(v.raw[28:])) }
1345+func (v ScreenView) MaxInstalledMaps() uint16 { return uint16(binary.LittleEndian.Uint16(v.raw[30:])) }
1346+func (v ScreenView) RootVisual() Visualid { return Visualid(binary.LittleEndian.Uint32(v.raw[32:])) }
1347+func (v ScreenView) BackingStores() BackingStore { return BackingStore(v.raw[36:][0]) }
1348+func (v ScreenView) SaveUnders() bool { return (v.raw[37:][0] != 0) }
1349+func (v ScreenView) RootDepth() uint8 { return uint8(v.raw[38:][0]) }
1350+func (v ScreenView) AllowedDepthsLen() uint8 { return uint8(v.raw[39:][0]) }
1351+func (v ScreenView) AllowedDepths() DepthIter {
1352+ off := 40
1353+ return NewDepthIter(v.raw[off:], int(v.AllowedDepthsLen()))
1354+}
1355+
1356+// ScreenIter walks n consecutive Screen elements; Err reports a malformed length.
1357+// It shares the lifetime of the bytes it walks.
1358+type ScreenIter struct {
1359+ b []byte
1360+ n int
1361+ v ScreenView
1362+ err bool
1363+}
1364+
1365+// NewScreenIter iterates n elements encoded at the start of b.
1366+func NewScreenIter(b []byte, n int) ScreenIter { return ScreenIter{b: b, n: n} }
1367+func (it *ScreenIter) Next() bool {
1368+ if it.n <= 0 || it.err {
1369+ return false
1370+ }
1371+ s := sizeScreen(it.b)
1372+ if s < 0 {
1373+ it.err = true
1374+ it.n = 0
1375+ return false
1376+ }
1377+ it.v = ScreenView{raw: it.b[:s:s]}
1378+ it.b = it.b[s:]
1379+ it.n--
1380+ return true
1381+}
1382+func (it *ScreenIter) Value() ScreenView { return it.v }
1383+func (it *ScreenIter) Err() bool { return it.err }
1384+
1385+// SetupRequestView is a zero-copy view over one wire-encoded SetupRequest, yielded by SetupRequestIter.
1386+// It shares the lifetime of the bytes it was taken from (for replies: until the Handler callback returns). Copy what you keep.
1387+type SetupRequestView struct{ raw []byte }
1388+
1389+func (v SetupRequestView) Raw() []byte { return v.raw }
1390+
1391+// Size is the element's wire size, including trailing alignment.
1392+func (v SetupRequestView) Size() int { return len(v.raw) }
1393+func sizeSetupRequest(p []byte) int {
1394+ if len(p) < 12 {
1395+ return -1
1396+ }
1397+ v := SetupRequestView{raw: p}
1398+ _ = v
1399+ off := 12
1400+ {
1401+ n := int(v.AuthorizationProtocolNameLen())
1402+ if n < 0 || n > len(v.raw) {
1403+ return -1
1404+ }
1405+ off += n * 1
1406+ }
1407+ if off > len(v.raw) {
1408+ return -1
1409+ }
1410+ off = (off + 3) &^ 3
1411+ {
1412+ n := int(v.AuthorizationProtocolDataLen())
1413+ if n < 0 || n > len(v.raw) {
1414+ return -1
1415+ }
1416+ off += n * 1
1417+ }
1418+ if off > len(v.raw) {
1419+ return -1
1420+ }
1421+ off = (off + 3) &^ 3
1422+ if off > len(v.raw) {
1423+ return -1
1424+ }
1425+ return off
1426+}
1427+func (v SetupRequestView) ByteOrder() uint8 { return uint8(v.raw[0:][0]) }
1428+func (v SetupRequestView) ProtocolMajorVersion() uint16 {
1429+ return uint16(binary.LittleEndian.Uint16(v.raw[2:]))
1430+}
1431+func (v SetupRequestView) ProtocolMinorVersion() uint16 {
1432+ return uint16(binary.LittleEndian.Uint16(v.raw[4:]))
1433+}
1434+func (v SetupRequestView) AuthorizationProtocolNameLen() uint16 {
1435+ return uint16(binary.LittleEndian.Uint16(v.raw[6:]))
1436+}
1437+func (v SetupRequestView) AuthorizationProtocolDataLen() uint16 {
1438+ return uint16(binary.LittleEndian.Uint16(v.raw[8:]))
1439+}
1440+func (v SetupRequestView) AuthorizationProtocolName() []byte {
1441+ off := 12
1442+ end := off + int(v.AuthorizationProtocolNameLen())
1443+ return v.raw[off:end:end]
1444+}
1445+func (v SetupRequestView) AuthorizationProtocolData() []byte {
1446+ off := 12
1447+ {
1448+ n := int(v.AuthorizationProtocolNameLen())
1449+ off += n * 1
1450+ }
1451+ off = (off + 3) &^ 3
1452+ end := off + int(v.AuthorizationProtocolDataLen())
1453+ return v.raw[off:end:end]
1454+}
1455+
1456+// SetupRequestIter walks n consecutive SetupRequest elements; Err reports a malformed length.
1457+// It shares the lifetime of the bytes it walks.
1458+type SetupRequestIter struct {
1459+ b []byte
1460+ n int
1461+ v SetupRequestView
1462+ err bool
1463+}
1464+
1465+// NewSetupRequestIter iterates n elements encoded at the start of b.
1466+func NewSetupRequestIter(b []byte, n int) SetupRequestIter { return SetupRequestIter{b: b, n: n} }
1467+func (it *SetupRequestIter) Next() bool {
1468+ if it.n <= 0 || it.err {
1469+ return false
1470+ }
1471+ s := sizeSetupRequest(it.b)
1472+ if s < 0 {
1473+ it.err = true
1474+ it.n = 0
1475+ return false
1476+ }
1477+ it.v = SetupRequestView{raw: it.b[:s:s]}
1478+ it.b = it.b[s:]
1479+ it.n--
1480+ return true
1481+}
1482+func (it *SetupRequestIter) Value() SetupRequestView { return it.v }
1483+func (it *SetupRequestIter) Err() bool { return it.err }
1484+
1485+// SetupFailedView is a zero-copy view over one wire-encoded SetupFailed, yielded by SetupFailedIter.
1486+// It shares the lifetime of the bytes it was taken from (for replies: until the Handler callback returns). Copy what you keep.
1487+type SetupFailedView struct{ raw []byte }
1488+
1489+func (v SetupFailedView) Raw() []byte { return v.raw }
1490+
1491+// Size is the element's wire size, including trailing alignment.
1492+func (v SetupFailedView) Size() int { return len(v.raw) }
1493+func sizeSetupFailed(p []byte) int {
1494+ if len(p) < 8 {
1495+ return -1
1496+ }
1497+ v := SetupFailedView{raw: p}
1498+ _ = v
1499+ off := 8
1500+ {
1501+ n := int(v.ReasonLen())
1502+ if n < 0 || n > len(v.raw) {
1503+ return -1
1504+ }
1505+ off += n * 1
1506+ }
1507+ if off > len(v.raw) {
1508+ return -1
1509+ }
1510+ if off > len(v.raw) {
1511+ return -1
1512+ }
1513+ return off
1514+}
1515+func (v SetupFailedView) Status() uint8 { return uint8(v.raw[0:][0]) }
1516+func (v SetupFailedView) ReasonLen() uint8 { return uint8(v.raw[1:][0]) }
1517+func (v SetupFailedView) ProtocolMajorVersion() uint16 {
1518+ return uint16(binary.LittleEndian.Uint16(v.raw[2:]))
1519+}
1520+func (v SetupFailedView) ProtocolMinorVersion() uint16 {
1521+ return uint16(binary.LittleEndian.Uint16(v.raw[4:]))
1522+}
1523+func (v SetupFailedView) Length() uint16 { return uint16(binary.LittleEndian.Uint16(v.raw[6:])) }
1524+func (v SetupFailedView) Reason() []byte {
1525+ off := 8
1526+ end := off + int(v.ReasonLen())
1527+ return v.raw[off:end:end]
1528+}
1529+
1530+// SetupFailedIter walks n consecutive SetupFailed elements; Err reports a malformed length.
1531+// It shares the lifetime of the bytes it walks.
1532+type SetupFailedIter struct {
1533+ b []byte
1534+ n int
1535+ v SetupFailedView
1536+ err bool
1537+}
1538+
1539+// NewSetupFailedIter iterates n elements encoded at the start of b.
1540+func NewSetupFailedIter(b []byte, n int) SetupFailedIter { return SetupFailedIter{b: b, n: n} }
1541+func (it *SetupFailedIter) Next() bool {
1542+ if it.n <= 0 || it.err {
1543+ return false
1544+ }
1545+ s := sizeSetupFailed(it.b)
1546+ if s < 0 {
1547+ it.err = true
1548+ it.n = 0
1549+ return false
1550+ }
1551+ it.v = SetupFailedView{raw: it.b[:s:s]}
1552+ it.b = it.b[s:]
1553+ it.n--
1554+ return true
1555+}
1556+func (it *SetupFailedIter) Value() SetupFailedView { return it.v }
1557+func (it *SetupFailedIter) Err() bool { return it.err }
1558+
1559+// SetupAuthenticateView is a zero-copy view over one wire-encoded SetupAuthenticate, yielded by SetupAuthenticateIter.
1560+// It shares the lifetime of the bytes it was taken from (for replies: until the Handler callback returns). Copy what you keep.
1561+type SetupAuthenticateView struct{ raw []byte }
1562+
1563+func (v SetupAuthenticateView) Raw() []byte { return v.raw }
1564+
1565+// Size is the element's wire size, including trailing alignment.
1566+func (v SetupAuthenticateView) Size() int { return len(v.raw) }
1567+func sizeSetupAuthenticate(p []byte) int {
1568+ if len(p) < 8 {
1569+ return -1
1570+ }
1571+ v := SetupAuthenticateView{raw: p}
1572+ _ = v
1573+ off := 8
1574+ {
1575+ n := (int(v.Length()) * 4)
1576+ if n < 0 || n > len(v.raw) {
1577+ return -1
1578+ }
1579+ off += n * 1
1580+ }
1581+ if off > len(v.raw) {
1582+ return -1
1583+ }
1584+ if off > len(v.raw) {
1585+ return -1
1586+ }
1587+ return off
1588+}
1589+func (v SetupAuthenticateView) Status() uint8 { return uint8(v.raw[0:][0]) }
1590+func (v SetupAuthenticateView) Length() uint16 { return uint16(binary.LittleEndian.Uint16(v.raw[6:])) }
1591+func (v SetupAuthenticateView) Reason() []byte {
1592+ off := 8
1593+ end := off + (int(v.Length()) * 4)
1594+ return v.raw[off:end:end]
1595+}
1596+
1597+// SetupAuthenticateIter walks n consecutive SetupAuthenticate elements; Err reports a malformed length.
1598+// It shares the lifetime of the bytes it walks.
1599+type SetupAuthenticateIter struct {
1600+ b []byte
1601+ n int
1602+ v SetupAuthenticateView
1603+ err bool
1604+}
1605+
1606+// NewSetupAuthenticateIter iterates n elements encoded at the start of b.
1607+func NewSetupAuthenticateIter(b []byte, n int) SetupAuthenticateIter {
1608+ return SetupAuthenticateIter{b: b, n: n}
1609+}
1610+func (it *SetupAuthenticateIter) Next() bool {
1611+ if it.n <= 0 || it.err {
1612+ return false
1613+ }
1614+ s := sizeSetupAuthenticate(it.b)
1615+ if s < 0 {
1616+ it.err = true
1617+ it.n = 0
1618+ return false
1619+ }
1620+ it.v = SetupAuthenticateView{raw: it.b[:s:s]}
1621+ it.b = it.b[s:]
1622+ it.n--
1623+ return true
1624+}
1625+func (it *SetupAuthenticateIter) Value() SetupAuthenticateView { return it.v }
1626+func (it *SetupAuthenticateIter) Err() bool { return it.err }
1627+
1628+// SetupView is a zero-copy view over one wire-encoded Setup, yielded by SetupIter.
1629+// It shares the lifetime of the bytes it was taken from (for replies: until the Handler callback returns). Copy what you keep.
1630+type SetupView struct{ raw []byte }
1631+
1632+func (v SetupView) Raw() []byte { return v.raw }
1633+
1634+// Size is the element's wire size, including trailing alignment.
1635+func (v SetupView) Size() int { return len(v.raw) }
1636+func sizeSetup(p []byte) int {
1637+ if len(p) < 40 {
1638+ return -1
1639+ }
1640+ v := SetupView{raw: p}
1641+ _ = v
1642+ off := 40
1643+ {
1644+ n := int(v.VendorLen())
1645+ if n < 0 || n > len(v.raw) {
1646+ return -1
1647+ }
1648+ off += n * 1
1649+ }
1650+ if off > len(v.raw) {
1651+ return -1
1652+ }
1653+ off = (off + 3) &^ 3
1654+ {
1655+ n := int(v.PixmapFormatsLen())
1656+ if n < 0 || n > len(v.raw) {
1657+ return -1
1658+ }
1659+ off += n * 8
1660+ }
1661+ if off > len(v.raw) {
1662+ return -1
1663+ }
1664+ {
1665+ n := int(v.RootsLen())
1666+ if off > len(v.raw) || n < 0 || n > len(v.raw) {
1667+ return -1
1668+ }
1669+ it := NewScreenIter(v.raw[off:], n)
1670+ for it.Next() {
1671+ off += it.Value().Size()
1672+ }
1673+ if it.Err() {
1674+ return -1
1675+ }
1676+ }
1677+ if off > len(v.raw) {
1678+ return -1
1679+ }
1680+ return off
1681+}
1682+func (v SetupView) Status() uint8 { return uint8(v.raw[0:][0]) }
1683+func (v SetupView) ProtocolMajorVersion() uint16 {
1684+ return uint16(binary.LittleEndian.Uint16(v.raw[2:]))
1685+}
1686+func (v SetupView) ProtocolMinorVersion() uint16 {
1687+ return uint16(binary.LittleEndian.Uint16(v.raw[4:]))
1688+}
1689+func (v SetupView) Length() uint16 { return uint16(binary.LittleEndian.Uint16(v.raw[6:])) }
1690+func (v SetupView) ReleaseNumber() uint32 { return uint32(binary.LittleEndian.Uint32(v.raw[8:])) }
1691+func (v SetupView) ResourceIdBase() uint32 { return uint32(binary.LittleEndian.Uint32(v.raw[12:])) }
1692+func (v SetupView) ResourceIdMask() uint32 { return uint32(binary.LittleEndian.Uint32(v.raw[16:])) }
1693+func (v SetupView) MotionBufferSize() uint32 { return uint32(binary.LittleEndian.Uint32(v.raw[20:])) }
1694+func (v SetupView) VendorLen() uint16 { return uint16(binary.LittleEndian.Uint16(v.raw[24:])) }
1695+func (v SetupView) MaximumRequestLength() uint16 {
1696+ return uint16(binary.LittleEndian.Uint16(v.raw[26:]))
1697+}
1698+func (v SetupView) RootsLen() uint8 { return uint8(v.raw[28:][0]) }
1699+func (v SetupView) PixmapFormatsLen() uint8 { return uint8(v.raw[29:][0]) }
1700+func (v SetupView) ImageByteOrder() ImageOrder { return ImageOrder(v.raw[30:][0]) }
1701+func (v SetupView) BitmapFormatBitOrder() ImageOrder { return ImageOrder(v.raw[31:][0]) }
1702+func (v SetupView) BitmapFormatScanlineUnit() uint8 { return uint8(v.raw[32:][0]) }
1703+func (v SetupView) BitmapFormatScanlinePad() uint8 { return uint8(v.raw[33:][0]) }
1704+func (v SetupView) MinKeycode() Keycode { return Keycode(v.raw[34:][0]) }
1705+func (v SetupView) MaxKeycode() Keycode { return Keycode(v.raw[35:][0]) }
1706+func (v SetupView) Vendor() []byte {
1707+ off := 40
1708+ end := off + int(v.VendorLen())
1709+ return v.raw[off:end:end]
1710+}
1711+func (v SetupView) PixmapFormats() FormatList {
1712+ off := 40
1713+ {
1714+ n := int(v.VendorLen())
1715+ off += n * 1
1716+ }
1717+ off = (off + 3) &^ 3
1718+ end := off + (int(v.PixmapFormatsLen()))*8
1719+ return FormatList{b: v.raw[off:end:end]}
1720+}
1721+func (v SetupView) Roots() ScreenIter {
1722+ off := 40
1723+ {
1724+ n := int(v.VendorLen())
1725+ off += n * 1
1726+ }
1727+ off = (off + 3) &^ 3
1728+ {
1729+ n := int(v.PixmapFormatsLen())
1730+ off += n * 8
1731+ }
1732+ return NewScreenIter(v.raw[off:], int(v.RootsLen()))
1733+}
1734+
1735+// SetupIter walks n consecutive Setup elements; Err reports a malformed length.
1736+// It shares the lifetime of the bytes it walks.
1737+type SetupIter struct {
1738+ b []byte
1739+ n int
1740+ v SetupView
1741+ err bool
1742+}
1743+
1744+// NewSetupIter iterates n elements encoded at the start of b.
1745+func NewSetupIter(b []byte, n int) SetupIter { return SetupIter{b: b, n: n} }
1746+func (it *SetupIter) Next() bool {
1747+ if it.n <= 0 || it.err {
1748+ return false
1749+ }
1750+ s := sizeSetup(it.b)
1751+ if s < 0 {
1752+ it.err = true
1753+ it.n = 0
1754+ return false
1755+ }
1756+ it.v = SetupView{raw: it.b[:s:s]}
1757+ it.b = it.b[s:]
1758+ it.n--
1759+ return true
1760+}
1761+func (it *SetupIter) Value() SetupView { return it.v }
1762+func (it *SetupIter) Err() bool { return it.err }
1763+
1764+// StrView is a zero-copy view over one wire-encoded Str, yielded by StrIter.
1765+// It shares the lifetime of the bytes it was taken from (for replies: until the Handler callback returns). Copy what you keep.
1766+type StrView struct{ raw []byte }
1767+
1768+func (v StrView) Raw() []byte { return v.raw }
1769+
1770+// Size is the element's wire size, including trailing alignment.
1771+func (v StrView) Size() int { return len(v.raw) }
1772+func sizeStr(p []byte) int {
1773+ if len(p) < 1 {
1774+ return -1
1775+ }
1776+ v := StrView{raw: p}
1777+ _ = v
1778+ off := 1
1779+ {
1780+ n := int(v.NameLen())
1781+ if n < 0 || n > len(v.raw) {
1782+ return -1
1783+ }
1784+ off += n * 1
1785+ }
1786+ if off > len(v.raw) {
1787+ return -1
1788+ }
1789+ if off > len(v.raw) {
1790+ return -1
1791+ }
1792+ return off
1793+}
1794+func (v StrView) NameLen() uint8 { return uint8(v.raw[0:][0]) }
1795+func (v StrView) Name() []byte { off := 1; end := off + int(v.NameLen()); return v.raw[off:end:end] }
1796+
1797+// StrIter walks n consecutive Str elements; Err reports a malformed length.
1798+// It shares the lifetime of the bytes it walks.
1799+type StrIter struct {
1800+ b []byte
1801+ n int
1802+ v StrView
1803+ err bool
1804+}
1805+
1806+// NewStrIter iterates n elements encoded at the start of b.
1807+func NewStrIter(b []byte, n int) StrIter { return StrIter{b: b, n: n} }
1808+func (it *StrIter) Next() bool {
1809+ if it.n <= 0 || it.err {
1810+ return false
1811+ }
1812+ s := sizeStr(it.b)
1813+ if s < 0 {
1814+ it.err = true
1815+ it.n = 0
1816+ return false
1817+ }
1818+ it.v = StrView{raw: it.b[:s:s]}
1819+ it.b = it.b[s:]
1820+ it.n--
1821+ return true
1822+}
1823+func (it *StrIter) Value() StrView { return it.v }
1824+func (it *StrIter) Err() bool { return it.err }
1825+
1826+// HostView is a zero-copy view over one wire-encoded Host, yielded by HostIter.
1827+// It shares the lifetime of the bytes it was taken from (for replies: until the Handler callback returns). Copy what you keep.
1828+type HostView struct{ raw []byte }
1829+
1830+func (v HostView) Raw() []byte { return v.raw }
1831+
1832+// Size is the element's wire size, including trailing alignment.
1833+func (v HostView) Size() int { return len(v.raw) }
1834+func sizeHost(p []byte) int {
1835+ if len(p) < 4 {
1836+ return -1
1837+ }
1838+ v := HostView{raw: p}
1839+ _ = v
1840+ off := 4
1841+ {
1842+ n := int(v.AddressLen())
1843+ if n < 0 || n > len(v.raw) {
1844+ return -1
1845+ }
1846+ off += n * 1
1847+ }
1848+ if off > len(v.raw) {
1849+ return -1
1850+ }
1851+ off = (off + 3) &^ 3
1852+ if off > len(v.raw) {
1853+ return -1
1854+ }
1855+ return off
1856+}
1857+func (v HostView) Family() Family { return Family(v.raw[0:][0]) }
1858+func (v HostView) AddressLen() uint16 { return uint16(binary.LittleEndian.Uint16(v.raw[2:])) }
1859+func (v HostView) Address() []byte {
1860+ off := 4
1861+ end := off + int(v.AddressLen())
1862+ return v.raw[off:end:end]
1863+}
1864+
1865+// HostIter walks n consecutive Host elements; Err reports a malformed length.
1866+// It shares the lifetime of the bytes it walks.
1867+type HostIter struct {
1868+ b []byte
1869+ n int
1870+ v HostView
1871+ err bool
1872+}
1873+
1874+// NewHostIter iterates n elements encoded at the start of b.
1875+func NewHostIter(b []byte, n int) HostIter { return HostIter{b: b, n: n} }
1876+func (it *HostIter) Next() bool {
1877+ if it.n <= 0 || it.err {
1878+ return false
1879+ }
1880+ s := sizeHost(it.b)
1881+ if s < 0 {
1882+ it.err = true
1883+ it.n = 0
1884+ return false
1885+ }
1886+ it.v = HostView{raw: it.b[:s:s]}
1887+ it.b = it.b[s:]
1888+ it.n--
1889+ return true
1890+}
1891+func (it *HostIter) Value() HostView { return it.v }
1892+func (it *HostIter) Err() bool { return it.err }
1893+
1894+// AtomList is a borrowed view over a wire list of Atom. It shares the lifetime of the bytes it was taken from; copy what you keep.
1895+type AtomList struct{ b []byte }
1896+
1897+func (l AtomList) Len() int { return len(l.b) / 4 }
1898+func (l AtomList) At(i int) Atom { return Atom(binary.LittleEndian.Uint32(l.b[i*4:])) }
1899+
1900+// CharinfoList is a borrowed view over a wire list of Charinfo. It shares the lifetime of the bytes it was taken from; copy what you keep.
1901+type CharinfoList struct{ b []byte }
1902+
1903+func (l CharinfoList) Len() int { return len(l.b) / 12 }
1904+func (l CharinfoList) At(i int) Charinfo { return GetCharinfo(l.b[i*12:]) }
1905+
1906+// ColormapList is a borrowed view over a wire list of Colormap. It shares the lifetime of the bytes it was taken from; copy what you keep.
1907+type ColormapList struct{ b []byte }
1908+
1909+func (l ColormapList) Len() int { return len(l.b) / 4 }
1910+func (l ColormapList) At(i int) Colormap { return Colormap(binary.LittleEndian.Uint32(l.b[i*4:])) }
1911+
1912+// FontpropList is a borrowed view over a wire list of Fontprop. It shares the lifetime of the bytes it was taken from; copy what you keep.
1913+type FontpropList struct{ b []byte }
1914+
1915+func (l FontpropList) Len() int { return len(l.b) / 8 }
1916+func (l FontpropList) At(i int) Fontprop { return GetFontprop(l.b[i*8:]) }
1917+
1918+// FormatList is a borrowed view over a wire list of Format. It shares the lifetime of the bytes it was taken from; copy what you keep.
1919+type FormatList struct{ b []byte }
1920+
1921+func (l FormatList) Len() int { return len(l.b) / 8 }
1922+func (l FormatList) At(i int) Format { return GetFormat(l.b[i*8:]) }
1923+
1924+// KeycodeList is a borrowed view over a wire list of Keycode. It shares the lifetime of the bytes it was taken from; copy what you keep.
1925+type KeycodeList struct{ b []byte }
1926+
1927+func (l KeycodeList) Len() int { return len(l.b) / 1 }
1928+func (l KeycodeList) At(i int) Keycode { return Keycode(l.b[i*1:][0]) }
1929+
1930+// KeysymList is a borrowed view over a wire list of Keysym. It shares the lifetime of the bytes it was taken from; copy what you keep.
1931+type KeysymList struct{ b []byte }
1932+
1933+func (l KeysymList) Len() int { return len(l.b) / 4 }
1934+func (l KeysymList) At(i int) Keysym { return Keysym(binary.LittleEndian.Uint32(l.b[i*4:])) }
1935+
1936+// RgbList is a borrowed view over a wire list of Rgb. It shares the lifetime of the bytes it was taken from; copy what you keep.
1937+type RgbList struct{ b []byte }
1938+
1939+func (l RgbList) Len() int { return len(l.b) / 8 }
1940+func (l RgbList) At(i int) Rgb { return GetRgb(l.b[i*8:]) }
1941+
1942+// TimecoordList is a borrowed view over a wire list of Timecoord. It shares the lifetime of the bytes it was taken from; copy what you keep.
1943+type TimecoordList struct{ b []byte }
1944+
1945+func (l TimecoordList) Len() int { return len(l.b) / 8 }
1946+func (l TimecoordList) At(i int) Timecoord { return GetTimecoord(l.b[i*8:]) }
1947+
1948+// Uint32List is a borrowed view over a wire list of uint32. It shares the lifetime of the bytes it was taken from; copy what you keep.
1949+type Uint32List struct{ b []byte }
1950+
1951+func (l Uint32List) Len() int { return len(l.b) / 4 }
1952+func (l Uint32List) At(i int) uint32 { return uint32(binary.LittleEndian.Uint32(l.b[i*4:])) }
1953+
1954+// VisualtypeList is a borrowed view over a wire list of Visualtype. It shares the lifetime of the bytes it was taken from; copy what you keep.
1955+type VisualtypeList struct{ b []byte }
1956+
1957+func (l VisualtypeList) Len() int { return len(l.b) / 24 }
1958+func (l VisualtypeList) At(i int) Visualtype { return GetVisualtype(l.b[i*24:]) }
1959+
1960+// WindowList is a borrowed view over a wire list of Window. It shares the lifetime of the bytes it was taken from; copy what you keep.
1961+type WindowList struct{ b []byte }
1962+
1963+func (l WindowList) Len() int { return len(l.b) / 4 }
1964+func (l WindowList) At(i int) Window { return Window(binary.LittleEndian.Uint32(l.b[i*4:])) }

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