package main import ( "fmt" "sort" "strings" ) type ValueField struct { Field Field Bit string Value int64 } type ValueList struct { Mask Field Fields []ValueField } func resolveValues(m *Module, req *Node, name string) (*ValueList, error) { var sw *Node for _, n := range req.Children { if n.Kind == "switch" { if sw != nil { return nil, n.fail("multiple value switches are unsupported") } sw = n } } if sw == nil { return nil, nil } if len(sw.Children) < 2 || sw.Children[0].Kind != "fieldref" { return nil, sw.fail("value switch requires a mask fieldref and bitcases") } var mask *Node seenSwitch := false for _, n := range req.Children { if n == sw { seenSwitch = true continue } if seenSwitch && n.Kind != "doc" && n.Kind != "reply" { return nil, n.fail("value switch must be trailing") } if n.Kind == "list" { return nil, n.fail("lists combined with value switches are unsupported") } if !seenSwitch && n.Kind == "field" && n.Attr["name"] == sw.Children[0].Text { mask = n } } if mask == nil || mask.Attr["mask"] == "" { return nil, sw.fail("value switch requires a preceding typed mask") } mt, err := lookup(m, mask.Attr["type"]) if err != nil { return nil, mask.fail("%v", err) } e, owner := findEnum(m, mask.Attr["mask"]) if e == nil { return nil, mask.fail("unknown mask %s", mask.Attr["mask"]) } if mt.Size > 4 || !isScalar(mt) { return nil, mask.fail("value mask must fit in 32 bits") } v := &ValueList{Mask: Field{Node: mask, GoType: qualified(m, owner, e.Name)}} used := map[int64]bool{} names := map[string]bool{"Mask": true} for _, bc := range sw.Children[1:] { var members []*Node for _, c := range bc.Children { if c.Kind != "doc" { members = append(members, c) } } if bc.Kind != "bitcase" || len(members) != 2 || members[0].Kind != "enumref" || members[1].Kind != "field" { return nil, bc.fail("value switch requires one enumref and one 4-byte field per bitcase") } bit, n := members[0], members[1] be, bo := findEnum(m, bit.Attr["ref"]) if be != e || bo != owner { return nil, bit.fail("bitcase enum must match value mask") } var value int64 found := false for _, item := range e.Items { if item.Name == e.Name+goName(bit.Text) { value = item.Value found = true } } if !found || value <= 0 || value&(value-1) != 0 || uint64(value) >= uint64(1)<<(8*mt.Size) || used[value] { return nil, bit.fail("bitcase requires a unique mask bit") } used[value] = true typ, err := lookup(m, n.Attr["type"]) if err != nil { return nil, n.fail("%v", err) } if typ.Size != 4 || !isScalar(typ) { return nil, n.fail("value-list bitcase requires a 4-byte scalar field") } f := Field{Name: goName(n.Attr["name"]), GoType: qualified(m, typ.Module, typ.GoType), Type: typ, Node: n} for _, attr := range []string{"enum", "mask"} { if ref := n.Attr[attr]; ref != "" { fe, fo := findEnum(m, ref) if fe == nil { return nil, n.fail("unknown enum %s", ref) } f.GoType = qualified(m, fo, fe.Name) } } if names[f.Name] { return nil, n.fail("value field name collision %s", f.Name) } names[f.Name] = true v.Fields = append(v.Fields, ValueField{Field: f, Bit: qualified(m, owner, e.Name+goName(bit.Text)), Value: value}) } sort.Slice(v.Fields, func(i, j int) bool { return v.Fields[i].Value < v.Fields[j].Value }) if err := m.claim(name+"Values", sw); err != nil { return nil, err } return v, nil } func valuesDecl(name string, v *ValueList) string { var b strings.Builder fmt.Fprintf(&b, "type %sValues struct { Mask %s;", name, v.Mask.GoType) for _, f := range v.Fields { fmt.Fprintf(&b, "%s %s;", f.Field.Name, f.Field.GoType) } b.WriteString("};\n") for _, f := range v.Fields { 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) } return b.String() }