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  }