github.com/chrislusf/greenpack@v3.7.1-0.20170911073826-ad5bd10b7c47+incompatible/gen/size.go (about) 1 package gen 2 3 import ( 4 "fmt" 5 "io" 6 "strconv" 7 8 "github.com/glycerine/greenpack/cfg" 9 "github.com/glycerine/greenpack/msgp" 10 ) 11 12 type sizeState uint8 13 14 const ( 15 // need to write "s = ..." 16 assign sizeState = iota 17 18 // need to write "s += ..." 19 add 20 21 // can just append "+ ..." 22 expr 23 ) 24 25 func sizes(w io.Writer, cfg *cfg.GreenConfig) *sizeGen { 26 return &sizeGen{ 27 p: printer{w: w}, 28 state: assign, 29 cfg: cfg, 30 } 31 } 32 33 type sizeGen struct { 34 passes 35 p printer 36 state sizeState 37 cfg *cfg.GreenConfig 38 } 39 40 func (s *sizeGen) MethodPrefix() string { 41 return s.cfg.MethodPrefix 42 } 43 44 func (s *sizeGen) Method() Method { return Size } 45 46 func (s *sizeGen) Apply(dirs []string) error { 47 return nil 48 } 49 50 func builtinSize(typ string) string { 51 return "msgp." + typ + "Size" 52 } 53 54 // this lets us chain together addition 55 // operations where possible 56 func (s *sizeGen) addConstant(sz string) { 57 if !s.p.ok() { 58 return 59 } 60 61 switch s.state { 62 case assign: 63 s.p.print("\ns = " + sz) 64 s.state = expr 65 return 66 case add: 67 s.p.print("\ns += " + sz) 68 s.state = expr 69 return 70 case expr: 71 s.p.print(" + " + sz) 72 return 73 } 74 75 panic("unknown size state") 76 } 77 78 func (s *sizeGen) Execute(p Elem) error { 79 if !s.p.ok() { 80 return s.p.err 81 } 82 p = s.applyall(p) 83 if p == nil { 84 return nil 85 } 86 if !IsPrintable(p) { 87 return nil 88 } 89 90 s.p.comment(fmt.Sprintf("%sMsgsize returns an upper bound estimate of the number of bytes occupied by the serialized message", s.cfg.MethodPrefix)) 91 92 s.p.printf("\nfunc (%s %s) %sMsgsize() (s int) {", p.Varname(), imutMethodReceiver(p), s.cfg.MethodPrefix) 93 s.state = assign 94 next(s, p) 95 s.p.nakedReturn() 96 return s.p.err 97 } 98 99 func (s *sizeGen) gStruct(st *Struct) { 100 if !s.p.ok() { 101 return 102 } 103 nfields := uint32(len(st.Fields) - st.SkipCount) 104 105 if s.cfg.AllTuple || st.AsTuple { 106 data := msgp.AppendArrayHeader(nil, nfields) 107 s.addConstant(strconv.Itoa(len(data))) 108 for i := range st.Fields { 109 if st.Fields[i].Skip { 110 continue 111 } 112 113 if !s.p.ok() { 114 return 115 } 116 next(s, st.Fields[i].FieldElem) 117 } 118 } else { 119 data := msgp.AppendMapHeader(nil, nfields) 120 s.addConstant(strconv.Itoa(len(data))) 121 for i := range st.Fields { 122 if st.Fields[i].Skip { 123 continue 124 } 125 data = data[:0] 126 data = msgp.AppendString(data, st.Fields[i].FieldTagZidClue) 127 128 s.addConstant(strconv.Itoa(len(data))) 129 next(s, st.Fields[i].FieldElem) 130 } 131 } 132 } 133 134 func (s *sizeGen) gPtr(p *Ptr) { 135 s.state = add // inner must use add 136 s.p.printf("\nif %s == nil {\ns += msgp.NilSize\n} else {", p.Varname()) 137 next(s, p.Value) 138 s.state = add // closing block; reset to add 139 s.p.closeblock() 140 } 141 142 func (s *sizeGen) gSlice(sl *Slice) { 143 if !s.p.ok() { 144 return 145 } 146 147 s.addConstant(builtinSize(arrayHeader)) 148 149 // if the slice's element is a fixed size 150 // (e.g. float64, [32]int, etc.), then 151 // print the length times the element size directly 152 if str, ok := fixedsizeExpr(sl.Els); ok { 153 s.addConstant(fmt.Sprintf("(%s * (%s))", lenExpr(sl), str)) 154 return 155 } 156 157 // add inside the range block, and immediately after 158 s.state = add 159 s.p.rangeBlock(sl.Index, sl.Varname(), s, sl.Els) 160 s.state = add 161 } 162 163 func (s *sizeGen) gArray(a *Array) { 164 if !s.p.ok() { 165 return 166 } 167 168 s.addConstant(builtinSize(arrayHeader)) 169 170 // if the array's children are a fixed 171 // size, we can compile an expression 172 // that always represents the array's wire size 173 if str, ok := fixedsizeExpr(a); ok { 174 s.addConstant(str) 175 return 176 } 177 178 s.state = add 179 s.p.rangeBlock(a.Index, a.Varname(), s, a.Els) 180 s.state = add 181 } 182 183 func (s *sizeGen) gMap(m *Map) { 184 s.addConstant(builtinSize(mapHeader)) 185 vn := m.Varname() 186 s.p.printf("\nif %s != nil {", vn) 187 s.p.printf("\nfor %s, %s := range %s {", m.Keyidx, m.Validx, vn) 188 s.p.printf("\n_ = %s", m.Validx) // we may not use the value 189 s.p.printf("\n_ = %s", m.Keyidx) // we may not use the value 190 switch m.KeyTyp { 191 case "String": 192 s.p.printf("\ns += msgp.StringPrefixSize + len(%s)", m.Keyidx) 193 default: 194 s.p.printf("\ns += msgp.%sSize", m.KeyTyp) 195 } 196 s.state = expr 197 next(s, m.Value) 198 s.p.closeblock() 199 s.p.closeblock() 200 s.state = add 201 } 202 203 func (s *sizeGen) gBase(b *BaseElem) { 204 if !s.p.ok() { 205 return 206 } 207 s.addConstant(basesizeExpr(b, s)) 208 } 209 210 // returns "len(slice)" 211 func lenExpr(sl *Slice) string { 212 return "len(" + sl.Varname() + ")" 213 } 214 215 // is a given primitive always the same (max) 216 // size on the wire? 217 func fixedSize(p Primitive) bool { 218 switch p { 219 case Intf, Ext, IDENT, Bytes, String: 220 return false 221 default: 222 return true 223 } 224 } 225 226 // strip reference from string 227 func stripRef(s string) string { 228 if s[0] == '&' { 229 return s[1:] 230 } 231 return s 232 } 233 234 // return a fixed-size expression, if possible. 235 // only possible for *BaseElem and *Array. 236 // returns (expr, ok) 237 func fixedsizeExpr(e Elem) (string, bool) { 238 switch e := e.(type) { 239 case *Array: 240 if str, ok := fixedsizeExpr(e.Els); ok { 241 return fmt.Sprintf("(%s * (%s))", e.SizeResolved, str), true 242 } 243 case *BaseElem: 244 if fixedSize(e.Value) { 245 return builtinSize(e.BaseName()), true 246 } 247 case *Struct: 248 var str string 249 for _, f := range e.Fields { 250 if f.Skip { 251 continue 252 } 253 254 if fs, ok := fixedsizeExpr(f.FieldElem); ok { 255 if str == "" { 256 str = fs 257 } else { 258 str += "+" + fs 259 } 260 } else { 261 return "", false 262 } 263 } 264 var hdrlen int 265 mhdr := msgp.AppendMapHeader(nil, uint32(len(e.Fields)-e.SkipCount)) 266 hdrlen += len(mhdr) 267 var strbody []byte 268 for _, f := range e.Fields { 269 if !f.Skip { 270 271 strbody = msgp.AppendString(strbody[:0], f.FieldTagZidClue) 272 hdrlen += len(strbody) 273 } 274 } 275 return fmt.Sprintf("%d + %s", hdrlen, str), true 276 } 277 return "", false 278 } 279 280 // print size expression of a variable name 281 func basesizeExpr(b *BaseElem, s *sizeGen) string { 282 vname := b.Varname() 283 if b.Convert { 284 vname = tobaseConvert(b) 285 } 286 switch b.Value { 287 case Ext: 288 return "msgp.ExtensionPrefixSize + " + stripRef(vname) + ".Len()" 289 case Intf: 290 return "msgp.GuessSize(" + vname + ")" 291 case IDENT: 292 return vname + fmt.Sprintf(".%sMsgsize()", s.cfg.MethodPrefix) 293 case Bytes: 294 return "msgp.BytesPrefixSize + len(" + vname + ")" 295 case String: 296 return "msgp.StringPrefixSize + len(" + vname + ")" 297 default: 298 return builtinSize(b.BaseName()) 299 } 300 }