fast-path.go.tmpl 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. // +build !notfastpath
  2. // Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
  3. // Use of this source code is governed by a MIT license found in the LICENSE file.
  4. // ************************************************************
  5. // DO NOT EDIT.
  6. // THIS FILE IS AUTO-GENERATED from fast-path.go.tmpl
  7. // ************************************************************
  8. package codec
  9. // Fast path functions try to create a fast path encode or decode implementation
  10. // for common maps and slices.
  11. //
  12. // We define the functions and register then in this single file
  13. // so as not to pollute the encode.go and decode.go, and create a dependency in there.
  14. // This file can be omitted without causing a build failure.
  15. //
  16. // The advantage of fast paths is:
  17. // - Many calls bypass reflection altogether
  18. //
  19. // Currently support
  20. // - slice of all builtin types,
  21. // - map of all builtin types to string or interface value
  22. // - symetrical maps of all builtin types (e.g. str-str, uint8-uint8)
  23. // This should provide adequate "typical" implementations.
  24. //
  25. // Note that fast track decode functions must handle values for which an address cannot be obtained.
  26. // For example:
  27. // m2 := map[string]int{}
  28. // p2 := []interface{}{m2}
  29. // // decoding into p2 will bomb if fast track functions do not treat like unaddressable.
  30. //
  31. import (
  32. "reflect"
  33. "sort"
  34. )
  35. const fastpathCheckNilFalse = false // for reflect
  36. const fastpathCheckNilTrue = true // for type switch
  37. type fastpathT struct {}
  38. var fastpathTV fastpathT
  39. type fastpathE struct {
  40. rtid uintptr
  41. rt reflect.Type
  42. encfn func(*encFnInfo, reflect.Value)
  43. decfn func(*decFnInfo, reflect.Value)
  44. }
  45. type fastpathA [{{ .FastpathLen }}]fastpathE
  46. func (x *fastpathA) index(rtid uintptr) int {
  47. // use binary search to grab the index (adapted from sort/search.go)
  48. h, i, j := 0, 0, {{ .FastpathLen }} // len(x)
  49. for i < j {
  50. h = i + (j-i)/2
  51. if x[h].rtid < rtid {
  52. i = h + 1
  53. } else {
  54. j = h
  55. }
  56. }
  57. if i < {{ .FastpathLen }} && x[i].rtid == rtid {
  58. return i
  59. }
  60. return -1
  61. }
  62. type fastpathAslice []fastpathE
  63. func (x fastpathAslice) Len() int { return len(x) }
  64. func (x fastpathAslice) Less(i, j int) bool { return x[i].rtid < x[j].rtid }
  65. func (x fastpathAslice) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
  66. var fastpathAV fastpathA
  67. // due to possible initialization loop error, make fastpath in an init()
  68. func init() {
  69. if !fastpathEnabled {
  70. return
  71. }
  72. i := 0
  73. fn := func(v interface{}, fe func(*encFnInfo, reflect.Value), fd func(*decFnInfo, reflect.Value)) (f fastpathE) {
  74. xrt := reflect.TypeOf(v)
  75. xptr := reflect.ValueOf(xrt).Pointer()
  76. fastpathAV[i] = fastpathE{xptr, xrt, fe, fd}
  77. i++
  78. return
  79. }
  80. {{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
  81. fn([]{{ .Elem }}(nil), (*encFnInfo).{{ .MethodNamePfx "fastpathEnc" false }}R, (*decFnInfo).{{ .MethodNamePfx "fastpathDec" false }}R){{end}}{{end}}{{end}}
  82. {{range .Values}}{{if not .Primitive}}{{if .MapKey }}
  83. fn(map[{{ .MapKey }}]{{ .Elem }}(nil), (*encFnInfo).{{ .MethodNamePfx "fastpathEnc" false }}R, (*decFnInfo).{{ .MethodNamePfx "fastpathDec" false }}R){{end}}{{end}}{{end}}
  84. sort.Sort(fastpathAslice(fastpathAV[:]))
  85. }
  86. // -- encode
  87. // -- -- fast path type switch
  88. func fastpathEncodeTypeSwitch(iv interface{}, e *Encoder) bool {
  89. if !fastpathEnabled {
  90. return false
  91. }
  92. switch v := iv.(type) {
  93. {{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
  94. case []{{ .Elem }}:{{else}}
  95. case map[{{ .MapKey }}]{{ .Elem }}:{{end}}
  96. fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, fastpathCheckNilTrue, e){{if not .MapKey }}
  97. case *[]{{ .Elem }}:{{else}}
  98. case *map[{{ .MapKey }}]{{ .Elem }}:{{end}}
  99. fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, fastpathCheckNilTrue, e)
  100. {{end}}{{end}}
  101. default:
  102. _ = v // TODO: workaround https://github.com/golang/go/issues/12927 (remove after go 1.6 release)
  103. return false
  104. }
  105. return true
  106. }
  107. func fastpathEncodeTypeSwitchSlice(iv interface{}, e *Encoder) bool {
  108. if !fastpathEnabled {
  109. return false
  110. }
  111. switch v := iv.(type) {
  112. {{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
  113. case []{{ .Elem }}:
  114. fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, fastpathCheckNilTrue, e)
  115. case *[]{{ .Elem }}:
  116. fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, fastpathCheckNilTrue, e)
  117. {{end}}{{end}}{{end}}
  118. default:
  119. _ = v // TODO: workaround https://github.com/golang/go/issues/12927 (remove after go 1.6 release)
  120. return false
  121. }
  122. return true
  123. }
  124. func fastpathEncodeTypeSwitchMap(iv interface{}, e *Encoder) bool {
  125. if !fastpathEnabled {
  126. return false
  127. }
  128. switch v := iv.(type) {
  129. {{range .Values}}{{if not .Primitive}}{{if .MapKey }}
  130. case map[{{ .MapKey }}]{{ .Elem }}:
  131. fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, fastpathCheckNilTrue, e)
  132. case *map[{{ .MapKey }}]{{ .Elem }}:
  133. fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, fastpathCheckNilTrue, e)
  134. {{end}}{{end}}{{end}}
  135. default:
  136. _ = v // TODO: workaround https://github.com/golang/go/issues/12927 (remove after go 1.6 release)
  137. return false
  138. }
  139. return true
  140. }
  141. // -- -- fast path functions
  142. {{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
  143. func (f *encFnInfo) {{ .MethodNamePfx "fastpathEnc" false }}R(rv reflect.Value) {
  144. if f.ti.mbs {
  145. fastpathTV.{{ .MethodNamePfx "EncAsMap" false }}V(rv.Interface().([]{{ .Elem }}), fastpathCheckNilFalse, f.e)
  146. } else {
  147. fastpathTV.{{ .MethodNamePfx "Enc" false }}V(rv.Interface().([]{{ .Elem }}), fastpathCheckNilFalse, f.e)
  148. }
  149. }
  150. func (_ fastpathT) {{ .MethodNamePfx "Enc" false }}V(v []{{ .Elem }}, checkNil bool, e *Encoder) {
  151. ee := e.e
  152. cr := e.cr
  153. if checkNil && v == nil {
  154. ee.EncodeNil()
  155. return
  156. }
  157. ee.EncodeArrayStart(len(v))
  158. for _, v2 := range v {
  159. if cr != nil { cr.sendContainerState(containerArrayElem) }
  160. {{ encmd .Elem "v2"}}
  161. }
  162. if cr != nil { cr.sendContainerState(containerArrayEnd) }{{/* ee.EncodeEnd() */}}
  163. }
  164. func (_ fastpathT) {{ .MethodNamePfx "EncAsMap" false }}V(v []{{ .Elem }}, checkNil bool, e *Encoder) {
  165. ee := e.e
  166. cr := e.cr
  167. if checkNil && v == nil {
  168. ee.EncodeNil()
  169. return
  170. }
  171. if len(v)%2 == 1 {
  172. e.errorf("mapBySlice requires even slice length, but got %v", len(v))
  173. return
  174. }
  175. ee.EncodeMapStart(len(v) / 2)
  176. for j, v2 := range v {
  177. if cr != nil {
  178. if j%2 == 0 {
  179. cr.sendContainerState(containerMapKey)
  180. } else {
  181. cr.sendContainerState(containerMapValue)
  182. }
  183. }
  184. {{ encmd .Elem "v2"}}
  185. }
  186. if cr != nil { cr.sendContainerState(containerMapEnd) }
  187. }
  188. {{end}}{{end}}{{end}}
  189. {{range .Values}}{{if not .Primitive}}{{if .MapKey }}
  190. func (f *encFnInfo) {{ .MethodNamePfx "fastpathEnc" false }}R(rv reflect.Value) {
  191. fastpathTV.{{ .MethodNamePfx "Enc" false }}V(rv.Interface().(map[{{ .MapKey }}]{{ .Elem }}), fastpathCheckNilFalse, f.e)
  192. }
  193. func (_ fastpathT) {{ .MethodNamePfx "Enc" false }}V(v map[{{ .MapKey }}]{{ .Elem }}, checkNil bool, e *Encoder) {
  194. ee := e.e
  195. cr := e.cr
  196. if checkNil && v == nil {
  197. ee.EncodeNil()
  198. return
  199. }
  200. ee.EncodeMapStart(len(v))
  201. {{if eq .MapKey "string"}}asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
  202. {{end}}if e.h.Canonical {
  203. {{if eq .MapKey "interface{}"}}{{/* out of band
  204. */}}var mksv []byte = make([]byte, 0, len(v)*16) // temporary byte slice for the encoding
  205. e2 := NewEncoderBytes(&mksv, e.hh)
  206. v2 := make([]bytesI, len(v))
  207. var i, l int
  208. var vp *bytesI {{/* put loop variables outside. seems currently needed for better perf */}}
  209. for k2, _ := range v {
  210. l = len(mksv)
  211. e2.MustEncode(k2)
  212. vp = &v2[i]
  213. vp.v = mksv[l:]
  214. vp.i = k2
  215. i++
  216. }
  217. sort.Sort(bytesISlice(v2))
  218. for j := range v2 {
  219. if cr != nil { cr.sendContainerState(containerMapKey) }
  220. e.asis(v2[j].v)
  221. if cr != nil { cr.sendContainerState(containerMapValue) }
  222. e.encode(v[v2[j].i])
  223. } {{else}}{{ $x := sorttype .MapKey true}}v2 := make([]{{ $x }}, len(v))
  224. var i int
  225. for k, _ := range v {
  226. v2[i] = {{ $x }}(k)
  227. i++
  228. }
  229. sort.Sort({{ sorttype .MapKey false}}(v2))
  230. for _, k2 := range v2 {
  231. if cr != nil { cr.sendContainerState(containerMapKey) }
  232. {{if eq .MapKey "string"}}if asSymbols {
  233. ee.EncodeSymbol(k2)
  234. } else {
  235. ee.EncodeString(c_UTF8, k2)
  236. }{{else}}{{ $y := printf "%s(k2)" .MapKey }}{{ encmd .MapKey $y }}{{end}}
  237. if cr != nil { cr.sendContainerState(containerMapValue) }
  238. {{ $y := printf "v[%s(k2)]" .MapKey }}{{ encmd .Elem $y }}
  239. } {{end}}
  240. } else {
  241. for k2, v2 := range v {
  242. if cr != nil { cr.sendContainerState(containerMapKey) }
  243. {{if eq .MapKey "string"}}if asSymbols {
  244. ee.EncodeSymbol(k2)
  245. } else {
  246. ee.EncodeString(c_UTF8, k2)
  247. }{{else}}{{ encmd .MapKey "k2"}}{{end}}
  248. if cr != nil { cr.sendContainerState(containerMapValue) }
  249. {{ encmd .Elem "v2"}}
  250. }
  251. }
  252. if cr != nil { cr.sendContainerState(containerMapEnd) }{{/* ee.EncodeEnd() */}}
  253. }
  254. {{end}}{{end}}{{end}}
  255. // -- decode
  256. // -- -- fast path type switch
  257. func fastpathDecodeTypeSwitch(iv interface{}, d *Decoder) bool {
  258. if !fastpathEnabled {
  259. return false
  260. }
  261. switch v := iv.(type) {
  262. {{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
  263. case []{{ .Elem }}:{{else}}
  264. case map[{{ .MapKey }}]{{ .Elem }}:{{end}}
  265. fastpathTV.{{ .MethodNamePfx "Dec" false }}V(v, fastpathCheckNilFalse, false, d){{if not .MapKey }}
  266. case *[]{{ .Elem }}:{{else}}
  267. case *map[{{ .MapKey }}]{{ .Elem }}:{{end}}
  268. v2, changed2 := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*v, fastpathCheckNilFalse, true, d)
  269. if changed2 {
  270. *v = v2
  271. }
  272. {{end}}{{end}}
  273. default:
  274. _ = v // TODO: workaround https://github.com/golang/go/issues/12927 (remove after go 1.6 release)
  275. return false
  276. }
  277. return true
  278. }
  279. // -- -- fast path functions
  280. {{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
  281. {{/*
  282. Slices can change if they
  283. - did not come from an array
  284. - are addressable (from a ptr)
  285. - are settable (e.g. contained in an interface{})
  286. */}}
  287. func (f *decFnInfo) {{ .MethodNamePfx "fastpathDec" false }}R(rv reflect.Value) {
  288. array := f.seq == seqTypeArray
  289. if !array && rv.CanAddr() { {{/* // CanSet => CanAddr + Exported */}}
  290. vp := rv.Addr().Interface().(*[]{{ .Elem }})
  291. v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*vp, fastpathCheckNilFalse, !array, f.d)
  292. if changed {
  293. *vp = v
  294. }
  295. } else {
  296. v := rv.Interface().([]{{ .Elem }})
  297. fastpathTV.{{ .MethodNamePfx "Dec" false }}V(v, fastpathCheckNilFalse, false, f.d)
  298. }
  299. }
  300. func (f fastpathT) {{ .MethodNamePfx "Dec" false }}X(vp *[]{{ .Elem }}, checkNil bool, d *Decoder) {
  301. v, changed := f.{{ .MethodNamePfx "Dec" false }}V(*vp, checkNil, true, d)
  302. if changed {
  303. *vp = v
  304. }
  305. }
  306. func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, checkNil bool, canChange bool, d *Decoder) (_ []{{ .Elem }}, changed bool) {
  307. dd := d.d
  308. {{/* // if dd.isContainerType(valueTypeNil) { dd.TryDecodeAsNil() */}}
  309. if checkNil && dd.TryDecodeAsNil() {
  310. if v != nil {
  311. changed = true
  312. }
  313. return nil, changed
  314. }
  315. slh, containerLenS := d.decSliceHelperStart()
  316. if containerLenS == 0 {
  317. if canChange {
  318. if v == nil {
  319. v = []{{ .Elem }}{}
  320. } else if len(v) != 0 {
  321. v = v[:0]
  322. }
  323. changed = true
  324. }
  325. slh.End()
  326. return v, changed
  327. }
  328. if containerLenS > 0 {
  329. x2read := containerLenS
  330. var xtrunc bool
  331. if containerLenS > cap(v) {
  332. if canChange { {{/*
  333. // fast-path is for "basic" immutable types, so no need to copy them over
  334. // s := make([]{{ .Elem }}, decInferLen(containerLenS, d.h.MaxInitLen))
  335. // copy(s, v[:cap(v)])
  336. // v = s */}}
  337. var xlen int
  338. xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, {{ .Size }})
  339. if xtrunc {
  340. if xlen <= cap(v) {
  341. v = v[:xlen]
  342. } else {
  343. v = make([]{{ .Elem }}, xlen)
  344. }
  345. } else {
  346. v = make([]{{ .Elem }}, xlen)
  347. }
  348. changed = true
  349. } else {
  350. d.arrayCannotExpand(len(v), containerLenS)
  351. }
  352. x2read = len(v)
  353. } else if containerLenS != len(v) {
  354. if canChange {
  355. v = v[:containerLenS]
  356. changed = true
  357. }
  358. } {{/* // all checks done. cannot go past len. */}}
  359. j := 0
  360. for ; j < x2read; j++ {
  361. slh.ElemContainerState(j)
  362. {{ if eq .Elem "interface{}" }}d.decode(&v[j]){{ else }}v[j] = {{ decmd .Elem }}{{ end }}
  363. }
  364. if xtrunc { {{/* // means canChange=true, changed=true already. */}}
  365. for ; j < containerLenS; j++ {
  366. v = append(v, {{ zerocmd .Elem }})
  367. slh.ElemContainerState(j)
  368. {{ if eq .Elem "interface{}" }}d.decode(&v[j]){{ else }}v[j] = {{ decmd .Elem }}{{ end }}
  369. }
  370. } else if !canChange {
  371. for ; j < containerLenS; j++ {
  372. slh.ElemContainerState(j)
  373. d.swallow()
  374. }
  375. }
  376. } else {
  377. breakFound := dd.CheckBreak() {{/* check break first, so we can initialize v with a capacity of 4 if necessary */}}
  378. if breakFound {
  379. if canChange {
  380. if v == nil {
  381. v = []{{ .Elem }}{}
  382. } else if len(v) != 0 {
  383. v = v[:0]
  384. }
  385. changed = true
  386. }
  387. slh.End()
  388. return v, changed
  389. }
  390. if cap(v) == 0 {
  391. v = make([]{{ .Elem }}, 1, 4)
  392. changed = true
  393. }
  394. j := 0
  395. for ; !breakFound; j++ {
  396. if j >= len(v) {
  397. if canChange {
  398. v = append(v, {{ zerocmd .Elem }})
  399. changed = true
  400. } else {
  401. d.arrayCannotExpand(len(v), j+1)
  402. }
  403. }
  404. slh.ElemContainerState(j)
  405. if j < len(v) { {{/* // all checks done. cannot go past len. */}}
  406. {{ if eq .Elem "interface{}" }}d.decode(&v[j])
  407. {{ else }}v[j] = {{ decmd .Elem }}{{ end }}
  408. } else {
  409. d.swallow()
  410. }
  411. breakFound = dd.CheckBreak()
  412. }
  413. if canChange && j < len(v) {
  414. v = v[:j]
  415. changed = true
  416. }
  417. }
  418. slh.End()
  419. return v, changed
  420. }
  421. {{end}}{{end}}{{end}}
  422. {{range .Values}}{{if not .Primitive}}{{if .MapKey }}
  423. {{/*
  424. Maps can change if they are
  425. - addressable (from a ptr)
  426. - settable (e.g. contained in an interface{})
  427. */}}
  428. func (f *decFnInfo) {{ .MethodNamePfx "fastpathDec" false }}R(rv reflect.Value) {
  429. if rv.CanAddr() {
  430. vp := rv.Addr().Interface().(*map[{{ .MapKey }}]{{ .Elem }})
  431. v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*vp, fastpathCheckNilFalse, true, f.d)
  432. if changed {
  433. *vp = v
  434. }
  435. } else {
  436. v := rv.Interface().(map[{{ .MapKey }}]{{ .Elem }})
  437. fastpathTV.{{ .MethodNamePfx "Dec" false }}V(v, fastpathCheckNilFalse, false, f.d)
  438. }
  439. }
  440. func (f fastpathT) {{ .MethodNamePfx "Dec" false }}X(vp *map[{{ .MapKey }}]{{ .Elem }}, checkNil bool, d *Decoder) {
  441. v, changed := f.{{ .MethodNamePfx "Dec" false }}V(*vp, checkNil, true, d)
  442. if changed {
  443. *vp = v
  444. }
  445. }
  446. func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v map[{{ .MapKey }}]{{ .Elem }}, checkNil bool, canChange bool,
  447. d *Decoder) (_ map[{{ .MapKey }}]{{ .Elem }}, changed bool) {
  448. dd := d.d
  449. cr := d.cr
  450. {{/* // if dd.isContainerType(valueTypeNil) {dd.TryDecodeAsNil() */}}
  451. if checkNil && dd.TryDecodeAsNil() {
  452. if v != nil {
  453. changed = true
  454. }
  455. return nil, changed
  456. }
  457. containerLen := dd.ReadMapStart()
  458. if canChange && v == nil {
  459. xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, {{ .Size }})
  460. v = make(map[{{ .MapKey }}]{{ .Elem }}, xlen)
  461. changed = true
  462. }
  463. {{ if eq .Elem "interface{}" }}mapGet := !d.h.MapValueReset && !d.h.InterfaceReset{{end}}
  464. var mk {{ .MapKey }}
  465. var mv {{ .Elem }}
  466. if containerLen > 0 {
  467. for j := 0; j < containerLen; j++ {
  468. if cr != nil { cr.sendContainerState(containerMapKey) }
  469. {{ if eq .MapKey "interface{}" }}mk = nil
  470. d.decode(&mk)
  471. if bv, bok := mk.([]byte); bok {
  472. mk = d.string(bv) {{/* // maps cannot have []byte as key. switch to string. */}}
  473. }{{ else }}mk = {{ decmd .MapKey }}{{ end }}
  474. if cr != nil { cr.sendContainerState(containerMapValue) }
  475. {{ if eq .Elem "interface{}" }}if mapGet { mv = v[mk] } else { mv = nil }
  476. d.decode(&mv){{ else }}mv = {{ decmd .Elem }}{{ end }}
  477. if v != nil {
  478. v[mk] = mv
  479. }
  480. }
  481. } else if containerLen < 0 {
  482. for j := 0; !dd.CheckBreak(); j++ {
  483. if cr != nil { cr.sendContainerState(containerMapKey) }
  484. {{ if eq .MapKey "interface{}" }}mk = nil
  485. d.decode(&mk)
  486. if bv, bok := mk.([]byte); bok {
  487. mk = d.string(bv) {{/* // maps cannot have []byte as key. switch to string. */}}
  488. }{{ else }}mk = {{ decmd .MapKey }}{{ end }}
  489. if cr != nil { cr.sendContainerState(containerMapValue) }
  490. {{ if eq .Elem "interface{}" }}if mapGet { mv = v[mk] } else { mv = nil }
  491. d.decode(&mv){{ else }}mv = {{ decmd .Elem }}{{ end }}
  492. if v != nil {
  493. v[mk] = mv
  494. }
  495. }
  496. }
  497. if cr != nil { cr.sendContainerState(containerMapEnd) }
  498. return v, changed
  499. }
  500. {{end}}{{end}}{{end}}