github.com/ugorji/go/codec@v1.2.13-0.20240307214044-07c54c229a5a/fast-path.go.tmpl (about) 1 // +build !notfastpath 2 // +build !codec.notfastpath 3 4 // Copyright (c) 2012-2020 Ugorji Nwoke. All rights reserved. 5 // Use of this source code is governed by a MIT license found in the LICENSE file. 6 7 // Code generated from fast-path.go.tmpl - DO NOT EDIT. 8 9 package codec 10 11 // Fast path functions try to create a fast path encode or decode implementation 12 // for common maps and slices. 13 // 14 // We define the functions and register them in this single file 15 // so as not to pollute the encode.go and decode.go, and create a dependency in there. 16 // This file can be omitted without causing a build failure. 17 // 18 // The advantage of fast paths is: 19 // - Many calls bypass reflection altogether 20 // 21 // Currently support 22 // - slice of all builtin types (numeric, bool, string, []byte) 23 // - maps of builtin types to builtin or interface{} type, EXCEPT FOR 24 // keys of type uintptr, int8/16/32, uint16/32, float32/64, bool, interface{} 25 // AND values of type type int8/16/32, uint16/32 26 // This should provide adequate "typical" implementations. 27 // 28 // Note that fast track decode functions must handle values for which an address cannot be obtained. 29 // For example: 30 // m2 := map[string]int{} 31 // p2 := []interface{}{m2} 32 // // decoding into p2 will bomb if fast track functions do not treat like unaddressable. 33 // 34 35 {{/* 36 fastpathEncMapStringUint64R (called by fastpath...switch) 37 EncMapStringUint64V (called by codecgen) 38 39 fastpathEncSliceBoolR: (called by fastpath...switch) (checks f.ti.mbs and calls one of them below) 40 EncSliceBoolV (also called by codecgen) 41 EncAsMapSliceBoolV (delegate when mapbyslice=true) 42 43 fastpathDecSliceIntfR (called by fastpath...switch) (calls Y or N below depending on if it can be updated) 44 DecSliceIntfX (called by codecgen) (calls Y below) 45 DecSliceIntfY (delegate when slice CAN be updated) 46 DecSliceIntfN (delegate when slice CANNOT be updated e.g. from array or non-addressable slice) 47 48 fastpathDecMap...R (called by fastpath...switch) (calls L or X? below) 49 DecMap...X (called by codecgen) 50 DecMap...L (delegated to by both above) 51 */ -}} 52 53 import ( 54 "reflect" 55 "sort" 56 ) 57 58 const fastpathEnabled = true 59 60 {{/* 61 const fastpathMapBySliceErrMsg = "mapBySlice requires even slice length, but got %v" 62 */ -}} 63 64 type fastpathT struct {} 65 66 var fastpathTV fastpathT 67 68 type fastpathE struct { 69 {{/* rtid uintptr */ -}} 70 rt reflect.Type 71 encfn func(*Encoder, *codecFnInfo, reflect.Value) 72 decfn func(*Decoder, *codecFnInfo, reflect.Value) 73 } 74 75 type fastpathA [{{ .FastpathLen }}]fastpathE 76 type fastpathARtid [{{ .FastpathLen }}]uintptr 77 78 var fastpathAv fastpathA 79 var fastpathAvRtid fastpathARtid 80 81 type fastpathAslice struct{} 82 83 func (fastpathAslice) Len() int { return {{ .FastpathLen }} } 84 func (fastpathAslice) Less(i, j int) bool { 85 return fastpathAvRtid[uint(i)] < fastpathAvRtid[uint(j)] 86 } 87 func (fastpathAslice) Swap(i, j int) { 88 fastpathAvRtid[uint(i)], fastpathAvRtid[uint(j)] = fastpathAvRtid[uint(j)], fastpathAvRtid[uint(i)] 89 fastpathAv[uint(i)], fastpathAv[uint(j)] = fastpathAv[uint(j)], fastpathAv[uint(i)] 90 } 91 92 func fastpathAvIndex(rtid uintptr) int { 93 // use binary search to grab the index (adapted from sort/search.go) 94 // Note: we use goto (instead of for loop) so this can be inlined. 95 // h, i, j := 0, 0, {{ .FastpathLen }} 96 var h, i uint 97 var j uint = {{ .FastpathLen }} 98 LOOP: 99 if i < j { 100 h = (i + j) >> 1 // avoid overflow when computing h // h = i + (j-i)/2 101 if fastpathAvRtid[h] < rtid { 102 i = h + 1 103 } else { 104 j = h 105 } 106 goto LOOP 107 } 108 if i < {{ .FastpathLen }} && fastpathAvRtid[i] == rtid { 109 return int(i) 110 } 111 return -1 112 } 113 114 115 // due to possible initialization loop error, make fastpath in an init() 116 func init() { 117 var i uint = 0 118 fn := func(v interface{}, 119 fe func(*Encoder, *codecFnInfo, reflect.Value), 120 fd func(*Decoder, *codecFnInfo, reflect.Value)) { 121 xrt := reflect.TypeOf(v) 122 xptr := rt2id(xrt) 123 fastpathAvRtid[i] = xptr 124 fastpathAv[i] = fastpathE{xrt, fe, fd} 125 i++ 126 } 127 {{/* do not register []byte in fast-path */}} 128 {{range .Values}}{{if not .Primitive}}{{if not .MapKey -}} 129 fn([]{{ .Elem }}(nil), (*Encoder).{{ .MethodNamePfx "fastpathEnc" false }}R, (*Decoder).{{ .MethodNamePfx "fastpathDec" false }}R) 130 {{end}}{{end}}{{end}} 131 132 {{range .Values}}{{if not .Primitive}}{{if .MapKey -}} 133 fn(map[{{ .MapKey }}]{{ .Elem }}(nil), (*Encoder).{{ .MethodNamePfx "fastpathEnc" false }}R, (*Decoder).{{ .MethodNamePfx "fastpathDec" false }}R) 134 {{end}}{{end}}{{end}} 135 136 sort.Sort(fastpathAslice{}) 137 } 138 139 // -- encode 140 141 // -- -- fast path type switch 142 func fastpathEncodeTypeSwitch(iv interface{}, e *Encoder) bool { 143 switch v := iv.(type) { 144 {{range .Values}}{{if not .Primitive}}{{if not .MapKey -}} 145 case []{{ .Elem }}: 146 fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, e) 147 case *[]{{ .Elem }}: 148 if *v == nil { 149 e.e.EncodeNil() 150 } else { 151 fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, e) 152 } 153 {{end}}{{end}}{{end -}} 154 155 {{range .Values}}{{if not .Primitive}}{{if .MapKey -}} 156 case map[{{ .MapKey }}]{{ .Elem }}: 157 fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, e) 158 case *map[{{ .MapKey }}]{{ .Elem }}: 159 if *v == nil { 160 e.e.EncodeNil() 161 } else { 162 fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, e) 163 } 164 {{end}}{{end}}{{end -}} 165 166 default: 167 _ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4 168 return false 169 } 170 return true 171 } 172 173 // -- -- fast path functions 174 {{range .Values}}{{if not .Primitive}}{{if not .MapKey -}} 175 func (e *Encoder) {{ .MethodNamePfx "fastpathEnc" false }}R(f *codecFnInfo, rv reflect.Value) { 176 var v []{{ .Elem }} 177 if rv.Kind() == reflect.Array { 178 rvGetSlice4Array(rv, &v) 179 } else { 180 v = rv2i(rv).([]{{ .Elem }}) 181 } 182 if f.ti.mbs { 183 fastpathTV.{{ .MethodNamePfx "EncAsMap" false }}V(v, e) 184 } else { 185 fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, e) 186 } 187 } 188 func (fastpathT) {{ .MethodNamePfx "Enc" false }}V(v []{{ .Elem }}, e *Encoder) { 189 {{/* if v == nil { e.e.EncodeNil(); return } */ -}} 190 {{ if eq .Elem "uint8" "byte" -}} 191 e.e.EncodeStringBytesRaw(v) 192 {{ else -}} 193 e.arrayStart(len(v)) 194 for j := range v { 195 e.arrayElem() 196 {{ encmd .Elem "v[j]"}} 197 } 198 e.arrayEnd() 199 {{ end -}} 200 } 201 func (fastpathT) {{ .MethodNamePfx "EncAsMap" false }}V(v []{{ .Elem }}, e *Encoder) { 202 {{/* if v == nil { e.e.EncodeNil() } else */ -}} 203 e.haltOnMbsOddLen(len(v)) 204 {{/* 205 if len(v)&1 != 0 { // similar to &1==1 or %2 == 1 206 e.errorf(fastpathMapBySliceErrMsg, len(v)) 207 } 208 */ -}} 209 e.mapStart(len(v) >> 1) // e.mapStart(len(v) / 2) 210 for j := range v { 211 if j&1 == 0 { // if j%2 == 0 { 212 e.mapElemKey() 213 } else { 214 e.mapElemValue() 215 } 216 {{ encmd .Elem "v[j]"}} 217 } 218 e.mapEnd() 219 } 220 {{end}}{{end}}{{end -}} 221 222 {{range .Values}}{{if not .Primitive}}{{if .MapKey -}} 223 func (e *Encoder) {{ .MethodNamePfx "fastpathEnc" false }}R(f *codecFnInfo, rv reflect.Value) { 224 fastpathTV.{{ .MethodNamePfx "Enc" false }}V(rv2i(rv).(map[{{ .MapKey }}]{{ .Elem }}), e) 225 } 226 func (fastpathT) {{ .MethodNamePfx "Enc" false }}V(v map[{{ .MapKey }}]{{ .Elem }}, e *Encoder) { 227 {{/* if v == nil { e.e.EncodeNil(); return } */ -}} 228 e.mapStart(len(v)) 229 if e.h.Canonical { {{/* need to figure out .NoCanonical */}} 230 {{if eq .MapKey "interface{}"}}{{/* out of band */ -}} 231 var mksv []byte = make([]byte, 0, len(v)*16) // temporary byte slice for the encoding 232 e2 := NewEncoderBytes(&mksv, e.hh) 233 v2 := make([]bytesIntf, len(v)) 234 var i, l uint {{/* put loop variables outside. seems currently needed for better perf */}} 235 var vp *bytesIntf 236 for k2 := range v { 237 l = uint(len(mksv)) 238 e2.MustEncode(k2) 239 vp = &v2[i] 240 vp.v = mksv[l:] 241 vp.i = k2 242 i++ 243 } 244 sort.Sort(bytesIntfSlice(v2)) 245 for j := range v2 { 246 e.mapElemKey() 247 e.asis(v2[j].v) 248 e.mapElemValue() 249 e.encode(v[v2[j].i]) 250 } {{else}}{{ $x := sorttype .MapKey true}}v2 := make([]{{ $x }}, len(v)) 251 var i uint 252 for k := range v { 253 v2[i] = {{if eq $x .MapKey}}k{{else}}{{ $x }}(k){{end}} 254 i++ 255 } 256 sort.Sort({{ sorttype .MapKey false}}(v2)) 257 for _, k2 := range v2 { 258 e.mapElemKey() 259 {{if eq .MapKey "string"}} e.e.EncodeString(k2) {{else}}{{ $y := printf "%s(k2)" .MapKey }}{{if eq $x .MapKey }}{{ $y = "k2" }}{{end}}{{ encmd .MapKey $y }}{{end}} 260 e.mapElemValue() 261 {{ $y := printf "v[%s(k2)]" .MapKey }}{{if eq $x .MapKey }}{{ $y = "v[k2]" }}{{end}}{{ encmd .Elem $y }} 262 } {{end}} 263 } else { 264 for k2, v2 := range v { 265 e.mapElemKey() 266 {{if eq .MapKey "string"}} e.e.EncodeString(k2) {{else}}{{ encmd .MapKey "k2"}}{{end}} 267 e.mapElemValue() 268 {{ encmd .Elem "v2"}} 269 } 270 } 271 e.mapEnd() 272 } 273 {{end}}{{end}}{{end -}} 274 275 // -- decode 276 277 // -- -- fast path type switch 278 func fastpathDecodeTypeSwitch(iv interface{}, d *Decoder) bool { 279 var changed bool 280 var containerLen int 281 switch v := iv.(type) { 282 {{range .Values}}{{if not .Primitive}}{{if not .MapKey -}} 283 case []{{ .Elem }}: 284 fastpathTV.{{ .MethodNamePfx "Dec" false }}N(v, d) 285 case *[]{{ .Elem }}: 286 var v2 []{{ .Elem }} 287 if v2, changed = fastpathTV.{{ .MethodNamePfx "Dec" false }}Y(*v, d); changed { 288 *v = v2 289 } 290 {{end}}{{end}}{{end -}} 291 {{range .Values}}{{if not .Primitive}}{{if .MapKey }}{{/* 292 // maps only change if nil, and in that case, there's no point copying 293 */ -}} 294 case map[{{ .MapKey }}]{{ .Elem }}: 295 containerLen = d.mapStart(d.d.ReadMapStart()) 296 if containerLen != containerLenNil { 297 if containerLen != 0 { 298 fastpathTV.{{ .MethodNamePfx "Dec" false }}L(v, containerLen, d) 299 } 300 d.mapEnd() 301 } 302 case *map[{{ .MapKey }}]{{ .Elem }}: 303 {{/* 304 containerLen = d.mapStart(d.d.ReadMapStart()) 305 if containerLen == 0 { 306 d.mapEnd() 307 } else if containerLen == containerLenNil { 308 *v = nil 309 } else { 310 if *v == nil { 311 *v = make(map[{{ .MapKey }}]{{ .Elem }}, decInferLen(containerLen, d.h.MaxInitLen, {{ .Size }})) 312 } 313 fastpathTV.{{ .MethodNamePfx "Dec" false }}L(*v, containerLen, d) 314 } 315 // consider delegating fully to X - encoding *map is uncommon, so ok to pay small function call cost 316 */ -}} 317 fastpathTV.{{ .MethodNamePfx "Dec" false }}X(v, d) 318 {{end}}{{end}}{{end -}} 319 default: 320 _ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4 321 return false 322 } 323 return true 324 } 325 326 func fastpathDecodeSetZeroTypeSwitch(iv interface{}) bool { 327 switch v := iv.(type) { 328 {{range .Values}}{{if not .Primitive}}{{if not .MapKey -}} 329 case *[]{{ .Elem }}: 330 *v = nil 331 {{end}}{{end}}{{end}} 332 {{range .Values}}{{if not .Primitive}}{{if .MapKey -}} 333 case *map[{{ .MapKey }}]{{ .Elem }}: 334 *v = nil 335 {{end}}{{end}}{{end}} 336 default: 337 _ = v // workaround https://github.com/golang/go/issues/12927 seen in go1.4 338 return false 339 } 340 return true 341 } 342 343 // -- -- fast path functions 344 {{range .Values}}{{if not .Primitive}}{{if not .MapKey -}} 345 {{/* 346 Slices can change if they 347 - did not come from an array 348 - are addressable (from a ptr) 349 - are settable (e.g. contained in an interface{}) 350 */}} 351 func (d *Decoder) {{ .MethodNamePfx "fastpathDec" false }}R(f *codecFnInfo, rv reflect.Value) { 352 {{/* 353 // seqTypeArray=true means that we are not getting a pointer, so no need to check that. 354 if f.seq != seqTypeArray && rv.Kind() == reflect.Ptr { 355 */ -}} 356 var v []{{ .Elem }} 357 switch rv.Kind() { 358 case reflect.Ptr: 359 vp := rv2i(rv).(*[]{{ .Elem }}) 360 var changed bool 361 if v, changed = fastpathTV.{{ .MethodNamePfx "Dec" false }}Y(*vp, d); changed { 362 *vp = v 363 } 364 case reflect.Array: 365 rvGetSlice4Array(rv, &v) 366 fastpathTV.{{ .MethodNamePfx "Dec" false }}N(v, d) 367 default: 368 fastpathTV.{{ .MethodNamePfx "Dec" false }}N(rv2i(rv).([]{{ .Elem }}), d) 369 } 370 } 371 func (f fastpathT) {{ .MethodNamePfx "Dec" false }}X(vp *[]{{ .Elem }}, d *Decoder) { 372 if v, changed := f.{{ .MethodNamePfx "Dec" false }}Y(*vp, d); changed { *vp = v } 373 } 374 func (fastpathT) {{ .MethodNamePfx "Dec" false }}Y(v []{{ .Elem }}, d *Decoder) (v2 []{{ .Elem }}, changed bool) { 375 {{ if eq .Elem "uint8" "byte" -}} 376 switch d.d.ContainerType() { 377 case valueTypeNil, valueTypeMap: 378 break 379 default: 380 v2 = d.decodeBytesInto(v[:len(v):len(v)]) 381 changed = !(len(v2) > 0 && len(v2) == len(v) && &v2[0] == &v[0]) // not same slice 382 return 383 } 384 {{ end -}} 385 slh, containerLenS := d.decSliceHelperStart() 386 if slh.IsNil { 387 if v == nil { return } 388 return nil, true 389 } 390 if containerLenS == 0 { 391 if v == nil { v = []{{ .Elem }}{} } else if len(v) != 0 { v = v[:0] } 392 slh.End() 393 return v, true 394 } 395 hasLen := containerLenS > 0 396 var xlen int 397 if hasLen { 398 if containerLenS > cap(v) { 399 xlen = decInferLen(containerLenS, d.h.MaxInitLen, {{ .Size }}) 400 if xlen <= cap(v) { 401 v = v[:uint(xlen)] 402 } else { 403 v = make([]{{ .Elem }}, uint(xlen)) 404 } 405 changed = true 406 } else if containerLenS != len(v) { 407 v = v[:containerLenS] 408 changed = true 409 } 410 } 411 var j int 412 for j = 0; d.containerNext(j, containerLenS, hasLen); j++ { 413 if j == 0 && len(v) == 0 { // means hasLen == false 414 xlen = decInferLen(containerLenS, d.h.MaxInitLen, {{ .Size }}) {{/* xlen = decDefSliceCap */}} 415 v = make([]{{ .Elem }}, uint(xlen)) 416 changed = true 417 } 418 {{/* // if indefinite, etc, then expand the slice if necessary */ -}} 419 if j >= len(v) { 420 v = append(v, {{ zerocmd .Elem }}) 421 changed = true 422 } 423 slh.ElemContainerState(j) 424 {{ if eq .Elem "interface{}" }}d.decode(&v[uint(j)]){{ else }}v[uint(j)] = {{ decmd .Elem false }}{{ end }} 425 } 426 if j < len(v) { 427 v = v[:uint(j)] 428 changed = true 429 } else if j == 0 && v == nil { 430 v = []{{ .Elem }}{} 431 changed = true 432 } 433 slh.End() 434 return v, changed 435 } 436 func (fastpathT) {{ .MethodNamePfx "Dec" false }}N(v []{{ .Elem }}, d *Decoder) { 437 {{ if eq .Elem "uint8" "byte" -}} 438 switch d.d.ContainerType() { 439 case valueTypeNil, valueTypeMap: 440 break 441 default: 442 v2 := d.decodeBytesInto(v[:len(v):len(v)]) 443 if !(len(v2) > 0 && len(v2) == len(v) && &v2[0] == &v[0]) { // not same slice 444 copy(v, v2) 445 } 446 return 447 } 448 {{ end -}} 449 slh, containerLenS := d.decSliceHelperStart() 450 if slh.IsNil { 451 return 452 } 453 if containerLenS == 0 { 454 slh.End() 455 return 456 } 457 hasLen := containerLenS > 0 458 for j := 0; d.containerNext(j, containerLenS, hasLen); j++ { 459 {{/* // if indefinite, etc, then expand the slice if necessary */ -}} 460 if j >= len(v) { 461 slh.arrayCannotExpand(hasLen, len(v), j, containerLenS) 462 return 463 } 464 slh.ElemContainerState(j) 465 {{ if eq .Elem "interface{}" -}} 466 d.decode(&v[uint(j)]) 467 {{- else -}} 468 v[uint(j)] = {{ decmd .Elem false }} 469 {{- end }} 470 } 471 slh.End() 472 } 473 {{end}}{{end}}{{end -}} 474 475 {{range .Values}}{{if not .Primitive}}{{if .MapKey -}} 476 {{/* 477 Maps can change if they are 478 - addressable (from a ptr) 479 - settable (e.g. contained in an interface{}) 480 481 Also, these methods are called by decodeValue directly, after handling a TryNil. 482 Consequently, there's no need to check for containerLenNil here. 483 */ -}} 484 func (d *Decoder) {{ .MethodNamePfx "fastpathDec" false }}R(f *codecFnInfo, rv reflect.Value) { 485 containerLen := d.mapStart(d.d.ReadMapStart()) 486 {{/* 487 if containerLen == containerLenNil { 488 if rv.Kind() == reflect.Ptr { 489 *(rv2i(rv).(*map[{{ .MapKey }}]{{ .Elem }})) = nil 490 } 491 return 492 } 493 */ -}} 494 if rv.Kind() == reflect.Ptr { 495 vp, _ := rv2i(rv).(*map[{{ .MapKey }}]{{ .Elem }}) 496 if *vp == nil { 497 *vp = make(map[{{ .MapKey }}]{{ .Elem }}, decInferLen(containerLen, d.h.MaxInitLen, {{ .Size }})) 498 } 499 if containerLen != 0 { 500 fastpathTV.{{ .MethodNamePfx "Dec" false }}L(*vp, containerLen, d) 501 } 502 } else if containerLen != 0 { 503 fastpathTV.{{ .MethodNamePfx "Dec" false }}L(rv2i(rv).(map[{{ .MapKey }}]{{ .Elem }}), containerLen, d) 504 } 505 d.mapEnd() 506 } 507 func (f fastpathT) {{ .MethodNamePfx "Dec" false }}X(vp *map[{{ .MapKey }}]{{ .Elem }}, d *Decoder) { 508 containerLen := d.mapStart(d.d.ReadMapStart()) 509 if containerLen == containerLenNil { 510 *vp = nil 511 } else { 512 if *vp == nil { 513 *vp = make(map[{{ .MapKey }}]{{ .Elem }}, decInferLen(containerLen, d.h.MaxInitLen, {{ .Size }})) 514 } 515 if containerLen != 0 { 516 f.{{ .MethodNamePfx "Dec" false }}L(*vp, containerLen, d) 517 } 518 d.mapEnd() 519 } 520 } 521 func (fastpathT) {{ .MethodNamePfx "Dec" false }}L(v map[{{ .MapKey }}]{{ .Elem }}, containerLen int, d *Decoder) { 522 {{/* No need to check if containerLen == containerLenNil, as that is checked by R and L above */ -}} 523 if v == nil { 524 d.errorf("cannot decode into nil map[{{ .MapKey }}]{{ .Elem }} given stream length: %v", containerLen) 525 {{/* d.swallowMapContents(containerLen) */ -}} 526 return 527 } 528 {{if eq .Elem "interface{}" }}mapGet := v != nil && !d.h.MapValueReset && !d.h.InterfaceReset 529 {{else if eq .Elem "bytes" "[]byte" }}mapGet := v != nil && !d.h.MapValueReset 530 {{end -}} 531 var mk {{ .MapKey }} 532 var mv {{ .Elem }} 533 hasLen := containerLen > 0 534 for j := 0; d.containerNext(j, containerLen, hasLen); j++ { 535 d.mapElemKey() 536 {{ if eq .MapKey "interface{}" }}mk = nil 537 d.decode(&mk) 538 if bv, bok := mk.([]byte); bok { 539 mk = d.stringZC(bv) {{/* // maps cannot have []byte as key. switch to string. */}} 540 }{{ else }}mk = {{ decmd .MapKey true }}{{ end }} 541 d.mapElemValue() 542 {{ if eq .Elem "interface{}" "[]byte" "bytes" -}} 543 if mapGet { mv = v[mk] } else { mv = nil } 544 {{ end -}} 545 {{ if eq .Elem "interface{}" -}} 546 d.decode(&mv) 547 {{ else if eq .Elem "[]byte" "bytes" -}} 548 mv = d.decodeBytesInto(mv) 549 {{ else -}} 550 mv = {{ decmd .Elem false }} 551 {{ end -}} 552 v[mk] = mv 553 } 554 } 555 {{end}}{{end}}{{end}}