1package main
2
3import (
4 "fmt"
5 "sort"
6 "strings"
7)
8
9type ValueField struct {
10 Field Field
11 Bit string
12 Value int64
13}
14type ValueList struct {
15 Mask Field
16 Fields []ValueField
17}
18
19func resolveValues(m *Module, req *Node, name string) (*ValueList, error) {
20 var sw *Node
21 for _, n := range req.Children {
22 if n.Kind == "switch" {
23 if sw != nil {
24 return nil, n.fail("multiple value switches are unsupported")
25 }
26 sw = n
27 }
28 }
29 if sw == nil {
30 return nil, nil
31 }
32 if len(sw.Children) < 2 || sw.Children[0].Kind != "fieldref" {
33 return nil, sw.fail("value switch requires a mask fieldref and bitcases")
34 }
35 var mask *Node
36 seenSwitch := false
37 for _, n := range req.Children {
38 if n == sw {
39 seenSwitch = true
40 continue
41 }
42 if seenSwitch && n.Kind != "doc" && n.Kind != "reply" {
43 return nil, n.fail("value switch must be trailing")
44 }
45 if n.Kind == "list" {
46 return nil, n.fail("lists combined with value switches are unsupported")
47 }
48 if !seenSwitch && n.Kind == "field" && n.Attr["name"] == sw.Children[0].Text {
49 mask = n
50 }
51 }
52 if mask == nil || mask.Attr["mask"] == "" {
53 return nil, sw.fail("value switch requires a preceding typed mask")
54 }
55 mt, err := lookup(m, mask.Attr["type"])
56 if err != nil {
57 return nil, mask.fail("%v", err)
58 }
59 e, owner := findEnum(m, mask.Attr["mask"])
60 if e == nil {
61 return nil, mask.fail("unknown mask %s", mask.Attr["mask"])
62 }
63 if mt.Size > 4 || !isScalar(mt) {
64 return nil, mask.fail("value mask must fit in 32 bits")
65 }
66 v := &ValueList{Mask: Field{Node: mask, GoType: qualified(m, owner, e.Name)}}
67 used := map[int64]bool{}
68 names := map[string]bool{"Mask": true}
69 for _, bc := range sw.Children[1:] {
70 var members []*Node
71 for _, c := range bc.Children {
72 if c.Kind != "doc" {
73 members = append(members, c)
74 }
75 }
76 if bc.Kind != "bitcase" || len(members) != 2 || members[0].Kind != "enumref" || members[1].Kind != "field" {
77 return nil, bc.fail("value switch requires one enumref and one 4-byte field per bitcase")
78 }
79 bit, n := members[0], members[1]
80 be, bo := findEnum(m, bit.Attr["ref"])
81 if be != e || bo != owner {
82 return nil, bit.fail("bitcase enum must match value mask")
83 }
84 var value int64
85 found := false
86 for _, item := range e.Items {
87 if item.Name == e.Name+goName(bit.Text) {
88 value = item.Value
89 found = true
90 }
91 }
92 if !found || value <= 0 || value&(value-1) != 0 || uint64(value) >= uint64(1)<<(8*mt.Size) || used[value] {
93 return nil, bit.fail("bitcase requires a unique mask bit")
94 }
95 used[value] = true
96 typ, err := lookup(m, n.Attr["type"])
97 if err != nil {
98 return nil, n.fail("%v", err)
99 }
100 if typ.Size != 4 || !isScalar(typ) {
101 return nil, n.fail("value-list bitcase requires a 4-byte scalar field")
102 }
103 f := Field{Name: goName(n.Attr["name"]), GoType: qualified(m, typ.Module, typ.GoType), Type: typ, Node: n}
104 for _, attr := range []string{"enum", "mask"} {
105 if ref := n.Attr[attr]; ref != "" {
106 fe, fo := findEnum(m, ref)
107 if fe == nil {
108 return nil, n.fail("unknown enum %s", ref)
109 }
110 f.GoType = qualified(m, fo, fe.Name)
111 }
112 }
113 if names[f.Name] {
114 return nil, n.fail("value field name collision %s", f.Name)
115 }
116 names[f.Name] = true
117 v.Fields = append(v.Fields, ValueField{Field: f, Bit: qualified(m, owner, e.Name+goName(bit.Text)), Value: value})
118 }
119 sort.Slice(v.Fields, func(i, j int) bool { return v.Fields[i].Value < v.Fields[j].Value })
120 if err := m.claim(name+"Values", sw); err != nil {
121 return nil, err
122 }
123 return v, nil
124}
125func valuesDecl(name string, v *ValueList) string {
126 var b strings.Builder
127 fmt.Fprintf(&b, "type %sValues struct { Mask %s;", name, v.Mask.GoType)
128 for _, f := range v.Fields {
129 fmt.Fprintf(&b, "%s %s;", f.Field.Name, f.Field.GoType)
130 }
131 b.WriteString("};\n")
132 for _, f := range v.Fields {
133 fmt.Fprintf(&b, "func(v *%sValues) Set%s(x %s) *%sValues { v.Mask |= %s; v.%s=x; return v };\n", name, f.Field.Name, f.Field.GoType, name, f.Bit, f.Field.Name)
134 }
135 return b.String()
136}