You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

286 lines
5.8 KiB

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