aboutsummaryrefslogtreecommitdiff
path: root/nexgb/xgbgen/request_reply.go
blob: 5032e316c507c3c26fed7a9b3dcf268dc13a3b02 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package main

import (
	"fmt"
	"log"
	"unicode"
)

// Request represents all XML 'request' nodes.
// If the request doesn't have a reply, Reply is nil.
type Request struct {
	srcName string // The Go name of this request.
	xmlName string // The XML name of this request.
	Opcode  int
	Combine bool    // Not currently used.
	Fields  []Field // All fields in the request.
	Reply   *Reply  // A reply, if one exists for this request.
}

type Requests []*Request

func (rs Requests) Len() int           { return len(rs) }
func (rs Requests) Swap(i, j int)      { rs[i], rs[j] = rs[j], rs[i] }
func (rs Requests) Less(i, j int) bool { return rs[i].xmlName < rs[j].xmlName }

// Initialize creates the proper Go source name for this request.
// It also initializes the reply if one exists, and all fields in this request.
func (r *Request) Initialize(p *Protocol) {
	r.srcName = SrcName(p, r.xmlName)
	if p.isExt() {
		r.srcName = r.srcName
	}

	if r.Reply != nil {
		r.Reply.Initialize(p)
	}
	for _, field := range r.Fields {
		field.Initialize(p)
	}
}

func (r *Request) SrcName() string {
	return r.srcName
}

func (r *Request) XmlName() string {
	return r.xmlName
}

// ReplyName gets the Go source name of the function that generates a
// reply type from a slice of bytes.
// The generated function is not currently exported.
func (r *Request) ReplyName() string {
	if r.Reply == nil {
		log.Panicf("Cannot call 'ReplyName' on request %s, which has no reply.",
			r.SrcName())
	}
	name := r.SrcName()
	lower := string(unicode.ToLower(rune(name[0]))) + name[1:]
	return fmt.Sprintf("%sReply", lower)
}

// ReplyTypeName gets the Go source name of the type holding all reply data
// for this request.
func (r *Request) ReplyTypeName() string {
	if r.Reply == nil {
		log.Panicf("Cannot call 'ReplyName' on request %s, which has no reply.",
			r.SrcName())
	}
	return fmt.Sprintf("%sReply", r.SrcName())
}

// ReqName gets the Go source name of the function that generates a byte
// slice from a list of parameters.
// The generated function is not currently exported.
func (r *Request) ReqName() string {
	name := r.SrcName()
	lower := string(unicode.ToLower(rune(name[0]))) + name[1:]
	return fmt.Sprintf("%sRequest", lower)
}

// CookieName gets the Go source name of the type that holds cookies for
// this request.
func (r *Request) CookieName() string {
	return fmt.Sprintf("%sCookie", r.SrcName())
}

// Size for Request needs a context.
// Namely, if this is an extension, we need to account for *four* bytes
// of a header (extension opcode, request opcode, and the sequence number).
// If it's a core protocol request, then we only account for *three*
// bytes of the header (remove the extension opcode).
func (r *Request) Size(c *Context) Size {
	size := newFixedSize(0, true)

	// If this is a core protocol request, we squeeze in an extra byte of
	// data (from the fields below) between the opcode and the size of the
	// request. In an extension request, this byte is always occupied
	// by the opcode of the request (while the first byte is always occupied
	// by the opcode of the extension).
	if !c.protocol.isExt() {
		size = size.Add(newFixedSize(3, true))
	} else {
		size = size.Add(newFixedSize(4, true))
	}

	for _, field := range r.Fields {
		switch field := field.(type) {
		case *LocalField: // local fields don't go over the wire
			continue
		case *SingleField:
			fsz := field.Size()
			if _, isstruct := field.Type.(*Struct); isstruct {
				fsz.Expression = fsz.Expression.Specialize(field.SrcName())
			}
			size = size.Add(fsz)
		default:
			size = size.Add(field.Size())
		}
	}
	return newExpressionSize(&Padding{
		Expr: size.Expression,
	}, size.exact)
}

// Reply encapsulates the fields associated with a 'reply' element.
type Reply struct {
	Fields []Field
}

// Size gets the number of bytes in this request's reply.
// A reply always has at least 7 bytes:
// 1 byte: A reply discriminant (first byte set to 1)
// 2 bytes: A sequence number
// 4 bytes: Number of additional bytes in 4-byte units past initial 32 bytes.
func (r *Reply) Size() Size {
	size := newFixedSize(0, true)

	// Account for reply discriminant, sequence number and reply length
	size = size.Add(newFixedSize(7, true))

	for _, field := range r.Fields {
		size = size.Add(field.Size())
	}
	return size
}

func (r *Reply) Initialize(p *Protocol) {
	for _, field := range r.Fields {
		field.Initialize(p)
	}
}