github.com/robotn/xgb@v0.0.0-20190912153532-2cb92d044934/xgbgen/go_union.go (about) 1 package main 2 3 // Union types 4 func (u *Union) Define(c *Context) { 5 c.Putln("// %s is a represention of the %s union type.", 6 u.SrcName(), u.SrcName()) 7 c.Putln("// Note that to *create* a Union, you should *never* create") 8 c.Putln("// this struct directly (unless you know what you're doing).") 9 c.Putln("// Instead use one of the following constructors for '%s':", 10 u.SrcName()) 11 for _, field := range u.Fields { 12 c.Putln("// %s%sNew(%s %s) %s", u.SrcName(), field.SrcName(), 13 field.SrcName(), field.SrcType(), u.SrcName()) 14 } 15 16 c.Putln("type %s struct {", u.SrcName()) 17 for _, field := range u.Fields { 18 field.Define(c) 19 } 20 c.Putln("}") 21 c.Putln("") 22 23 // Write functions for each field that create instances of this 24 // union using the corresponding field. 25 u.New(c) 26 27 // Write function that reads bytes and produces this union. 28 u.Read(c) 29 30 // Write function that reads bytes and produces a list of this union. 31 u.ReadList(c) 32 33 // Write function that writes bytes given this union. 34 u.Write(c) 35 36 // Write function that writes a list of this union. 37 u.WriteList(c) 38 } 39 40 func (u *Union) New(c *Context) { 41 for _, field := range u.Fields { 42 c.Putln("// %s%sNew constructs a new %s union type with the %s field.", 43 u.SrcName(), field.SrcName(), u.SrcName(), field.SrcName()) 44 c.Putln("func %s%sNew(%s %s) %s {", 45 u.SrcName(), field.SrcName(), field.SrcName(), 46 field.SrcType(), u.SrcName()) 47 c.Putln("var b int") 48 c.Putln("buf := make([]byte, %s)", u.Size()) 49 c.Putln("") 50 field.Write(c, "") 51 c.Putln("") 52 c.Putln("// Create the Union type") 53 c.Putln("v := %s{}", u.SrcName()) 54 c.Putln("") 55 c.Putln("// Now copy buf into all fields") 56 c.Putln("") 57 for _, field2 := range u.Fields { 58 c.Putln("b = 0 // always read the same bytes") 59 field2.Read(c, "v.") 60 c.Putln("") 61 } 62 c.Putln("return v") 63 c.Putln("}") 64 c.Putln("") 65 } 66 } 67 68 func (u *Union) Read(c *Context) { 69 c.Putln("// %sRead reads a byte slice into a %s value.", 70 u.SrcName(), u.SrcName()) 71 c.Putln("func %sRead(buf []byte, v *%s) int {", u.SrcName(), u.SrcName()) 72 c.Putln("var b int") 73 c.Putln("") 74 for _, field := range u.Fields { 75 c.Putln("b = 0 // re-read the same bytes") 76 field.Read(c, "v.") 77 c.Putln("") 78 } 79 c.Putln("return %s", u.Size()) 80 c.Putln("}") 81 c.Putln("") 82 } 83 84 func (u *Union) ReadList(c *Context) { 85 c.Putln("// %sReadList reads a byte slice into a list of %s values.", 86 u.SrcName(), u.SrcName()) 87 c.Putln("func %sReadList(buf []byte, dest []%s) int {", 88 u.SrcName(), u.SrcName()) 89 c.Putln("b := 0") 90 c.Putln("for i := 0; i < len(dest); i++ {") 91 c.Putln("dest[i] = %s{}", u.SrcName()) 92 c.Putln("b += %sRead(buf[b:], &dest[i])", u.SrcName()) 93 c.Putln("}") 94 c.Putln("return xgb.Pad(b)") 95 c.Putln("}") 96 c.Putln("") 97 } 98 99 // This is a bit tricky since writing from a Union implies that only 100 // the data inside ONE of the elements is actually written. 101 // However, we only currently support unions where every field has the 102 // *same* *fixed* size. Thus, we make sure to always read bytes into 103 // every field which allows us to simply pick the first field and write it. 104 func (u *Union) Write(c *Context) { 105 c.Putln("// Bytes writes a %s value to a byte slice.", u.SrcName()) 106 c.Putln("// Each field in a union must contain the same data.") 107 c.Putln("// So simply pick the first field and write that to the wire.") 108 c.Putln("func (v %s) Bytes() []byte {", u.SrcName()) 109 c.Putln("buf := make([]byte, %s)", u.Size().Reduce("v.")) 110 c.Putln("b := 0") 111 c.Putln("") 112 u.Fields[0].Write(c, "v.") 113 c.Putln("return buf") 114 c.Putln("}") 115 c.Putln("") 116 } 117 118 func (u *Union) WriteList(c *Context) { 119 c.Putln("// %sListBytes writes a list of %s values to a byte slice.", 120 u.SrcName(), u.SrcName()) 121 c.Putln("func %sListBytes(buf []byte, list []%s) int {", 122 u.SrcName(), u.SrcName()) 123 c.Putln("b := 0") 124 c.Putln("var unionBytes []byte") 125 c.Putln("for _, item := range list {") 126 c.Putln("unionBytes = item.Bytes()") 127 c.Putln("copy(buf[b:], unionBytes)") 128 c.Putln("b += xgb.Pad(len(unionBytes))") 129 c.Putln("}") 130 c.Putln("return b") 131 c.Putln("}") 132 c.Putln("") 133 } 134 135 func (u *Union) WriteListSize(c *Context) { 136 c.Putln("// Union list size %s", u.SrcName()) 137 c.Putln("// %sListSize computes the size (bytes) of a list of %s values.", 138 u.SrcName()) 139 c.Putln("func %sListSize(list []%s) int {", u.SrcName(), u.SrcName()) 140 c.Putln("size := 0") 141 c.Putln("for _, item := range list {") 142 c.Putln("size += %s", u.Size().Reduce("item.")) 143 c.Putln("}") 144 c.Putln("return size") 145 c.Putln("}") 146 c.Putln("") 147 }