diff options
author | Přemysl Janouch <p@janouch.name> | 2018-09-08 16:54:17 +0200 |
---|---|---|
committer | Přemysl Janouch <p@janouch.name> | 2018-09-08 16:54:17 +0200 |
commit | 3173202cc1e08762c6e156a8fffd23269a5ddb2b (patch) | |
tree | 95c4a06f8384d41b15e9c22afac0a387de79dc51 /nexgb/xgbgen/field.go | |
parent | 632b3ae494d45755525644fe5d04475c95aae364 (diff) | |
parent | 3906399e7c2a40fbaf355de572cf50a314083f64 (diff) | |
download | haven-3173202cc1e08762c6e156a8fffd23269a5ddb2b.tar.gz haven-3173202cc1e08762c6e156a8fffd23269a5ddb2b.tar.xz haven-3173202cc1e08762c6e156a8fffd23269a5ddb2b.zip |
Merge aarzilli/xgb, branch xcb1.12 as nexgb
History has been linearized and rewritten to stay under the new
subdirectory. I want to make changes incompatible to BurntSushi/xgb.
The history begs for being thrown away entirely because of its quality
and because it doesn't cover the Google period but it is still useful
for copyright tracking.
Diffstat (limited to 'nexgb/xgbgen/field.go')
-rw-r--r-- | nexgb/xgbgen/field.go | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/nexgb/xgbgen/field.go b/nexgb/xgbgen/field.go new file mode 100644 index 0000000..d6957b7 --- /dev/null +++ b/nexgb/xgbgen/field.go @@ -0,0 +1,398 @@ +package main + +import ( + "fmt" + "log" + "strings" +) + +// Field corresponds to any field described in an XML protocol description +// file. This includes struct fields, union fields, request fields, +// reply fields and so on. +// To make code generation easier, fields that have types are also stored. +// Note that not all fields support all methods defined in this interface. +// For instance, a padding field does not have a source name. +type Field interface { + // Initialize sets up the source name of this field. + Initialize(p *Protocol) + + // SrcName is the Go source name of this field. + SrcName() string + + // XmlName is the name of this field from the XML file. + XmlName() string + + // SrcType is the Go source type name of this field. + SrcType() string + + // Size returns an expression that computes the size (in bytes) + // of this field. + Size() Size + + // Define writes the Go code to declare this field (in a struct definition). + Define(c *Context) + + // Read writes the Go code to convert a byte slice to a Go value + // of this field. + // 'prefix' is the prefix of the name of the Go value. + Read(c *Context, prefix string) + + // Write writes the Go code to convert a Go value to a byte slice of + // this field. + // 'prefix' is the prefix of the name of the Go value. + Write(c *Context, prefix string) +} + +func (pad *PadField) Initialize(p *Protocol) {} + +// PadField represents any type of padding. It is omitted from +// definitions, but is used in Read/Write to increment the buffer index. +// It is also used in size calculation. +type PadField struct { + Bytes uint + Align uint16 +} + +func (p *PadField) SrcName() string { + panic("illegal to take source name of a pad field") +} + +func (p *PadField) XmlName() string { + panic("illegal to take XML name of a pad field") +} + +func (f *PadField) SrcType() string { + panic("it is illegal to call SrcType on a PadField field") +} + +func (p *PadField) Size() Size { + if p.Align > 0 { + return newFixedSize(uint(p.Align), false) + } else { + return newFixedSize(p.Bytes, true) + } +} + +type RequiredStartAlign struct { +} + +func (f *RequiredStartAlign) Initialize(p *Protocol) {} + +func (f *RequiredStartAlign) SrcName() string { + panic("illegal to take source name of a required_start_align field") +} + +func (f *RequiredStartAlign) XmlName() string { + panic("illegal to take XML name of a required_start_align field") +} + +func (f *RequiredStartAlign) SrcType() string { + panic("it is illegal to call SrcType on a required_start_align field") +} + +func (f *RequiredStartAlign) Size() Size { + return newFixedSize(0, true) +} + +func (f *RequiredStartAlign) Define(c *Context) {} + +func (f *RequiredStartAlign) Read(c *Context, prefix string) {} +func (f *RequiredStartAlign) Write(c *Context, prefix string) {} + +// SingleField represents most of the fields in an XML protocol description. +// It corresponds to any single value. +type SingleField struct { + srcName string + xmlName string + Type Type +} + +func (f *SingleField) Initialize(p *Protocol) { + f.srcName = SrcName(p, f.XmlName()) + f.Type = f.Type.(*Translation).RealType(p) +} + +func (f *SingleField) SrcName() string { + if f.srcName == "Bytes" { + return "Bytes_" + } + return f.srcName +} + +func (f *SingleField) XmlName() string { + return f.xmlName +} + +func (f *SingleField) SrcType() string { + return f.Type.SrcName() +} + +func (f *SingleField) Size() Size { + return f.Type.Size() +} + +// ListField represents a list of values. +type ListField struct { + srcName string + xmlName string + Type Type + LengthExpr Expression +} + +func (f *ListField) SrcName() string { + return f.srcName +} + +func (f *ListField) XmlName() string { + return f.xmlName +} + +func (f *ListField) SrcType() string { + if strings.ToLower(f.Type.XmlName()) == "char" { + return fmt.Sprintf("string") + } + return fmt.Sprintf("[]%s", f.Type.SrcName()) +} + +// Length computes the *number* of values in a list. +// If this ListField does not have any length expression, we throw our hands +// up and simply compute the 'len' of the field name of this list. +func (f *ListField) Length() Size { + if f.LengthExpr == nil { + return newExpressionSize(&Function{ + Name: "len", + Expr: &FieldRef{ + Name: f.SrcName(), + }, + }, true) + } + return newExpressionSize(f.LengthExpr, true) +} + +// Size computes the *size* of a list (in bytes). +// It it typically a simple matter of multiplying the length of the list by +// the size of the type of the list. +// But if it's a list of struct where the struct has a list field, we use a +// special function written in go_struct.go to compute the size (since the +// size in this case can only be computed recursively). +func (f *ListField) Size() Size { + elsz := f.Type.Size() + simpleLen := &Padding{ + Expr: newBinaryOp("*", f.Length().Expression, elsz.Expression), + } + + switch field := f.Type.(type) { + case *Struct: + if field.HasList() { + sizeFun := &Function{ + Name: fmt.Sprintf("%sListSize", f.Type.SrcName()), + Expr: &FieldRef{Name: f.SrcName()}, + } + return newExpressionSize(sizeFun, elsz.exact) + } else { + return newExpressionSize(simpleLen, elsz.exact) + } + case *Union: + return newExpressionSize(simpleLen, elsz.exact) + case *Base: + return newExpressionSize(simpleLen, elsz.exact) + case *Resource: + return newExpressionSize(simpleLen, elsz.exact) + case *TypeDef: + return newExpressionSize(simpleLen, elsz.exact) + default: + log.Panicf("Cannot compute list size with type '%T'.", f.Type) + } + panic("unreachable") +} + +func (f *ListField) Initialize(p *Protocol) { + f.srcName = SrcName(p, f.XmlName()) + f.Type = f.Type.(*Translation).RealType(p) + if f.LengthExpr != nil { + f.LengthExpr.Initialize(p) + } +} + +// LocalField is exactly the same as a regular SingleField, except it isn't +// sent over the wire. (i.e., it's probably used to compute an ExprField). +type LocalField struct { + *SingleField +} + +// ExprField is a field that is not parameterized, but is computed from values +// of other fields. +type ExprField struct { + srcName string + xmlName string + Type Type + Expr Expression +} + +func (f *ExprField) SrcName() string { + return f.srcName +} + +func (f *ExprField) XmlName() string { + return f.xmlName +} + +func (f *ExprField) SrcType() string { + return f.Type.SrcName() +} + +func (f *ExprField) Size() Size { + return f.Type.Size() +} + +func (f *ExprField) Initialize(p *Protocol) { + f.srcName = SrcName(p, f.XmlName()) + f.Type = f.Type.(*Translation).RealType(p) + f.Expr.Initialize(p) +} + +// ValueField represents two fields in one: a mask and a list of 4-byte +// integers. The mask specifies which kinds of values are in the list. +// (i.e., See ConfigureWindow, CreateWindow, ChangeWindowAttributes, etc.) +type ValueField struct { + Parent interface{} + MaskType Type + MaskName string + ListName string +} + +func (f *ValueField) SrcName() string { + panic("it is illegal to call SrcName on a ValueField field") +} + +func (f *ValueField) XmlName() string { + panic("it is illegal to call XmlName on a ValueField field") +} + +func (f *ValueField) SrcType() string { + return f.MaskType.SrcName() +} + +// Size computes the size in bytes of the combination of the mask and list +// in this value field. +// The expression to compute this looks complicated, but it's really just +// the number of bits set in the mask multiplied 4 (and padded of course). +func (f *ValueField) Size() Size { + maskSize := f.MaskType.Size() + listSize := newExpressionSize(&Function{ + Name: "xgb.Pad", + Expr: &BinaryOp{ + Op: "*", + Expr1: &Value{v: 4}, + Expr2: &PopCount{ + Expr: &Function{ + Name: "int", + Expr: &FieldRef{ + Name: f.MaskName, + }, + }, + }, + }, + }, true) + return maskSize.Add(listSize) +} + +func (f *ValueField) ListLength() Size { + return newExpressionSize(&PopCount{ + Expr: &Function{ + Name: "int", + Expr: &FieldRef{ + Name: f.MaskName, + }, + }, + }, true) +} + +func (f *ValueField) Initialize(p *Protocol) { + f.MaskType = f.MaskType.(*Translation).RealType(p) + f.MaskName = SrcName(p, f.MaskName) + f.ListName = SrcName(p, f.ListName) +} + +// SwitchField represents a 'switch' element in the XML protocol description +// file. +// Currently we translate this to a slice of uint32 and let the user sort +// through it. +type SwitchField struct { + xmlName string + Name string + MaskName string + Expr Expression + Bitcases []*Bitcase +} + +func (f *SwitchField) SrcName() string { + return f.Name +} + +func (f *SwitchField) XmlName() string { + return f.xmlName +} + +func (f *SwitchField) SrcType() string { + return "[]uint32" +} + +func (f *SwitchField) Size() Size { + // TODO: size expression used here is not correct unless every element of + // the switch is 32 bit long. This assumption holds for xproto but may not + // hold for other protocols (xkb?) + + listSize := newExpressionSize(&Function{ + Name: "xgb.Pad", + Expr: &BinaryOp{ + Op: "*", + Expr1: &Value{v: 4}, + Expr2: &PopCount{ + Expr: &Function{ + Name: "int", + Expr: &FieldRef{ + Name: f.MaskName, + }, + }, + }, + }, + }, true) + + return listSize +} + +func (f *SwitchField) ListLength() Size { + return newExpressionSize(&PopCount{ + Expr: &Function{ + Name: "int", + Expr: &FieldRef{ + Name: f.MaskName, + }, + }, + }, true) +} + +func (f *SwitchField) Initialize(p *Protocol) { + f.xmlName = f.Name + f.Name = SrcName(p, f.Name) + f.Expr.Initialize(p) + fieldref, ok := f.Expr.(*FieldRef) + if !ok { + panic("switch field's expression not a fieldref") + } + f.MaskName = SrcName(p, fieldref.Name) + for _, bitcase := range f.Bitcases { + bitcase.Expr.Initialize(p) + for _, field := range bitcase.Fields { + field.Initialize(p) + } + } +} + +// Bitcase represents a single bitcase inside a switch expression. +// It is not currently used. (i.e., it's XKB voodoo.) +type Bitcase struct { + Fields []Field + Expr Expression +} |