用户登录和接口鉴权

This commit is contained in:
2025-09-07 21:13:15 +08:00
parent c4522b974b
commit 565cf3fa6a
380 changed files with 18330 additions and 16854 deletions

View File

@@ -80,32 +80,6 @@ Rich Feature Set includes:
rpc server/client codec to support msgpack-rpc protocol defined at:
https://github.com/msgpack-rpc/msgpack-rpc/blob/master/spec.md
# Supported build tags
We gain performance by code-generating fast-paths for slices and maps of built-in types,
and monomorphizing generic code explicitly so we gain inlining and de-virtualization benefits.
The results are 20-40% performance improvements.
Building and running is configured using build tags as below.
At runtime:
- codec.safe: run in safe mode (not using unsafe optimizations)
- codec.notmono: use generics code (bypassing performance-boosting monomorphized code)
- codec.notfastpath: skip fast path code for slices and maps of built-in types (number, bool, string, bytes)
Each of these "runtime" tags have a convenience synonym i.e. safe, notmono, notfastpath.
Pls use these mostly during development - use codec.XXX in your go files.
Build only:
- codec.build: used to generate fastpath and monomorphization code
Test only:
- codec.notmammoth: skip the mammoth generated tests
# Extension Support
Users can register a function to handle the encoding or decoding of their custom
@@ -245,12 +219,6 @@ You can run the tag 'codec.safe' to run tests or build in safe mode. e.g.
go test -tags "alltests codec.safe" -run Suite
```
You can run the tag 'codec.notmono' to build bypassing the monomorphized code e.g.
```
go test -tags codec.notmono -run Json
```
# Running Benchmarks
```

File diff suppressed because it is too large Load Diff

View File

@@ -1,61 +1,232 @@
#!/bin/bash
# Build and Run the different test permutations.
# This helps validate that nothing gets broken.
# Run all the different permutations of all the tests and other things
# This helps ensure that nothing gets broken.
_build_proceed() {
# return success (0) if we should, and 1 (fail) if not
if [[ "${zforce}" ]]; then return 0; fi
for a in "fastpath.generated.go" "json.mono.generated.go"; do
if [[ ! -e "$a" ]]; then return 0; fi
for b in `ls -1 *.go.tmpl gen.go gen_mono.go values_test.go`; do
if [[ "$a" -ot "$b" ]]; then return 0; fi
done
_tests() {
local vet="" # TODO: make it off
local gover=$( ${gocmd} version | cut -f 3 -d ' ' )
[[ $( ${gocmd} version ) == *"gccgo"* ]] && zcover=0
[[ $( ${gocmd} version ) == *"gollvm"* ]] && zcover=0
case $gover in
go1.[7-9]*|go1.1[0-9]*|go2.*|devel*) true ;;
*) return 1
esac
# note that codecgen requires fastpath, so you cannot do "codecgen codec.notfastpath"
# we test the following permutations wnich all execute different code paths as below.
echo "TestCodecSuite: (fastpath/unsafe), (!fastpath/unsafe), (fastpath/!unsafe), (!fastpath/!unsafe), (codecgen/unsafe)"
local echo=1
local nc=2 # count
local cpus="1,$(nproc)"
# if using the race detector, then set nc to
if [[ " ${zargs[@]} " =~ "-race" ]]; then
cpus="$(nproc)"
fi
local a=( "" "codec.notfastpath" "codec.safe" "codec.notfastpath codec.safe" "codecgen" )
local b=()
local c=()
for i in "${a[@]}"
do
local i2=${i:-default}
[[ "$zwait" == "1" ]] && echo ">>>> TAGS: 'alltests $i'; RUN: 'TestCodecSuite'"
[[ "$zcover" == "1" ]] && c=( -coverprofile "${i2// /-}.cov.out" )
true &&
${gocmd} vet -printfuncs "errorf" "$@" &&
if [[ "$echo" == 1 ]]; then set -o xtrace; fi &&
${gocmd} test ${zargs[*]} ${ztestargs[*]} -vet "$vet" -tags "alltests $i" -count $nc -cpu $cpus -run "TestCodecSuite" "${c[@]}" "$@" &
if [[ "$echo" == 1 ]]; then set +o xtrace; fi
b+=("${i2// /-}.cov.out")
[[ "$zwait" == "1" ]] && wait
# if [[ "$?" != 0 ]]; then return 1; fi
done
return 1
if [[ "$zextra" == "1" ]]; then
[[ "$zwait" == "1" ]] && echo ">>>> TAGS: 'codec.notfastpath x'; RUN: 'Test.*X$'"
[[ "$zcover" == "1" ]] && c=( -coverprofile "x.cov.out" )
${gocmd} test ${zargs[*]} ${ztestargs[*]} -vet "$vet" -tags "codec.notfastpath x" -count $nc -run 'Test.*X$' "${c[@]}" &
b+=("x.cov.out")
[[ "$zwait" == "1" ]] && wait
fi
wait
# go tool cover is not supported for gccgo, gollvm, other non-standard go compilers
[[ "$zcover" == "1" ]] &&
command -v gocovmerge &&
gocovmerge "${b[@]}" > __merge.cov.out &&
${gocmd} tool cover -html=__merge.cov.out
}
# _build generates fastpath.go
# is a generation needed?
_ng() {
local a="$1"
if [[ ! -e "$a" ]]; then echo 1; return; fi
for i in `ls -1 *.go.tmpl gen.go values_test.go`
do
if [[ "$a" -ot "$i" ]]; then echo 1; return; fi
done
}
_prependbt() {
cat > ${2} <<EOF
// +build generated
EOF
cat ${1} >> ${2}
rm -f ${1}
}
# _build generates fast-path.go and gen-helper.go.
_build() {
# if ! [[ "${zforce}" || $(_ng "fastpath.generated.go") || $(_ng "json.mono.generated.go") ]]; then return 0; fi
_build_proceed
if [ $? -eq 1 ]; then return 0; fi
if ! [[ "${zforce}" || $(_ng "fast-path.generated.go") || $(_ng "gen-helper.generated.go") || $(_ng "gen.generated.go") ]]; then return 0; fi
if [ "${zbak}" ]; then
_zts=`date '+%m%d%Y_%H%M%S'`
_gg=".generated.go"
[ -e "fastpath${_gg}" ] && mv fastpath${_gg} fastpath${_gg}__${_zts}.bak
[ -e "gen-helper${_gg}" ] && mv gen-helper${_gg} gen-helper${_gg}__${_zts}.bak
[ -e "fast-path${_gg}" ] && mv fast-path${_gg} fast-path${_gg}__${_zts}.bak
[ -e "gen${_gg}" ] && mv gen${_gg} gen${_gg}__${_zts}.bak
fi
rm -f fast*path.generated.go *mono*generated.go *_generated_test.go gen-from-tmpl*.generated.go
fi
rm -f gen-helper.generated.go fast-path.generated.go gen.generated.go \
*safe.generated.go *_generated_test.go *.generated_ffjson_expose.go
local btags="codec.build codec.notmono codec.safe codec.notfastpath"
cat > gen.generated.go <<EOF
// +build codecgen.exec
// Copyright (c) 2012-2020 Ugorji Nwoke. All rights reserved.
// Use of this source code is governed by a MIT license found in the LICENSE file.
cat > gen-from-tmpl.codec.generated.go <<EOF
package codec
func GenTmplRun2Go(in, out string) { genTmplRun2Go(in, out) }
func GenMonoAll() { genMonoAll() }
// DO NOT EDIT. THIS FILE IS AUTO-GENERATED FROM gen-dec-(map|array).go.tmpl
const genDecMapTmpl = \`
EOF
cat >> gen.generated.go < gen-dec-map.go.tmpl
cat >> gen.generated.go <<EOF
\`
const genDecListTmpl = \`
EOF
cat >> gen.generated.go < gen-dec-array.go.tmpl
cat >> gen.generated.go <<EOF
\`
const genEncChanTmpl = \`
EOF
cat >> gen.generated.go < gen-enc-chan.go.tmpl
cat >> gen.generated.go <<EOF
\`
EOF
cat > gen-from-tmpl.codec.generated.go <<EOF
package codec
func GenRunTmpl2Go(in, out string) { genRunTmpl2Go(in, out) }
func GenRunSortTmpl2Go(in, out string) { genRunSortTmpl2Go(in, out) }
EOF
cat > gen-from-tmpl.generated.go <<EOF
//go:build ignore
# stub xxxRv and xxxRvSlice creation, before you create it
cat > gen-from-tmpl.sort-slice-stubs.generated.go <<EOF
// +build codecgen.sort_slice
package codec
import "reflect"
import "time"
EOF
for i in string bool uint64 int64 float64 bytes time; do
local i2=$i
case $i in
'time' ) i2="time.Time";;
'bytes' ) i2="[]byte";;
esac
cat >> gen-from-tmpl.sort-slice-stubs.generated.go <<EOF
type ${i}Rv struct { v ${i2}; r reflect.Value }
type ${i}RvSlice []${i}Rv
func (${i}RvSlice) Len() int { return 0 }
func (${i}RvSlice) Less(i, j int) bool { return false }
func (${i}RvSlice) Swap(i, j int) {}
type ${i}Intf struct { v ${i2}; i interface{} }
type ${i}IntfSlice []${i}Intf
func (${i}IntfSlice) Len() int { return 0 }
func (${i}IntfSlice) Less(i, j int) bool { return false }
func (${i}IntfSlice) Swap(i, j int) {}
EOF
done
sed -e 's+// __DO_NOT_REMOVE__NEEDED_FOR_REPLACING__IMPORT_PATH__FOR_CODEC_BENCH__+import . "github.com/ugorji/go/codec"+' \
shared_test.go > bench/shared_test.go
# explicitly return 0 if this passes, else return 1
local btags="codec.notfastpath codec.safe codecgen.exec"
rm -f sort-slice.generated.go fast-path.generated.go gen-helper.generated.go mammoth_generated_test.go mammoth2_generated_test.go
cat > gen-from-tmpl.sort-slice.generated.go <<EOF
// +build ignore
package main
import "${zpkg}"
func main() {
codec.GenTmplRun2Go("fastpath.go.tmpl", "base.fastpath.generated.go")
codec.GenTmplRun2Go("fastpath.notmono.go.tmpl", "base.fastpath.notmono.generated.go")
codec.GenTmplRun2Go("mammoth_test.go.tmpl", "mammoth_generated_test.go")
codec.GenMonoAll()
codec.GenRunSortTmpl2Go("sort-slice.go.tmpl", "sort-slice.generated.go")
}
EOF
${gocmd} run -tags "$btags codecgen.sort_slice" gen-from-tmpl.sort-slice.generated.go || return 1
rm -f gen-from-tmpl.sort-slice.generated.go
cat > gen-from-tmpl.generated.go <<EOF
// +build ignore
package main
import "${zpkg}"
func main() {
codec.GenRunTmpl2Go("fast-path.go.tmpl", "fast-path.generated.go")
codec.GenRunTmpl2Go("gen-helper.go.tmpl", "gen-helper.generated.go")
codec.GenRunTmpl2Go("mammoth-test.go.tmpl", "mammoth_generated_test.go")
codec.GenRunTmpl2Go("mammoth2-test.go.tmpl", "mammoth2_generated_test.go")
}
EOF
# explicitly return 0 if this passes, else return 1
${gocmd} run -tags "$btags" gen-from-tmpl.generated.go || return 1
rm -f gen-from-tmpl*.generated.go
rm -f gen-from-tmpl.generated.go
rm -f gen-from-tmpl.*generated.go
return 0
}
_codegenerators() {
local c5="_generated_test.go"
local c7="$PWD/codecgen"
local c8="$c7/__codecgen"
local c9="codecgen-scratch.go"
if ! [[ $zforce || $(_ng "values_codecgen${c5}") ]]; then return 0; fi
# Note: ensure you run the codecgen for this codebase/directory i.e. ./codecgen/codecgen
true &&
echo "codecgen ... " &&
if [[ $zforce || ! -f "$c8" || "$c7/gen.go" -nt "$c8" ]]; then
echo "rebuilding codecgen ... " && ( cd codecgen && ${gocmd} build -o $c8 ${zargs[*]} . )
fi &&
$c8 -rt 'codecgen' -t 'codecgen generated' -o "values_codecgen${c5}" -d 19780 "$zfin" "$zfin2" &&
cp mammoth2_generated_test.go $c9 &&
$c8 -t 'codecgen,!codec.notfastpath,!codec.notmammoth generated,!codec.notfastpath,!codec.notmammoth' -o "mammoth2_codecgen${c5}" -d 19781 "mammoth2_generated_test.go" &&
rm -f $c9 &&
echo "generators done!"
}
_prebuild() {
echo "prebuild: zforce: $zforce"
local d="$PWD"
local zfin="test_values.generated.go"
local zfin2="test_values_flex.generated.go"
@@ -65,12 +236,13 @@ _prebuild() {
# zpkg=${d##*/src/}
# zgobase=${d%%/src/*}
# rm -f *_generated_test.go
# if [[ $zforce ]]; then ${gocmd} install ${zargs[*]} .; fi &&
true &&
rm -f codecgen-*.go &&
_build &&
cp $d/values_test.go $d/$zfin &&
cp $d/values_flex_test.go $d/$zfin2 &&
_codegenerators &&
if [[ "$(type -t _codegenerators_external )" = "function" ]]; then _codegenerators_external ; fi &&
if [[ $zforce ]]; then ${gocmd} install ${zargs[*]} .; fi &&
returncode=0 &&
echo "prebuild done successfully"
rm -f $d/$zfin $d/$zfin2
@@ -79,67 +251,54 @@ _prebuild() {
}
_make() {
_prebuild && ${gocmd} install ${zargs[*]} .
local makeforce=${zforce}
zforce=1
(cd codecgen && ${gocmd} install ${zargs[*]} .) && _prebuild && ${gocmd} install ${zargs[*]} .
zforce=${makeforce}
}
_clean() {
rm -f \
gen-from-tmpl.*generated.go \
codecgen-*.go \
test_values.generated.go test_values_flex.generated.go
}
_tests_run_one() {
local tt="alltests $i"
local rr="TestCodecSuite"
if [[ "x$i" == "xx" ]]; then tt="codec.notmono codec.notfastpath x"; rr='Test.*X$'; fi
local g=( ${zargs[*]} ${ztestargs[*]} -count $nc -cpu $cpus -vet "$vet" -tags "$tt" -run "$rr" )
[[ "$zcover" == "1" ]] && g+=( -cover )
# g+=( -ti "$k" )
g+=( -tdiff )
[[ "$zcover" == "1" ]] && g+=( -test.gocoverdir $covdir )
local -
set -x
${gocmd} test "${g[@]}" &
}
_release() {
local reply
read -p "Pre-release validation takes a few minutes and MUST be run from within GOPATH/src. Confirm y/n? " -n 1 -r reply
echo
if [[ ! $reply =~ ^[Yy]$ ]]; then return 1; fi
_tests() {
local vet="" # TODO: make it off
local gover=$( ${gocmd} version | cut -f 3 -d ' ' )
# go tool cover is not supported for gccgo, gollvm, other non-standard go compilers
[[ $( ${gocmd} version ) == *"gccgo"* ]] && zcover=0
[[ $( ${gocmd} version ) == *"gollvm"* ]] && zcover=0
case $gover in
go1.2[0-9]*|go2.*|devel*) true ;;
*) return 1
esac
# we test the following permutations wnich all execute different code paths as below.
echo "TestCodecSuite: (fastpath/unsafe), (!fastpath/unsafe), (fastpath/!unsafe), (!fastpath/!unsafe)"
local nc=2 # count
local cpus="1,$(nproc)"
# if using the race detector, then set nc to
if [[ " ${zargs[@]} " =~ "-race" ]]; then
cpus="$(nproc)"
fi
local covdir=""
local a=( "" "codec.safe" "codec.notfastpath" "codec.safe codec.notfastpath"
"codec.notmono" "codec.notmono codec.safe"
"codec.notmono codec.notfastpath" "codec.notmono codec.safe codec.notfastpath" )
[[ "$zextra" == "1" ]] && a+=( "x" )
[[ "$zcover" == "1" ]] && covdir=`mktemp -d`
${gocmd} vet -printfuncs "errorf" "$@" || return 1
for i in "${a[@]}"; do
local j=${i:-default}; j="${j// /-}"; j="${j//codec./}"
[[ "$zwait" == "1" ]] && echo ">>>> TAGS: 'alltests $i'; RUN: 'TestCodecSuite'"
_tests_run_one
[[ "$zwait" == "1" ]] && wait
# if [[ "$?" != 0 ]]; then return 1; fi
# expects GOROOT, GOROOT_BOOTSTRAP to have been set.
if [[ -z "${GOROOT// }" || -z "${GOROOT_BOOTSTRAP// }" ]]; then return 1; fi
# (cd $GOROOT && git checkout -f master && git pull && git reset --hard)
(cd $GOROOT && git pull)
local f=`pwd`/make.release.out
cat > $f <<EOF
========== `date` ===========
EOF
# # go 1.6 and below kept giving memory errors on Mac OS X during SDK build or go run execution,
# # that is fine, as we only explicitly test the last 3 releases and tip (2 years).
local makeforce=${zforce}
zforce=1
for i in 1.10 1.11 1.12 master
do
echo "*********** $i ***********" >>$f
if [[ "$i" != "master" ]]; then i="release-branch.go$i"; fi
(false ||
(echo "===== BUILDING GO SDK for branch: $i ... =====" &&
cd $GOROOT &&
git checkout -f $i && git reset --hard && git clean -f . &&
cd src && ./make.bash >>$f 2>&1 && sleep 1 ) ) &&
echo "===== GO SDK BUILD DONE =====" &&
_prebuild &&
echo "===== PREBUILD DONE with exit: $? =====" &&
_tests "$@"
if [[ "$?" != 0 ]]; then return 1; fi
done
wait
[[ "$zcover" == "1" ]] &&
echo "go tool covdata output" &&
${gocmd} tool covdata percent -i $covdir &&
${gocmd} tool covdata textfmt -i $covdir -o __cov.out &&
${gocmd} tool cover -html=__cov.out
zforce=${makeforce}
echo "++++++++ RELEASE TEST SUITES ALL PASSED ++++++++"
}
_usage() {
@@ -147,10 +306,11 @@ _usage() {
# -pf [p=prebuild (f=force)]
cat <<EOF
primary usage: $0
primary usage: $0
-t[esow] -> t=tests [e=extra, s=short, o=cover, w=wait]
-[md] -> [m=make, d=race detector]
-v -> v=verbose (more v's to increase verbose level)
-[n l i] -> [n=inlining diagnostics, l=mid-stack inlining, i=check inlining for path (path)]
-v -> v=verbose
EOF
if [[ "$(type -t _usage_run)" = "function" ]]; then _usage_run ; fi
}
@@ -171,15 +331,15 @@ _main() {
local gocmd=${MYGOCMD:-go}
OPTIND=1
while getopts ":cetmnrgpfvldsowikxyz" flag
while getopts ":cetmnrgpfvldsowkxyzi" flag
do
case "x$flag" in
'xw') zwait=1 ;;
'xv') zverbose+=(1) ;;
'xo') zcover=1 ;;
'xe') zextra=1 ;;
'xw') zwait=1 ;;
'xf') zforce=1 ;;
'xs') ztestargs+=("-short") ;;
'xv') zverbose+=(1) ;;
'xl') zargs+=("-gcflags"); zargs+=("-l=4") ;;
'xn') zargs+=("-gcflags"); zargs+=("-m=2") ;;
'xd') zargs+=("-race") ;;
@@ -197,23 +357,14 @@ _main() {
'xg') _go ;;
'xp') _prebuild "$@" ;;
'xc') _clean "$@" ;;
esac
# handle from local run.sh
case "x$x" in
'xi') _check_inlining_one "$@" ;;
'xk') _go_compiler_validation_suite ;;
'xx') _analyze_checks "$@" ;;
'xy') _analyze_debug_types "$@" ;;
'xz') _analyze_do_inlining_and_more "$@" ;;
'xk') _go_compiler_validation_suite ;;
'xi') _check_inlining_one "$@" ;;
esac
# unset zforce zargs zbenchflags
}
[ "." = `dirname $0` ] && _main "$@"
# _xtrace() {
# local -
# set -x
# "${@}"
# }

File diff suppressed because it is too large Load Diff

View File

@@ -8,19 +8,6 @@ import (
"strconv"
)
type readFloatResult struct {
mantissa uint64
exp int8
neg bool
trunc bool
bad bool // bad decimal string
hardexp bool // exponent is hard to handle (> 2 digits, etc)
ok bool
// sawdot bool
// sawexp bool
//_ [2]bool // padding
}
// Per go spec, floats are represented in memory as
// IEEE single or double precision floating point values.
//
@@ -247,10 +234,6 @@ func parseFloat64_custom(b []byte) (f float64, err error) {
}
func parseUint64_simple(b []byte) (n uint64, ok bool) {
if len(b) > 1 && b[0] == '0' { // punt on numbers with leading zeros
return
}
var i int
var n1 uint64
var c uint8
@@ -373,6 +356,19 @@ func parseNumber(b []byte, z *fauxUnion, preferSignedInt bool) (err error) {
return
}
type readFloatResult struct {
mantissa uint64
exp int8
neg bool
trunc bool
bad bool // bad decimal string
hardexp bool // exponent is hard to handle (> 2 digits, etc)
ok bool
// sawdot bool
// sawexp bool
//_ [2]bool // padding
}
func readFloat(s []byte, y floatinfo) (r readFloatResult) {
var i uint // uint, so that we eliminate bounds checking
var slen = uint(len(s))
@@ -388,23 +384,13 @@ func readFloat(s []byte, y floatinfo) (r readFloatResult) {
i++
}
// considered punting early if string has length > maxMantDigits, but doesn't account
// we considered punting early if string has length > maxMantDigits, but this doesn't account
// for trailing 0's e.g. 700000000000000000000 can be encoded exactly as it is 7e20
var nd, ndMant, dp int8
var sawdot, sawexp bool
var xu uint64
if i+1 < slen && s[i] == '0' {
switch s[i+1] {
case '.', 'e', 'E':
// ok
default:
r.bad = true
return
}
}
LOOP:
for ; i < slen; i++ {
switch s[i] {

File diff suppressed because it is too large Load Diff

View File

@@ -12,7 +12,7 @@ Supported Serialization formats are:
- binc: http://github.com/ugorji/binc
- cbor: http://cbor.io http://tools.ietf.org/html/rfc7049
- json: http://json.org http://tools.ietf.org/html/rfc7159
- simple: (unpublished)
- simple:
This package will carefully use 'package unsafe' for performance reasons in specific places.
You can build without unsafe use by passing the safe or appengine tag
@@ -78,32 +78,6 @@ Rich Feature Set includes:
msgpack-rpc protocol defined at:
https://github.com/msgpack-rpc/msgpack-rpc/blob/master/spec.md
# Supported build tags
We gain performance by code-generating fast-paths for slices and maps of built-in types,
and monomorphizing generic code explicitly so we gain inlining and de-virtualization benefits.
The results are 20-40% performance improvements.
Building and running is configured using build tags as below.
At runtime:
- codec.safe: run in safe mode (not using unsafe optimizations)
- codec.notmono: use generics code (bypassing performance-boosting monomorphized code)
- codec.notfastpath: skip fast path code for slices and maps of built-in types (number, bool, string, bytes)
Each of these "runtime" tags have a convenience synonym i.e. safe, notmono, notfastpath.
Pls use these mostly during development - use codec.XXX in your go files.
Build only:
- codec.build: used to generate fastpath and monomorphization code
Test only:
- codec.notmammoth: skip the mammoth generated tests
# Extension Support
Users can register a function to handle the encoding or decoding of
@@ -229,10 +203,6 @@ You can run the tag 'codec.safe' to run tests or build in safe mode. e.g.
go test -tags codec.safe -run Json
go test -tags "alltests codec.safe" -run Suite
You can run the tag 'codec.notmono' to build bypassing the monomorphized code e.g.
go test -tags codec.notmono -run Json
Running Benchmarks
cd bench
@@ -255,87 +225,3 @@ Embedded fields are encoded as if they exist in the top-level struct,
with some caveats. See Encode documentation.
*/
package codec
/*
Generics
Generics are used across to board to reduce boilerplate, and hopefully
improve performance by
- reducing need for interface calls (de-virtualization)
- resultant inlining of those calls
encoder/decoder --> Driver (json/cbor/...) --> input/output (bytes or io abstraction)
There are 2 * 5 * 2 (20) combinations of monomorphized values.
Key rules
- do not use top-level generic functions.
Due to type inference, monomorphizing them proves challenging
- only use generic methods.
Monomorphizing is done at the type once, and method names need not change
- do not have method calls have a parameter of an encWriter or decReader.
All those calls are handled directly by the driver.
- Include a helper type for each parameterized thing, and add all generic functions to them e.g.
helperEncWriter[T encWriter]
helperEncReader[T decReader]
helperEncDriver[T encDriver]
helperDecDriver[T decDriver]
- Always use T as the generic type name (when needed)
- No inline types
- No closures taking parameters of generic types
*/
/*
Naming convention:
Currently, as generic and non-generic types/functions/vars are put in the same files,
we suffer because:
- build takes longer as non-generic code is built when a build tag wants only monomorphised code
- files have many lines which are not used at runtime (due to type parameters)
- code coverage is inaccurate on a single run
To resolve this, we are streamlining our file naming strategy.
Basically, we will have the following nomenclature for filenames:
- fastpath (tag:notfastpath): *.notfastpath.*.go vs *.fastpath.*.go
- typed parameters (tag:notmono): *.notmono.*.go vs *.mono.*.go
- safe (tag:safe): *.safe.*.go vs *.unsafe.go
- generated files: *.generated.go
- all others (tags:N/A): *.go without safe/mono/fastpath/generated in the name
The following files will be affected and split/renamed accordingly
Base files:
- binc.go
- cbor.go
- json.go
- msgpack.go
- simple.go
- decode.go
- encode.go
For each base file, split into __file__.go (containing type parameters) and __file__.base.go.
__file__.go will only build with notmono.
Other files:
- fastpath.generated.go -> base.fastpath.generated.go and base.fastpath.notmono.generated.go
- fastpath.not.go -> base.notfastpath.go
- init.go -> init.notmono.go
Appropriate build tags will be included in the files, and the right ones only used for
monomorphization.
*/
/*
Caching Handle options for fast runtime use
If using cached values from Handle options, then
- re-cache them at each reset() call
- reset is always called at the start of each (Must)(En|De)code
- which calls (en|de)coder.reset([]byte|io.Reader|String)
- which calls (en|de)cDriver.reset()
- at reset, (en|de)c(oder|Driver) can re-cache Handle options before each run
Some examples:
- json: e.rawext,di,d,ks,is / d.rawext
- decode: (decoderBase) d.jsms,mtr,str,
*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT license found in the LICENSE file.
//go:build !go1.9 || safe || codec.safe || appengine
// +build !go1.9 safe codec.safe appengine
package codec
@@ -18,11 +19,8 @@ import (
const safeMode = true
func isTransientType4Size(size uint32) bool { return true }
type mapReqParams struct{}
func getMapReqParams(ti *typeInfo) (r mapReqParams) { return }
const transientSizeMax = 0
const transientValueHasStringSlice = true
func byteAt(b []byte, index uint) byte {
return b[index]
@@ -32,6 +30,14 @@ func setByteAt(b []byte, index uint, val byte) {
b[index] = val
}
func byteSliceOf(b []byte, start, end uint) []byte {
return b[start:end]
}
// func byteSliceWithLen(b []byte, length uint) []byte {
// return b[:length]
// }
func stringView(v []byte) string {
return string(v)
}
@@ -44,24 +50,32 @@ func byteSliceSameData(v1 []byte, v2 []byte) bool {
return cap(v1) != 0 && cap(v2) != 0 && &(v1[:1][0]) == &(v2[:1][0])
}
func isNil(v interface{}, checkPtr bool) (rv reflect.Value, b bool) {
b = v == nil
if b || !checkPtr {
return
}
rv = reflect.ValueOf(v)
if rv.Kind() == reflect.Ptr {
b = rv.IsNil()
}
func okBytes2(b []byte) (v [2]byte) {
copy(v[:], b)
return
}
func ptrToLowLevel(v interface{}) interface{} {
return v
func okBytes3(b []byte) (v [3]byte) {
copy(v[:], b)
return
}
func lowLevelToPtr[T any](v interface{}) *T {
return v.(*T)
func okBytes4(b []byte) (v [4]byte) {
copy(v[:], b)
return
}
func okBytes8(b []byte) (v [8]byte) {
copy(v[:], b)
return
}
func isNil(v interface{}) (rv reflect.Value, isnil bool) {
rv = reflect.ValueOf(v)
if isnilBitset.isset(byte(rv.Kind())) {
isnil = rv.IsNil()
}
return
}
func eq4i(i0, i1 interface{}) bool {
@@ -71,21 +85,17 @@ func eq4i(i0, i1 interface{}) bool {
func rv4iptr(i interface{}) reflect.Value { return reflect.ValueOf(i) }
func rv4istr(i interface{}) reflect.Value { return reflect.ValueOf(i) }
// func rv4i(i interface{}) reflect.Value { return reflect.ValueOf(i) }
// func rv4iK(i interface{}, kind byte, isref bool) reflect.Value { return reflect.ValueOf(i) }
func rv2i(rv reflect.Value) interface{} {
if rv.IsValid() {
return rv.Interface()
}
return nil
return rv.Interface()
}
func rvAddr(rv reflect.Value, ptrType reflect.Type) reflect.Value {
return rv.Addr()
}
func rvPtrIsNil(rv reflect.Value) bool {
return rv.IsNil()
}
func rvIsNil(rv reflect.Value) bool {
return rv.IsNil()
}
@@ -121,30 +131,6 @@ func i2rtid(i interface{}) uintptr {
// --------------------------
// is this an empty interface/ptr/struct/map/slice/chan/array
func isEmptyContainerValue(v reflect.Value, tinfos *TypeInfos, recursive bool) (empty bool) {
switch v.Kind() {
case reflect.Array:
for i, vlen := 0, v.Len(); i < vlen; i++ {
if !isEmptyValue(v.Index(i), tinfos, false) {
return false
}
}
return true
case reflect.Map, reflect.Slice, reflect.Chan:
return v.IsNil() || v.Len() == 0
case reflect.Interface, reflect.Ptr:
empty = v.IsNil()
if recursive && !empty {
return isEmptyValue(v.Elem(), tinfos, recursive)
}
return empty
case reflect.Struct:
return isEmptyStruct(v, tinfos, recursive)
}
return false
}
func isEmptyValue(v reflect.Value, tinfos *TypeInfos, recursive bool) bool {
switch v.Kind() {
case reflect.Invalid:
@@ -229,7 +215,7 @@ func isEmptyStruct(v reflect.Value, tinfos *TypeInfos, recursive bool) bool {
// We only care about what we can encode/decode,
// so that is what we use to check omitEmpty.
for _, si := range ti.sfi.source() {
sfv := si.fieldNoAlloc(v, true)
sfv := si.path.field(v)
if sfv.IsValid() && !isEmptyValue(sfv, tinfos, recursive) {
return false
}
@@ -237,10 +223,6 @@ func isEmptyStruct(v reflect.Value, tinfos *TypeInfos, recursive bool) bool {
return true
}
func makeMapReflect(t reflect.Type, size int) reflect.Value {
return reflect.MakeMapWithSize(t, size)
}
// --------------------------
type perTypeElem struct {
@@ -265,9 +247,13 @@ type perType struct {
v []perTypeElem
}
type decPerType = perType
type decPerType struct {
perType
}
type encPerType = perType
type encPerType struct {
perType
}
func (x *perType) elem(t reflect.Type) *perTypeElem {
rtid := rt2id(t)
@@ -309,44 +295,10 @@ func (x *perType) AddressableRO(v reflect.Value) (rv reflect.Value) {
return
}
// --------------------------
type mapIter struct {
t *reflect.MapIter
m reflect.Value
values bool
}
func (t *mapIter) Next() (r bool) {
return t.t.Next()
}
func (t *mapIter) Key() reflect.Value {
return t.t.Key()
}
func (t *mapIter) Value() (r reflect.Value) {
if t.values {
return t.t.Value()
}
return
}
func (t *mapIter) Done() {}
func mapRange(t *mapIter, m, k, v reflect.Value, values bool) {
*t = mapIter{
m: m,
t: m.MapRange(),
values: values,
}
}
// --------------------------
type structFieldInfos struct {
c []*structFieldInfo
s []*structFieldInfo
t uint8To32TrieNode
// byName map[string]*structFieldInfo // find sfi given a name
}
func (x *structFieldInfos) load(source, sorted []*structFieldInfo) {
@@ -354,24 +306,55 @@ func (x *structFieldInfos) load(source, sorted []*structFieldInfo) {
x.s = sorted
}
// func (x *structFieldInfos) count() int { return len(x.c) }
func (x *structFieldInfos) source() (v []*structFieldInfo) { return x.c }
func (x *structFieldInfos) sorted() (v []*structFieldInfo) { return x.s }
func (x *structFieldInfos) source() (v []*structFieldInfo) { return x.c }
// --------------------------
type uint8To32TrieNodeNoKids struct {
key uint8
valid bool // the value marks the end of a full stored string
_ [2]byte // padding
value uint32
type atomicClsErr struct {
v atomic.Value
}
type uint8To32TrieNodeKids = []uint8To32TrieNode
func (x *atomicClsErr) load() (e clsErr) {
if i := x.v.Load(); i != nil {
e = i.(clsErr)
}
return
}
func (x *uint8To32TrieNode) setKids(kids []uint8To32TrieNode) { x.kids = kids }
func (x *uint8To32TrieNode) getKids() []uint8To32TrieNode { return x.kids }
func (x *uint8To32TrieNode) truncKids() { x.kids = x.kids[:0] } // set len to 0
func (x *atomicClsErr) store(p clsErr) {
x.v.Store(p)
}
// --------------------------
type atomicTypeInfoSlice struct {
v atomic.Value
}
func (x *atomicTypeInfoSlice) load() (e []rtid2ti) {
if i := x.v.Load(); i != nil {
e = i.([]rtid2ti)
}
return
}
func (x *atomicTypeInfoSlice) store(p []rtid2ti) {
x.v.Store(p)
}
// --------------------------
type atomicRtidFnSlice struct {
v atomic.Value
}
func (x *atomicRtidFnSlice) load() (e []codecRtidFn) {
if i := x.v.Load(); i != nil {
e = i.([]codecRtidFn)
}
return
}
func (x *atomicRtidFnSlice) store(p []codecRtidFn) {
x.v.Store(p)
}
// --------------------------
func (n *fauxUnion) ru() reflect.Value {
@@ -518,13 +501,13 @@ func rvGrowSlice(rv reflect.Value, ti *typeInfo, cap, incr int) (v reflect.Value
// ----------------
func rvArrayIndex(rv reflect.Value, i int, _ *typeInfo, _ bool) reflect.Value {
func rvSliceIndex(rv reflect.Value, i int, ti *typeInfo) reflect.Value {
return rv.Index(i)
}
// func rvArrayIndex(rv reflect.Value, i int, ti *typeInfo) reflect.Value {
// return rv.Index(i)
// }
func rvArrayIndex(rv reflect.Value, i int, ti *typeInfo) reflect.Value {
return rv.Index(i)
}
func rvSliceZeroCap(t reflect.Type) (v reflect.Value) {
return reflect.MakeSlice(t, 0, 0)
@@ -540,7 +523,7 @@ func rvCapSlice(rv reflect.Value) int {
func rvGetArrayBytes(rv reflect.Value, scratch []byte) (bs []byte) {
l := rv.Len()
if scratch == nil && rv.CanAddr() {
if scratch == nil || rv.CanAddr() {
return rv.Slice(0, l).Bytes()
}
@@ -554,7 +537,7 @@ func rvGetArrayBytes(rv reflect.Value, scratch []byte) (bs []byte) {
}
func rvGetArray4Slice(rv reflect.Value) (v reflect.Value) {
v = rvZeroAddrK(reflect.ArrayOf(rvLenSlice(rv), rv.Type().Elem()), reflect.Array)
v = rvZeroAddrK(reflectArrayOf(rvLenSlice(rv), rv.Type().Elem()), reflect.Array)
reflect.Copy(v, rv)
return
}
@@ -664,43 +647,60 @@ func rvLenMap(rv reflect.Value) int {
return rv.Len()
}
// func copybytes(to, from []byte) int {
// return copy(to, from)
// }
// func copybytestr(to []byte, from string) int {
// return copy(to, from)
// }
// func rvLenArray(rv reflect.Value) int { return rv.Len() }
// ------------ map range and map indexing ----------
func mapSet(m, k, v reflect.Value, _ mapReqParams) {
func mapStoresElemIndirect(elemsize uintptr) bool { return false }
func mapSet(m, k, v reflect.Value, keyFastKind mapKeyFastKind, _, _ bool) {
m.SetMapIndex(k, v)
}
func mapGet(m, k, v reflect.Value, _ mapReqParams) (vv reflect.Value) {
func mapGet(m, k, v reflect.Value, keyFastKind mapKeyFastKind, _, _ bool) (vv reflect.Value) {
return m.MapIndex(k)
}
// func mapDelete(m, k reflect.Value) {
// m.SetMapIndex(k, reflect.Value{})
// }
func mapAddrLoopvarRV(t reflect.Type, k reflect.Kind) (r reflect.Value) {
return // reflect.New(t).Elem()
}
// ---------- ENCODER optimized ---------------
func (d *decoderBase) bytes2Str(in []byte, att dBytesAttachState) (s string, mutable bool) {
return d.detach2Str(in, att), false
func (e *Encoder) jsondriver() *jsonEncDriver {
return e.e.(*jsonEncDriver)
}
// ---------- DECODER optimized ---------------
func (d *Decoder) jsondriver() *jsonDecDriver {
return d.d.(*jsonDecDriver)
}
func (d *Decoder) stringZC(v []byte) (s string) {
return d.string(v)
}
func (d *Decoder) mapKeyString(callFnRvk *bool, kstrbs, kstr2bs *[]byte) string {
return d.string(*kstr2bs)
}
// ---------- structFieldInfo optimized ---------------
func (n *structFieldInfoNode) rvField(v reflect.Value) reflect.Value {
func (n *structFieldInfoPathNode) rvField(v reflect.Value) reflect.Value {
return v.Field(int(n.index))
}
// ---------- others ---------------
// --------------------------
type atomicRtidFnSlice struct {
v atomic.Value
}
func (x *atomicRtidFnSlice) load() interface{} {
return x.v.Load()
}
func (x *atomicRtidFnSlice) store(p interface{}) {
x.v.Store(p)
}

View File

@@ -1,15 +1,12 @@
// Copyright (c) 2012-2020 Ugorji Nwoke. All rights reserved.
// Use of this source code is governed by a MIT license found in the LICENSE file.
//go:build !safe && !codec.safe && !appengine && go1.21
//go:build !safe && !codec.safe && !appengine && go1.9
// +build !safe,!codec.safe,!appengine,go1.9
// minimum of go 1.21 is needed, as that is the minimum for all features and linked functions we need
// - typedmemclr : go1.8
// - mapassign_fastXXX: go1.9
// - clear was added in go1.21
// - unsafe.String(Data): go1.20
// - unsafe.Add: go1.17
// - generics/any: go1.18
// minimum of go 1.9 is needed, as that is the minimum for all features and linked functions we need
// - typedmemclr was introduced in go 1.8
// - mapassign_fastXXX was introduced in go 1.9
// etc
package codec
@@ -24,7 +21,7 @@ import (
// This file has unsafe variants of some helper functions.
// MARKER: See helper_unsafe.go for the usage documentation.
//
// There are a number of helper_*unsafe*.go files.
//
// - helper_unsafe
@@ -44,32 +41,19 @@ import (
// As of March 2021, we cannot differentiate whether running with gccgo or gollvm
// using a build constraint, as both satisfy 'gccgo' build tag.
// Consequently, we must use the lowest common denominator to support both.
//
// For reflect.Value code, we decided to do the following:
// - if we know the kind, we can elide conditional checks for
// - SetXXX (Int, Uint, String, Bool, etc)
// - SetLen
//
// We can also optimize many others, incl IsNil, etc
//
// We can also optimize
// - IsNil
// MARKER: Some functions here will not be hit during code coverage runs due to optimizations, e.g.
// - rvCopySlice: called by decode if rvGrowSlice did not set new slice into pointer to orig slice.
// however, helper_unsafe sets it, so no need to call rvCopySlice later
// - rvSlice: same as above
//
// MARKER: Handling flagIndir ----
//
// flagIndir means that the reflect.Value holds a pointer to the data itself.
//
// flagIndir can be set for:
// - references
// Here, type.IfaceIndir() --> false
// flagIndir is usually false (except when the value is addressable, where in flagIndir may be true)
// - everything else (numbers, bools, string, slice, struct, etc).
// Here, type.IfaceIndir() --> true
// flagIndir is always true
//
// This knowlege is used across this file, e.g. in rv2i and rvRefPtr
const safeMode = false
@@ -104,9 +88,7 @@ const (
const transientSizeMax = 64
// should struct/array support internal strings and slices?
// const transientValueHasStringSlice = false
func isTransientType4Size(size uint32) bool { return size <= transientSizeMax }
const transientValueHasStringSlice = false
type unsafeString struct {
Data unsafe.Pointer
@@ -162,8 +144,7 @@ func (x *unsafePerTypeElem) addrFor(k reflect.Kind) unsafe.Pointer {
x.slice = unsafeSlice{} // memclr
return unsafe.Pointer(&x.slice)
}
clear(x.arr[:])
// x.arr = [transientSizeMax]byte{} // memclr
x.arr = [transientSizeMax]byte{} // memclr
return unsafe.Pointer(&x.arr)
}
@@ -171,7 +152,9 @@ type perType struct {
elems [2]unsafePerTypeElem
}
type decPerType = perType
type decPerType struct {
perType
}
type encPerType struct{}
@@ -200,6 +183,19 @@ func byteAt(b []byte, index uint) byte {
return *(*byte)(unsafe.Pointer(uintptr((*unsafeSlice)(unsafe.Pointer(&b)).Data) + uintptr(index)))
}
func byteSliceOf(b []byte, start, end uint) []byte {
s := (*unsafeSlice)(unsafe.Pointer(&b))
s.Data = unsafe.Pointer(uintptr(s.Data) + uintptr(start))
s.Len = int(end - start)
s.Cap -= int(start)
return b
}
// func byteSliceWithLen(b []byte, length uint) []byte {
// (*unsafeSlice)(unsafe.Pointer(&b)).Len = int(length)
// return b
// }
func setByteAt(b []byte, index uint, val byte) {
// b[index] = val
*(*byte)(unsafe.Pointer(uintptr((*unsafeSlice)(unsafe.Pointer(&b)).Data) + uintptr(index))) = val
@@ -226,26 +222,49 @@ func byteSliceSameData(v1 []byte, v2 []byte) bool {
return (*unsafeSlice)(unsafe.Pointer(&v1)).Data == (*unsafeSlice)(unsafe.Pointer(&v2)).Data
}
// isNil checks - without much effort - if an interface is nil.
//
// returned rv is not guaranteed to be valid (e.g. if v == nil).
//
// Note that this will handle all pointer-sized types e.g.
// pointer, map, chan, func, etc.
func isNil(v interface{}, checkPtr bool) (rv reflect.Value, b bool) {
b = ((*unsafeIntf)(unsafe.Pointer(&v))).ptr == nil
// MARKER: okBytesN functions will copy N bytes into the top slots of the return array.
// These functions expect that the bound check already occured and are are valid.
// copy(...) does a number of checks which are unnecessary in this situation when in bounds.
func okBytes2(b []byte) [2]byte {
return *((*[2]byte)(((*unsafeSlice)(unsafe.Pointer(&b))).Data))
}
func okBytes3(b []byte) [3]byte {
return *((*[3]byte)(((*unsafeSlice)(unsafe.Pointer(&b))).Data))
}
func okBytes4(b []byte) [4]byte {
return *((*[4]byte)(((*unsafeSlice)(unsafe.Pointer(&b))).Data))
}
func okBytes8(b []byte) [8]byte {
return *((*[8]byte)(((*unsafeSlice)(unsafe.Pointer(&b))).Data))
}
// isNil says whether the value v is nil.
// This applies to references like map/ptr/unsafepointer/chan/func,
// and non-reference values like interface/slice.
func isNil(v interface{}) (rv reflect.Value, isnil bool) {
var ui = (*unsafeIntf)(unsafe.Pointer(&v))
isnil = ui.ptr == nil
if !isnil {
rv, isnil = unsafeIsNilIntfOrSlice(ui, v)
}
return
}
func ptrToLowLevel[T any](ptr *T) unsafe.Pointer {
return unsafe.Pointer(ptr)
func unsafeIsNilIntfOrSlice(ui *unsafeIntf, v interface{}) (rv reflect.Value, isnil bool) {
rv = reflect.ValueOf(v) // reflect.ValueOf is currently not inline'able - so call it directly
tk := rv.Kind()
isnil = (tk == reflect.Interface || tk == reflect.Slice) && *(*unsafe.Pointer)(ui.ptr) == nil
return
}
func lowLevelToPtr[T any](v unsafe.Pointer) *T {
return (*T)(v)
}
// Given that v is a reference (map/func/chan/ptr/unsafepointer) kind, return the pointer
// return the pointer for a reference (map/chan/func/pointer/unsafe.Pointer).
// true references (map, func, chan, ptr - NOT slice) may be double-referenced? as flagIndir
//
// Assumes that v is a reference (map/func/chan/ptr/func)
func rvRefPtr(v *unsafeReflectValue) unsafe.Pointer {
if v.flag&unsafeFlagIndir != 0 {
return *(*unsafe.Pointer)(v.ptr)
@@ -276,6 +295,13 @@ func rv4istr(i interface{}) (v reflect.Value) {
}
func rv2i(rv reflect.Value) (i interface{}) {
// We tap into implememtation details from
// the source go stdlib reflect/value.go, and trims the implementation.
//
// e.g.
// - a map/ptr is a reference, thus flagIndir is not set on it
// - an int/slice is not a reference, thus flagIndir is set on it
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
if refBitset.isset(byte(rv.Kind())) && urv.flag&unsafeFlagIndir != 0 {
urv.ptr = *(*unsafe.Pointer)(urv.ptr)
@@ -290,22 +316,12 @@ func rvAddr(rv reflect.Value, ptrType reflect.Type) reflect.Value {
return rv
}
// return true if this rv - got from a pointer kind - is nil.
// For now, only use for struct fields of pointer types, as we're guaranteed
// that flagIndir will never be set.
func rvPtrIsNil(rv reflect.Value) bool {
return rvIsNil(rv)
}
// checks if a nil'able value is nil
func rvIsNil(rv reflect.Value) bool {
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
if urv.flag&unsafeFlagIndir == 0 {
return urv.ptr == nil
if urv.flag&unsafeFlagIndir != 0 {
return *(*unsafe.Pointer)(urv.ptr) == nil
}
// flagIndir is set for a reference (ptr/map/func/unsafepointer/chan)
// OR kind is slice/interface
return *(*unsafe.Pointer)(urv.ptr) == nil
return urv.ptr == nil
}
func rvSetSliceLen(rv reflect.Value, length int) {
@@ -483,62 +499,29 @@ func isEmptyValueFallbackRecur(urv *unsafeReflectValue, v reflect.Value, tinfos
return false
}
// is this an empty interface/ptr/struct/map/slice/chan/array
func isEmptyContainerValue(v reflect.Value, tinfos *TypeInfos, recursive bool) bool {
urv := (*unsafeReflectValue)(unsafe.Pointer(&v))
switch v.Kind() {
case reflect.Slice:
return (*unsafeSlice)(urv.ptr).Len == 0
case reflect.Struct:
if tinfos == nil {
tinfos = defTypeInfos
}
ti := tinfos.find(uintptr(urv.typ))
if ti == nil {
ti = tinfos.load(v.Type())
}
return unsafeCmpZero(urv.ptr, int(ti.size))
case reflect.Interface, reflect.Ptr:
// isnil := urv.ptr == nil // (not sufficient, as a pointer value encodes the type)
isnil := urv.ptr == nil || *(*unsafe.Pointer)(urv.ptr) == nil
if recursive && !isnil {
return isEmptyValue(v.Elem(), tinfos, recursive)
}
return isnil
case reflect.Chan:
return urv.ptr == nil || len_chan(rvRefPtr(urv)) == 0
case reflect.Map:
return urv.ptr == nil || len_map(rvRefPtr(urv)) == 0
case reflect.Array:
return v.Len() == 0 ||
urv.ptr == nil ||
urv.typ == nil ||
rtsize2(urv.typ) == 0 ||
unsafeCmpZero(urv.ptr, int(rtsize2(urv.typ)))
}
return false
}
// --------------------------
type structFieldInfos struct {
c unsafe.Pointer // source
s unsafe.Pointer // sorted
t uint8To32TrieNode
c unsafe.Pointer // source
s unsafe.Pointer // sorted
length int
// byName map[string]*structFieldInfo // find sfi given a name
}
// func (x *structFieldInfos) load(source, sorted []*structFieldInfo, sourceNames, sortedNames []string) {
func (x *structFieldInfos) load(source, sorted []*structFieldInfo) {
var s *unsafeSlice
s := (*unsafeSlice)(unsafe.Pointer(&sorted))
x.s = s.Data
x.length = s.Len
s = (*unsafeSlice)(unsafe.Pointer(&source))
x.c = s.Data
x.length = s.Len
s = (*unsafeSlice)(unsafe.Pointer(&sorted))
x.s = s.Data
}
func (x *structFieldInfos) sorted() (v []*structFieldInfo) {
*(*unsafeSlice)(unsafe.Pointer(&v)) = unsafeSlice{x.s, x.length, x.length}
// s := (*unsafeSlice)(unsafe.Pointer(&v))
// s.Data = x.sorted0
// s.Len = x.length
// s.Cap = s.Len
return
}
func (x *structFieldInfos) source() (v []*structFieldInfo) {
@@ -546,48 +529,66 @@ func (x *structFieldInfos) source() (v []*structFieldInfo) {
return
}
func (x *structFieldInfos) sorted() (v []*structFieldInfo) {
*(*unsafeSlice)(unsafe.Pointer(&v)) = unsafeSlice{x.s, x.length, x.length}
return
}
// --------------------------
type uint8To32TrieNodeNoKids struct {
key uint8
valid bool // the value marks the end of a full stored string
numkids uint8
_ byte // padding
value uint32
}
type uint8To32TrieNodeKids = *uint8To32TrieNode
func (x *uint8To32TrieNode) setKids(kids []uint8To32TrieNode) {
x.numkids = uint8(len(kids))
x.kids = &kids[0]
}
func (x *uint8To32TrieNode) getKids() (v []uint8To32TrieNode) {
*(*unsafeSlice)(unsafe.Pointer(&v)) = unsafeSlice{unsafe.Pointer(x.kids), int(x.numkids), int(x.numkids)}
return
}
func (x *uint8To32TrieNode) truncKids() { x.numkids = 0 }
// --------------------------
// atomicXXX is expected to be 2 words (for symmetry with atomic.Value)
//
// Note that we do not atomically load/store length and data pointer separately,
// as this could lead to some races. Instead, we atomically load/store cappedSlice.
//
// Note: with atomic.(Load|Store)Pointer, we MUST work with an unsafe.Pointer directly.
// ----------------------
type atomicTypeInfoSlice struct {
v unsafe.Pointer // *[]rtid2ti
}
func (x *atomicTypeInfoSlice) load() (s []rtid2ti) {
x2 := atomic.LoadPointer(&x.v)
if x2 != nil {
s = *(*[]rtid2ti)(x2)
}
return
}
func (x *atomicTypeInfoSlice) store(p []rtid2ti) {
atomic.StorePointer(&x.v, unsafe.Pointer(&p))
}
// MARKER: in safe mode, atomicXXX are atomic.Value, which contains an interface{}.
// This is 2 words.
// consider padding atomicXXX here with a uintptr, so they fit into 2 words also.
// --------------------------
type atomicRtidFnSlice struct {
v unsafe.Pointer // *[]codecRtidFn
}
func (x *atomicRtidFnSlice) load() (s unsafe.Pointer) {
return atomic.LoadPointer(&x.v)
func (x *atomicRtidFnSlice) load() (s []codecRtidFn) {
x2 := atomic.LoadPointer(&x.v)
if x2 != nil {
s = *(*[]codecRtidFn)(x2)
}
return
}
func (x *atomicRtidFnSlice) store(p unsafe.Pointer) {
atomic.StorePointer(&x.v, p)
func (x *atomicRtidFnSlice) store(p []codecRtidFn) {
atomic.StorePointer(&x.v, unsafe.Pointer(&p))
}
// --------------------------
type atomicClsErr struct {
v unsafe.Pointer // *clsErr
}
func (x *atomicClsErr) load() (e clsErr) {
x2 := (*clsErr)(atomic.LoadPointer(&x.v))
if x2 != nil {
e = *x2
}
return
}
func (x *atomicClsErr) store(p clsErr) {
atomic.StorePointer(&x.v, unsafe.Pointer(&p))
}
// --------------------------
@@ -659,79 +660,98 @@ func (n *fauxUnion) rb() (v reflect.Value) {
// --------------------------
func rvSetBytes(rv reflect.Value, v []byte) {
*(*[]byte)(rvPtr(rv)) = v
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
*(*[]byte)(urv.ptr) = v
}
func rvSetString(rv reflect.Value, v string) {
*(*string)(rvPtr(rv)) = v
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
*(*string)(urv.ptr) = v
}
func rvSetBool(rv reflect.Value, v bool) {
*(*bool)(rvPtr(rv)) = v
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
*(*bool)(urv.ptr) = v
}
func rvSetTime(rv reflect.Value, v time.Time) {
*(*time.Time)(rvPtr(rv)) = v
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
*(*time.Time)(urv.ptr) = v
}
func rvSetFloat32(rv reflect.Value, v float32) {
*(*float32)(rvPtr(rv)) = v
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
*(*float32)(urv.ptr) = v
}
func rvSetFloat64(rv reflect.Value, v float64) {
*(*float64)(rvPtr(rv)) = v
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
*(*float64)(urv.ptr) = v
}
func rvSetComplex64(rv reflect.Value, v complex64) {
*(*complex64)(rvPtr(rv)) = v
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
*(*complex64)(urv.ptr) = v
}
func rvSetComplex128(rv reflect.Value, v complex128) {
*(*complex128)(rvPtr(rv)) = v
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
*(*complex128)(urv.ptr) = v
}
func rvSetInt(rv reflect.Value, v int) {
*(*int)(rvPtr(rv)) = v
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
*(*int)(urv.ptr) = v
}
func rvSetInt8(rv reflect.Value, v int8) {
*(*int8)(rvPtr(rv)) = v
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
*(*int8)(urv.ptr) = v
}
func rvSetInt16(rv reflect.Value, v int16) {
*(*int16)(rvPtr(rv)) = v
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
*(*int16)(urv.ptr) = v
}
func rvSetInt32(rv reflect.Value, v int32) {
*(*int32)(rvPtr(rv)) = v
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
*(*int32)(urv.ptr) = v
}
func rvSetInt64(rv reflect.Value, v int64) {
*(*int64)(rvPtr(rv)) = v
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
*(*int64)(urv.ptr) = v
}
func rvSetUint(rv reflect.Value, v uint) {
*(*uint)(rvPtr(rv)) = v
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
*(*uint)(urv.ptr) = v
}
func rvSetUintptr(rv reflect.Value, v uintptr) {
*(*uintptr)(rvPtr(rv)) = v
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
*(*uintptr)(urv.ptr) = v
}
func rvSetUint8(rv reflect.Value, v uint8) {
*(*uint8)(rvPtr(rv)) = v
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
*(*uint8)(urv.ptr) = v
}
func rvSetUint16(rv reflect.Value, v uint16) {
*(*uint16)(rvPtr(rv)) = v
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
*(*uint16)(urv.ptr) = v
}
func rvSetUint32(rv reflect.Value, v uint32) {
*(*uint32)(rvPtr(rv)) = v
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
*(*uint32)(urv.ptr) = v
}
func rvSetUint64(rv reflect.Value, v uint64) {
*(*uint64)(rvPtr(rv)) = v
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
*(*uint64)(urv.ptr) = v
}
// ----------------
@@ -755,10 +775,12 @@ func rvSetDirect(rv reflect.Value, v reflect.Value) {
uv := (*unsafeReflectValue)(unsafe.Pointer(&v))
if uv.flag&unsafeFlagIndir == 0 {
*(*unsafe.Pointer)(urv.ptr) = uv.ptr
} else if uv.ptr != unsafeZeroAddr {
} else if uv.ptr == unsafeZeroAddr {
if urv.ptr != unsafeZeroAddr {
typedmemclr(urv.typ, urv.ptr)
}
} else {
typedmemmove(urv.typ, urv.ptr, uv.ptr)
} else if urv.ptr != unsafeZeroAddr {
typedmemclr(urv.typ, urv.ptr)
}
}
@@ -790,9 +812,11 @@ func rvMakeSlice(rv reflect.Value, ti *typeInfo, xlen, xcap int) (_ reflect.Valu
// It is typically called when we know that SetLen(...) cannot be done.
func rvSlice(rv reflect.Value, length int) reflect.Value {
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
ux := *(*unsafeSlice)(urv.ptr) // copy slice header
var x []struct{}
ux := (*unsafeSlice)(unsafe.Pointer(&x))
*ux = *(*unsafeSlice)(urv.ptr)
ux.Len = length
urv.ptr = unsafe.Pointer(&ux)
urv.ptr = unsafe.Pointer(ux)
return rv
}
@@ -810,16 +834,10 @@ func rvGrowSlice(rv reflect.Value, ti *typeInfo, cap, incr int) (v reflect.Value
// ------------
func rvArrayIndex(rv reflect.Value, i int, ti *typeInfo, isSlice bool) (v reflect.Value) {
func rvSliceIndex(rv reflect.Value, i int, ti *typeInfo) (v reflect.Value) {
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
uv := (*unsafeReflectValue)(unsafe.Pointer(&v))
if isSlice {
uv.ptr = unsafe.Pointer(uintptr(((*unsafeSlice)(urv.ptr)).Data))
} else {
uv.ptr = unsafe.Pointer(uintptr(urv.ptr))
}
uv.ptr = unsafe.Add(uv.ptr, ti.elemsize*uint32(i))
// uv.ptr = unsafe.Pointer(ptr + uintptr(int(ti.elemsize)*i))
uv.ptr = unsafe.Pointer(uintptr(((*unsafeSlice)(urv.ptr)).Data) + uintptr(int(ti.elemsize)*i))
uv.typ = ((*unsafeIntf)(unsafe.Pointer(&ti.elem))).ptr
uv.flag = uintptr(ti.elemkind) | unsafeFlagIndir | unsafeFlagAddr
return
@@ -843,11 +861,19 @@ func rvCapSlice(rv reflect.Value) int {
return (*unsafeSlice)(urv.ptr).Cap
}
func rvArrayIndex(rv reflect.Value, i int, ti *typeInfo) (v reflect.Value) {
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
uv := (*unsafeReflectValue)(unsafe.Pointer(&v))
uv.ptr = unsafe.Pointer(uintptr(urv.ptr) + uintptr(int(ti.elemsize)*i))
uv.typ = ((*unsafeIntf)(unsafe.Pointer(&ti.elem))).ptr
uv.flag = uintptr(ti.elemkind) | unsafeFlagIndir | unsafeFlagAddr
return
}
// if scratch is nil, then return a writable view (assuming canAddr=true)
func rvGetArrayBytes(rv reflect.Value, _ []byte) (bs []byte) {
func rvGetArrayBytes(rv reflect.Value, scratch []byte) (bs []byte) {
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
bx := (*unsafeSlice)(unsafe.Pointer(&bs))
// bx.Data, bx.Len, bx.Cap = urv.ptr, rv.Len(), bx.Len
bx.Data = urv.ptr
bx.Len = rv.Len()
bx.Cap = bx.Len
@@ -863,7 +889,7 @@ func rvGetArray4Slice(rv reflect.Value) (v reflect.Value) {
//
// Consequently, we use rvLenSlice, not rvCapSlice.
t := reflect.ArrayOf(rvLenSlice(rv), rv.Type().Elem())
t := reflectArrayOf(rvLenSlice(rv), rv.Type().Elem())
// v = rvZeroAddrK(t, reflect.Array)
uv := (*unsafeReflectValue)(unsafe.Pointer(&v))
@@ -895,84 +921,99 @@ func rvCopySlice(dest, src reflect.Value, elemType reflect.Type) {
// ------------
func rvPtr(rv reflect.Value) unsafe.Pointer {
return (*unsafeReflectValue)(unsafe.Pointer(&rv)).ptr
}
func rvGetBool(rv reflect.Value) bool {
return *(*bool)(rvPtr(rv))
v := (*unsafeReflectValue)(unsafe.Pointer(&rv))
return *(*bool)(v.ptr)
}
func rvGetBytes(rv reflect.Value) []byte {
return *(*[]byte)(rvPtr(rv))
v := (*unsafeReflectValue)(unsafe.Pointer(&rv))
return *(*[]byte)(v.ptr)
}
func rvGetTime(rv reflect.Value) time.Time {
return *(*time.Time)(rvPtr(rv))
v := (*unsafeReflectValue)(unsafe.Pointer(&rv))
return *(*time.Time)(v.ptr)
}
func rvGetString(rv reflect.Value) string {
return *(*string)(rvPtr(rv))
v := (*unsafeReflectValue)(unsafe.Pointer(&rv))
return *(*string)(v.ptr)
}
func rvGetFloat64(rv reflect.Value) float64 {
return *(*float64)(rvPtr(rv))
v := (*unsafeReflectValue)(unsafe.Pointer(&rv))
return *(*float64)(v.ptr)
}
func rvGetFloat32(rv reflect.Value) float32 {
return *(*float32)(rvPtr(rv))
v := (*unsafeReflectValue)(unsafe.Pointer(&rv))
return *(*float32)(v.ptr)
}
func rvGetComplex64(rv reflect.Value) complex64 {
return *(*complex64)(rvPtr(rv))
v := (*unsafeReflectValue)(unsafe.Pointer(&rv))
return *(*complex64)(v.ptr)
}
func rvGetComplex128(rv reflect.Value) complex128 {
return *(*complex128)(rvPtr(rv))
v := (*unsafeReflectValue)(unsafe.Pointer(&rv))
return *(*complex128)(v.ptr)
}
func rvGetInt(rv reflect.Value) int {
return *(*int)(rvPtr(rv))
v := (*unsafeReflectValue)(unsafe.Pointer(&rv))
return *(*int)(v.ptr)
}
func rvGetInt8(rv reflect.Value) int8 {
return *(*int8)(rvPtr(rv))
v := (*unsafeReflectValue)(unsafe.Pointer(&rv))
return *(*int8)(v.ptr)
}
func rvGetInt16(rv reflect.Value) int16 {
return *(*int16)(rvPtr(rv))
v := (*unsafeReflectValue)(unsafe.Pointer(&rv))
return *(*int16)(v.ptr)
}
func rvGetInt32(rv reflect.Value) int32 {
return *(*int32)(rvPtr(rv))
v := (*unsafeReflectValue)(unsafe.Pointer(&rv))
return *(*int32)(v.ptr)
}
func rvGetInt64(rv reflect.Value) int64 {
return *(*int64)(rvPtr(rv))
v := (*unsafeReflectValue)(unsafe.Pointer(&rv))
return *(*int64)(v.ptr)
}
func rvGetUint(rv reflect.Value) uint {
return *(*uint)(rvPtr(rv))
v := (*unsafeReflectValue)(unsafe.Pointer(&rv))
return *(*uint)(v.ptr)
}
func rvGetUint8(rv reflect.Value) uint8 {
return *(*uint8)(rvPtr(rv))
v := (*unsafeReflectValue)(unsafe.Pointer(&rv))
return *(*uint8)(v.ptr)
}
func rvGetUint16(rv reflect.Value) uint16 {
return *(*uint16)(rvPtr(rv))
v := (*unsafeReflectValue)(unsafe.Pointer(&rv))
return *(*uint16)(v.ptr)
}
func rvGetUint32(rv reflect.Value) uint32 {
return *(*uint32)(rvPtr(rv))
v := (*unsafeReflectValue)(unsafe.Pointer(&rv))
return *(*uint32)(v.ptr)
}
func rvGetUint64(rv reflect.Value) uint64 {
return *(*uint64)(rvPtr(rv))
v := (*unsafeReflectValue)(unsafe.Pointer(&rv))
return *(*uint64)(v.ptr)
}
func rvGetUintptr(rv reflect.Value) uintptr {
return *(*uintptr)(rvPtr(rv))
v := (*unsafeReflectValue)(unsafe.Pointer(&rv))
return *(*uintptr)(v.ptr)
}
func rvLenMap(rv reflect.Value) int {
@@ -986,6 +1027,32 @@ func rvLenMap(rv reflect.Value) int {
return len_map(rvRefPtr((*unsafeReflectValue)(unsafe.Pointer(&rv))))
}
// copy is an intrinsic, which may use asm if length is small,
// or make a runtime call to runtime.memmove if length is large.
// Performance suffers when you always call runtime.memmove function.
//
// Consequently, there's no value in a copybytes call - just call copy() directly
// func copybytes(to, from []byte) (n int) {
// n = (*unsafeSlice)(unsafe.Pointer(&from)).Len
// memmove(
// (*unsafeSlice)(unsafe.Pointer(&to)).Data,
// (*unsafeSlice)(unsafe.Pointer(&from)).Data,
// uintptr(n),
// )
// return
// }
// func copybytestr(to []byte, from string) (n int) {
// n = (*unsafeSlice)(unsafe.Pointer(&from)).Len
// memmove(
// (*unsafeSlice)(unsafe.Pointer(&to)).Data,
// (*unsafeSlice)(unsafe.Pointer(&from)).Data,
// uintptr(n),
// )
// return
// }
// Note: it is hard to find len(...) of an array type,
// as that is a field in the arrayType representing the array, and hard to introspect.
//
@@ -998,26 +1065,24 @@ func rvLenMap(rv reflect.Value) int {
//
// It is more performant to provide a value that the map entry is set into,
// and that elides the allocation.
// go 1.4+ has runtime/hashmap.go or runtime/map.go which has a
// hIter struct with the first 2 values being key and value
// of the current iteration.
//
// go 1.4 through go 1.23 (in runtime/hashmap.go or runtime/map.go) has a hIter struct
// with the first 2 values being pointers for key and value of the current iteration.
// The next 6 values are pointers, followed by numeric types (uintptr, uint8, bool, etc).
// This *hIter is passed to mapiterinit, mapiternext, mapiterkey, mapiterelem.
// We bypass the reflect wrapper functions and just use the *hIter directly.
//
// In go 1.24, swissmap was introduced, and it provides a compatibility layer
// for hIter (called linknameIter). This has only 2 pointer fields after the key and value pointers.
// Though *hIter has many fields, we only care about the first 2.
//
// Note: We bypass the reflect wrapper functions and just use the *hIter directly.
// We directly embed this in unsafeMapIter below
//
// When 'faking' these types with our own, we MUST ensure that the GC sees the pointers
// appropriately. These are reflected in goversion_(no)swissmap_unsafe.go files.
// In these files, we pad the extra spaces appropriately.
//
// Note: the faux hIter/linknameIter is directly embedded in unsafeMapIter below
// hiter is typically about 12 words, but we just fill up unsafeMapIter to 32 words,
// so it fills multiple cache lines and can give some extra space to accomodate small growth.
type unsafeMapIter struct {
mtyp, mptr unsafe.Pointer
k, v unsafeReflectValue
k, v reflect.Value
kisref bool
visref bool
mapvalues bool
@@ -1027,7 +1092,7 @@ type unsafeMapIter struct {
it struct {
key unsafe.Pointer
value unsafe.Pointer
_ unsafeMapIterPadding
_ [20]uintptr // padding for other fields (to make up 32 words for enclosing struct)
}
}
@@ -1047,16 +1112,18 @@ func (t *unsafeMapIter) Next() (r bool) {
}
if helperUnsafeDirectAssignMapEntry || t.kisref {
t.k.ptr = t.it.key
(*unsafeReflectValue)(unsafe.Pointer(&t.k)).ptr = t.it.key
} else {
typedmemmove(t.k.typ, t.k.ptr, t.it.key)
k := (*unsafeReflectValue)(unsafe.Pointer(&t.k))
typedmemmove(k.typ, k.ptr, t.it.key)
}
if t.mapvalues {
if helperUnsafeDirectAssignMapEntry || t.visref {
t.v.ptr = t.it.value
(*unsafeReflectValue)(unsafe.Pointer(&t.v)).ptr = t.it.value
} else {
typedmemmove(t.v.typ, t.v.ptr, t.it.value)
v := (*unsafeReflectValue)(unsafe.Pointer(&t.v))
typedmemmove(v.typ, v.ptr, t.it.value)
}
}
@@ -1064,11 +1131,11 @@ func (t *unsafeMapIter) Next() (r bool) {
}
func (t *unsafeMapIter) Key() (r reflect.Value) {
return *(*reflect.Value)(unsafe.Pointer(&t.k))
return t.k
}
func (t *unsafeMapIter) Value() (r reflect.Value) {
return *(*reflect.Value)(unsafe.Pointer(&t.v))
return t.v
}
func (t *unsafeMapIter) Done() {}
@@ -1095,14 +1162,14 @@ func mapRange(t *mapIter, m, k, v reflect.Value, mapvalues bool) {
// t.it = (*unsafeMapHashIter)(reflect_mapiterinit(t.mtyp, t.mptr))
mapiterinit(t.mtyp, t.mptr, unsafe.Pointer(&t.it))
t.k = *(*unsafeReflectValue)(unsafe.Pointer(&k))
t.k = k
t.kisref = refBitset.isset(byte(k.Kind()))
if mapvalues {
t.v = *(*unsafeReflectValue)(unsafe.Pointer(&v))
t.v = v
t.visref = refBitset.isset(byte(v.Kind()))
} else {
t.v = unsafeReflectValue{}
t.v = reflect.Value{}
}
}
@@ -1115,6 +1182,13 @@ func unsafeMapKVPtr(urv *unsafeReflectValue) unsafe.Pointer {
return urv.ptr
}
// func mapDelete(m, k reflect.Value) {
// var urv = (*unsafeReflectValue)(unsafe.Pointer(&k))
// var kptr = unsafeMapKVPtr(urv)
// urv = (*unsafeReflectValue)(unsafe.Pointer(&m))
// mapdelete(urv.typ, rv2ptr(urv), kptr)
// }
// return an addressable reflect value that can be used in mapRange and mapGet operations.
//
// all calls to mapGet or mapRange will call here to get an addressable reflect.Value.
@@ -1131,39 +1205,53 @@ func mapAddrLoopvarRV(t reflect.Type, k reflect.Kind) (rv reflect.Value) {
return
}
func makeMapReflect(typ reflect.Type, size int) (rv reflect.Value) {
t := (*unsafeIntf)(unsafe.Pointer(&typ)).ptr
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
urv.typ = t
urv.flag = uintptr(reflect.Map)
urv.ptr = makemap(t, size, nil)
return
// ---------- ENCODER optimized ---------------
func (e *Encoder) jsondriver() *jsonEncDriver {
return (*jsonEncDriver)((*unsafeIntf)(unsafe.Pointer(&e.e)).ptr)
}
func (d *decoderBase) bytes2Str(in []byte, state dBytesAttachState) (s string, mutable bool) {
return stringView(in), state <= dBytesAttachBuffer
func (d *Decoder) zerocopystate() bool {
return d.decByteState == decByteStateZerocopy && d.h.ZeroCopy
}
func (d *Decoder) stringZC(v []byte) (s string) {
// MARKER: inline zerocopystate directly so genHelper forwarding function fits within inlining cost
// if d.zerocopystate() {
if d.decByteState == decByteStateZerocopy && d.h.ZeroCopy {
return stringView(v)
}
return d.string(v)
}
func (d *Decoder) mapKeyString(callFnRvk *bool, kstrbs, kstr2bs *[]byte) string {
if !d.zerocopystate() {
*callFnRvk = true
if d.decByteState == decByteStateReuseBuf {
*kstrbs = append((*kstrbs)[:0], (*kstr2bs)...)
*kstr2bs = *kstrbs
}
}
return stringView(*kstr2bs)
}
// ---------- DECODER optimized ---------------
func (d *Decoder) jsondriver() *jsonDecDriver {
return (*jsonDecDriver)((*unsafeIntf)(unsafe.Pointer(&d.d)).ptr)
}
// ---------- structFieldInfo optimized ---------------
func (n *structFieldInfoNode) rvField(v reflect.Value) (rv reflect.Value) {
func (n *structFieldInfoPathNode) rvField(v reflect.Value) (rv reflect.Value) {
// we already know this is exported, and maybe embedded (based on what si says)
uv := (*unsafeReflectValue)(unsafe.Pointer(&v))
urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
// clear flagEmbedRO if necessary, and inherit permission bits from v
urv.flag = uv.flag&(unsafeFlagStickyRO|unsafeFlagIndir|unsafeFlagAddr) | uintptr(n.kind)
urv.typ = ((*unsafeIntf)(unsafe.Pointer(&n.typ))).ptr
urv.ptr = unsafe.Pointer(uintptr(uv.ptr) + uintptr(n.offset))
// *(*unsafeReflectValue)(unsafe.Pointer(&rv)) = unsafeReflectValue{
// unsafeIntf: unsafeIntf{
// typ: ((*unsafeIntf)(unsafe.Pointer(&n.typ))).ptr,
// ptr: unsafe.Pointer(uintptr(uv.ptr) + uintptr(n.offset)),
// },
// flag: uv.flag&(unsafeFlagStickyRO|unsafeFlagIndir|unsafeFlagAddr) | uintptr(n.kind),
// }
return
}
@@ -1211,6 +1299,10 @@ func unsafeNew(typ unsafe.Pointer) unsafe.Pointer {
// failing with "error: undefined reference" error.
// however, runtime.{mallocgc, newarray} are supported, so use that instead.
//go:linkname memmove runtime.memmove
//go:noescape
func memmove(to, from unsafe.Pointer, n uintptr)
//go:linkname mallocgc runtime.mallocgc
//go:noescape
func mallocgc(size uintptr, typ unsafe.Pointer, needzero bool) unsafe.Pointer
@@ -1227,6 +1319,10 @@ func mapiterinit(typ unsafe.Pointer, m unsafe.Pointer, it unsafe.Pointer)
//go:noescape
func mapiternext(it unsafe.Pointer) (key unsafe.Pointer)
//go:linkname mapdelete runtime.mapdelete
//go:noescape
func mapdelete(typ unsafe.Pointer, m unsafe.Pointer, key unsafe.Pointer)
//go:linkname mapassign runtime.mapassign
//go:noescape
func mapassign(typ unsafe.Pointer, m unsafe.Pointer, key unsafe.Pointer) unsafe.Pointer
@@ -1235,10 +1331,6 @@ func mapassign(typ unsafe.Pointer, m unsafe.Pointer, key unsafe.Pointer) unsafe.
//go:noescape
func mapaccess2(typ unsafe.Pointer, m unsafe.Pointer, key unsafe.Pointer) (val unsafe.Pointer, ok bool)
//go:linkname makemap runtime.makemap
//go:noescape
func makemap(typ unsafe.Pointer, size int, h unsafe.Pointer) unsafe.Pointer
// reflect.typed{memmove, memclr, slicecopy} will handle checking if the type has pointers or not,
// and if a writeBarrier is needed, before delegating to the right method in the runtime.
//

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT license found in the LICENSE file.
//go:build !safe && !codec.safe && !appengine && go1.9 && gc
// +build !safe,!codec.safe,!appengine,go1.9,gc
package codec
@@ -23,67 +24,8 @@ const (
mapMaxElemSize = 128
)
type mapKeyFastKind uint8
const (
mapKeyFastKindAny = iota + 1
mapKeyFastKind32
mapKeyFastKind32ptr
mapKeyFastKind64
mapKeyFastKind64ptr
mapKeyFastKindStr
)
var mapKeyFastKindVals [32]mapKeyFastKind
type mapReqParams struct {
kfast mapKeyFastKind
ref bool
indirect bool
}
func getMapReqParams(ti *typeInfo) (r mapReqParams) {
r.indirect = mapStoresElemIndirect(uintptr(ti.elemsize))
r.ref = refBitset.isset(ti.elemkind)
r.kfast = mapKeyFastKindFor(reflect.Kind(ti.keykind))
return
}
func init() {
xx := func(f mapKeyFastKind, k ...reflect.Kind) {
for _, v := range k {
mapKeyFastKindVals[byte(v)&31] = f // 'v % 32' equal to 'v & 31'
}
}
var f mapKeyFastKind
f = mapKeyFastKind64
if wordSizeBits == 32 {
f = mapKeyFastKind32
}
xx(f, reflect.Int, reflect.Uint, reflect.Uintptr)
f = mapKeyFastKind64ptr
if wordSizeBits == 32 {
f = mapKeyFastKind32ptr
}
xx(f, reflect.Ptr)
xx(mapKeyFastKindStr, reflect.String)
xx(mapKeyFastKind32, reflect.Uint32, reflect.Int32, reflect.Float32)
xx(mapKeyFastKind64, reflect.Uint64, reflect.Int64, reflect.Float64)
}
func mapKeyFastKindFor(k reflect.Kind) mapKeyFastKind {
return mapKeyFastKindVals[k&31]
}
func unsafeGrowslice(typ unsafe.Pointer, old unsafeSlice, cap, incr int) (s unsafeSlice) {
// culled from GOROOT/runtime/slice.go
s = rtgrowslice(old.Data, old.Cap+incr, old.Cap, incr, typ)
s.Len = old.Len
return
func unsafeGrowslice(typ unsafe.Pointer, old unsafeSlice, cap, incr int) (v unsafeSlice) {
return growslice(typ, old, cap+incr)
}
// func rvType(rv reflect.Value) reflect.Type {
@@ -101,7 +43,7 @@ func mapStoresElemIndirect(elemsize uintptr) bool {
return elemsize > mapMaxElemSize
}
func mapSet(m, k, v reflect.Value, p mapReqParams) { // valIsRef
func mapSet(m, k, v reflect.Value, keyFastKind mapKeyFastKind, valIsIndirect, valIsRef bool) {
var urv = (*unsafeReflectValue)(unsafe.Pointer(&k))
var kptr = unsafeMapKVPtr(urv)
urv = (*unsafeReflectValue)(unsafe.Pointer(&v))
@@ -118,15 +60,14 @@ func mapSet(m, k, v reflect.Value, p mapReqParams) { // valIsRef
// Sometimes, we got vvptr == nil when we dereferenced vvptr (if valIsIndirect).
// Consequently, only use fastXXX functions if !valIsIndirect
if p.indirect {
if valIsIndirect {
vvptr = mapassign(urv.typ, mptr, kptr)
// typedmemmove(vtyp, vvptr, vptr)
// // reflect_mapassign(urv.typ, mptr, kptr, vptr)
// return
goto END
typedmemmove(vtyp, vvptr, vptr)
// reflect_mapassign(urv.typ, mptr, kptr, vptr)
return
}
switch p.kfast {
switch keyFastKind {
case mapKeyFastKind32:
vvptr = mapassign_fast32(urv.typ, mptr, *(*uint32)(kptr))
case mapKeyFastKind32ptr:
@@ -141,14 +82,14 @@ func mapSet(m, k, v reflect.Value, p mapReqParams) { // valIsRef
vvptr = mapassign(urv.typ, mptr, kptr)
}
// if p.kfast != 0 && valIsIndirect {
// if keyFastKind != 0 && valIsIndirect {
// vvptr = *(*unsafe.Pointer)(vvptr)
// }
END:
typedmemmove(vtyp, vvptr, vptr)
}
func mapGet(m, k, v reflect.Value, p mapReqParams) (_ reflect.Value) {
func mapGet(m, k, v reflect.Value, keyFastKind mapKeyFastKind, valIsIndirect, valIsRef bool) (_ reflect.Value) {
var urv = (*unsafeReflectValue)(unsafe.Pointer(&k))
var kptr = unsafeMapKVPtr(urv)
urv = (*unsafeReflectValue)(unsafe.Pointer(&m))
@@ -160,7 +101,7 @@ func mapGet(m, k, v reflect.Value, p mapReqParams) (_ reflect.Value) {
// Note that mapaccess2_fastXXX functions do not check if the value needs to be copied.
// if they do, we should dereference the pointer and return that
switch p.kfast {
switch keyFastKind {
case mapKeyFastKind32, mapKeyFastKind32ptr:
vvptr, ok = mapaccess2_fast32(urv.typ, mptr, *(*uint32)(kptr))
case mapKeyFastKind64, mapKeyFastKind64ptr:
@@ -177,9 +118,9 @@ func mapGet(m, k, v reflect.Value, p mapReqParams) (_ reflect.Value) {
urv = (*unsafeReflectValue)(unsafe.Pointer(&v))
if p.kfast != 0 && p.indirect {
if keyFastKind != 0 && valIsIndirect {
urv.ptr = *(*unsafe.Pointer)(vvptr)
} else if helperUnsafeDirectAssignMapEntry || p.ref {
} else if helperUnsafeDirectAssignMapEntry || valIsRef {
urv.ptr = vvptr
} else {
typedmemmove(urv.typ, urv.ptr, vvptr)
@@ -188,11 +129,13 @@ func mapGet(m, k, v reflect.Value, p mapReqParams) (_ reflect.Value) {
return v
}
// ----
//go:linkname unsafeZeroArr runtime.zeroVal
var unsafeZeroArr [1024]byte
// //go:linkname rvPtrToType reflect.toType
// //go:noescape
// func rvPtrToType(typ unsafe.Pointer) reflect.Type
//go:linkname mapassign_fast32 runtime.mapassign_fast32
//go:noescape
func mapassign_fast32(typ unsafe.Pointer, m unsafe.Pointer, key uint32) unsafe.Pointer
@@ -224,19 +167,3 @@ func mapaccess2_fast64(typ unsafe.Pointer, m unsafe.Pointer, key uint64) (val un
//go:linkname mapaccess2_faststr runtime.mapaccess2_faststr
//go:noescape
func mapaccess2_faststr(typ unsafe.Pointer, m unsafe.Pointer, key string) (val unsafe.Pointer, ok bool)
//go:linkname rtgrowslice runtime.growslice
//go:noescape
func rtgrowslice(oldPtr unsafe.Pointer, newLen, oldCap, num int, typ unsafe.Pointer) unsafeSlice
// ----
// //go:linkname rvPtrToType reflect.toType
// //go:noescape
// func rvPtrToType(typ unsafe.Pointer) reflect.Type
// //go:linkname growslice reflect.growslice
// //go:noescape
// func growslice(typ unsafe.Pointer, old unsafeSlice, cap int) unsafeSlice
// ----

View File

@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT license found in the LICENSE file.
//go:build !safe && !codec.safe && !appengine && go1.9 && !gc
// +build !safe,!codec.safe,!appengine,go1.9,!gc
package codec
@@ -13,15 +14,6 @@ import (
var unsafeZeroArr [1024]byte
type mapReqParams struct {
ref bool
}
func getMapReqParams(ti *typeInfo) (r mapReqParams) {
r.ref = refBitset.isset(ti.elemkind)
return
}
// runtime.growslice does not work with gccgo, failing with "growslice: cap out of range" error.
// consequently, we just call newarray followed by typedslicecopy directly.
@@ -39,11 +31,18 @@ func unsafeGrowslice(typ unsafe.Pointer, old unsafeSlice, cap, incr int) (v unsa
return
}
// func unsafeNew(t reflect.Type, typ unsafe.Pointer) unsafe.Pointer {
// rv := reflect.New(t)
// return ((*unsafeReflectValue)(unsafe.Pointer(&rv))).ptr
// }
// runtime.{mapassign_fastXXX, mapaccess2_fastXXX} are not supported in gollvm,
// failing with "error: undefined reference" error.
// so we just use runtime.{mapassign, mapaccess2} directly
func mapSet(m, k, v reflect.Value, p mapReqParams) {
func mapStoresElemIndirect(elemsize uintptr) bool { return false }
func mapSet(m, k, v reflect.Value, _ mapKeyFastKind, _, valIsRef bool) {
var urv = (*unsafeReflectValue)(unsafe.Pointer(&k))
var kptr = unsafeMapKVPtr(urv)
urv = (*unsafeReflectValue)(unsafe.Pointer(&v))
@@ -57,7 +56,7 @@ func mapSet(m, k, v reflect.Value, p mapReqParams) {
typedmemmove(vtyp, vvptr, vptr)
}
func mapGet(m, k, v reflect.Value, p mapReqParams) (_ reflect.Value) {
func mapGet(m, k, v reflect.Value, _ mapKeyFastKind, _, valIsRef bool) (_ reflect.Value) {
var urv = (*unsafeReflectValue)(unsafe.Pointer(&k))
var kptr = unsafeMapKVPtr(urv)
urv = (*unsafeReflectValue)(unsafe.Pointer(&m))
@@ -71,7 +70,7 @@ func mapGet(m, k, v reflect.Value, p mapReqParams) (_ reflect.Value) {
urv = (*unsafeReflectValue)(unsafe.Pointer(&v))
if helperUnsafeDirectAssignMapEntry || p.ref {
if helperUnsafeDirectAssignMapEntry || valIsRef {
urv.ptr = vvptr
} else {
typedmemmove(urv.typ, urv.ptr, vvptr)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -4,11 +4,10 @@
package codec
import (
"bufio"
"errors"
"io"
"net"
"net/rpc"
"sync/atomic"
)
var (
@@ -29,44 +28,57 @@ type RPCOptions struct {
// RPCNoBuffer configures whether we attempt to buffer reads and writes during RPC calls.
//
// Set RPCNoBuffer=true to turn buffering off.
//
// Buffering can still be done if buffered connections are passed in, or
// buffering is configured on the handle.
//
// Deprecated: Buffering should be configured at the Handle or by using a buffer Reader.
// Setting this has no effect anymore (after v1.2.12 - authored 2025-05-06)
RPCNoBuffer bool
}
// rpcCodec defines the struct members and common methods.
type rpcCodec struct {
c io.Closer
r io.Reader
w io.Writer
f ioFlusher
nc net.Conn
c io.Closer
r io.Reader
w io.Writer
f ioFlusher
dec *Decoder
enc *Encoder
h Handle
cls atomic.Pointer[clsErr]
cls atomicClsErr
}
func newRPCCodec(conn io.ReadWriteCloser, h Handle) *rpcCodec {
nc, _ := conn.(net.Conn)
f, _ := conn.(ioFlusher)
rc := &rpcCodec{
h: h,
c: conn,
w: conn,
r: conn,
f: f,
nc: nc,
enc: NewEncoder(conn, h),
dec: NewDecoder(conn, h),
func newRPCCodec(conn io.ReadWriteCloser, h Handle) rpcCodec {
return newRPCCodec2(conn, conn, conn, h)
}
func newRPCCodec2(r io.Reader, w io.Writer, c io.Closer, h Handle) rpcCodec {
bh := h.getBasicHandle()
// if the writer can flush, ensure we leverage it, else
// we may hang waiting on read if write isn't flushed.
// var f ioFlusher
f, ok := w.(ioFlusher)
if !bh.RPCNoBuffer {
if bh.WriterBufferSize <= 0 {
if !ok { // a flusher means there's already a buffer
bw := bufio.NewWriter(w)
f, w = bw, bw
}
}
if bh.ReaderBufferSize <= 0 {
if _, ok = w.(ioBuffered); !ok {
r = bufio.NewReader(r)
}
}
}
return rpcCodec{
c: c,
w: w,
r: r,
f: f,
h: h,
enc: NewEncoder(w, h),
dec: NewDecoder(r, h),
}
rc.cls.Store(new(clsErr))
return rc
}
func (c *rpcCodec) write(obj ...interface{}) (err error) {
@@ -104,16 +116,10 @@ func (c *rpcCodec) write(obj ...interface{}) (err error) {
func (c *rpcCodec) read(obj interface{}) (err error) {
err = c.ready()
if err == nil {
// Setting ReadDeadline should not be necessary,
// especially since it only works for net.Conn (not generic ioReadCloser).
// if c.nc != nil {
// c.nc.SetReadDeadline(time.Now().Add(1 * time.Second))
// }
// Note: If nil is passed in, we should read and discard
//If nil is passed in, we should read and discard
if obj == nil {
// return c.dec.Decode(&obj)
err = panicToErr(c.dec, func() { c.dec.swallow() })
err = c.dec.swallowErr()
} else {
err = c.dec.Decode(obj)
}
@@ -123,11 +129,11 @@ func (c *rpcCodec) read(obj interface{}) (err error) {
func (c *rpcCodec) Close() (err error) {
if c.c != nil {
cls := c.cls.Load()
cls := c.cls.load()
if !cls.closed {
// writing to same pointer could lead to a data race (always make new one)
cls = &clsErr{closed: true, err: c.c.Close()}
c.cls.Store(cls)
cls.err = c.c.Close()
cls.closed = true
c.cls.store(cls)
}
err = cls.err
}
@@ -138,8 +144,8 @@ func (c *rpcCodec) ready() (err error) {
if c.c == nil {
err = errRpcNoConn
} else {
cls := c.cls.Load()
if cls != nil && cls.closed {
cls := c.cls.load()
if cls.closed {
if err = cls.err; err == nil {
err = errRpcIsClosed
}
@@ -155,7 +161,7 @@ func (c *rpcCodec) ReadResponseBody(body interface{}) error {
// -------------------------------------
type goRpcCodec struct {
*rpcCodec
rpcCodec
}
func (c *goRpcCodec) WriteRequest(r *rpc.Request, body interface{}) error {

View File

@@ -1,65 +1,111 @@
//go:build notmono || codec.notmono
// Copyright (c) 2012-2020 Ugorji Nwoke. All rights reserved.
// Use of this source code is governed by a MIT license found in the LICENSE file.
package codec
import (
"io"
"math"
"reflect"
"time"
)
type simpleEncDriver[T encWriter] struct {
const (
_ uint8 = iota
simpleVdNil = 1
simpleVdFalse = 2
simpleVdTrue = 3
simpleVdFloat32 = 4
simpleVdFloat64 = 5
// each lasts for 4 (ie n, n+1, n+2, n+3)
simpleVdPosInt = 8
simpleVdNegInt = 12
simpleVdTime = 24
// containers: each lasts for 4 (ie n, n+1, n+2, ... n+7)
simpleVdString = 216
simpleVdByteArray = 224
simpleVdArray = 232
simpleVdMap = 240
simpleVdExt = 248
)
var simpledescNames = map[byte]string{
simpleVdNil: "null",
simpleVdFalse: "false",
simpleVdTrue: "true",
simpleVdFloat32: "float32",
simpleVdFloat64: "float64",
simpleVdPosInt: "+int",
simpleVdNegInt: "-int",
simpleVdTime: "time",
simpleVdString: "string",
simpleVdByteArray: "binary",
simpleVdArray: "array",
simpleVdMap: "map",
simpleVdExt: "ext",
}
func simpledesc(bd byte) (s string) {
s = simpledescNames[bd]
if s == "" {
s = "unknown"
}
return
}
type simpleEncDriver struct {
noBuiltInTypes
encDriverNoopContainerWriter
encDriverNoState
encDriverContainerNoTrackerT
encInit2er
h *SimpleHandle
e *encoderBase
// b [8]byte
w T
e Encoder
}
func (e *simpleEncDriver[T]) EncodeNil() {
e.w.writen1(simpleVdNil)
func (e *simpleEncDriver) encoder() *Encoder {
return &e.e
}
func (e *simpleEncDriver[T]) EncodeBool(b bool) {
func (e *simpleEncDriver) EncodeNil() {
e.e.encWr.writen1(simpleVdNil)
}
func (e *simpleEncDriver) EncodeBool(b bool) {
if e.h.EncZeroValuesAsNil && e.e.c != containerMapKey && !b {
e.EncodeNil()
return
}
if b {
e.w.writen1(simpleVdTrue)
e.e.encWr.writen1(simpleVdTrue)
} else {
e.w.writen1(simpleVdFalse)
e.e.encWr.writen1(simpleVdFalse)
}
}
func (e *simpleEncDriver[T]) EncodeFloat32(f float32) {
func (e *simpleEncDriver) EncodeFloat32(f float32) {
if e.h.EncZeroValuesAsNil && e.e.c != containerMapKey && f == 0.0 {
e.EncodeNil()
return
}
e.w.writen1(simpleVdFloat32)
e.w.writen4(bigen.PutUint32(math.Float32bits(f)))
e.e.encWr.writen1(simpleVdFloat32)
bigen.writeUint32(e.e.w(), math.Float32bits(f))
}
func (e *simpleEncDriver[T]) EncodeFloat64(f float64) {
func (e *simpleEncDriver) EncodeFloat64(f float64) {
if e.h.EncZeroValuesAsNil && e.e.c != containerMapKey && f == 0.0 {
e.EncodeNil()
return
}
e.w.writen1(simpleVdFloat64)
e.w.writen8(bigen.PutUint64(math.Float64bits(f)))
e.e.encWr.writen1(simpleVdFloat64)
bigen.writeUint64(e.e.w(), math.Float64bits(f))
}
func (e *simpleEncDriver[T]) EncodeInt(v int64) {
func (e *simpleEncDriver) EncodeInt(v int64) {
if v < 0 {
e.encUint(uint64(-v), simpleVdNegInt)
} else {
@@ -67,62 +113,62 @@ func (e *simpleEncDriver[T]) EncodeInt(v int64) {
}
}
func (e *simpleEncDriver[T]) EncodeUint(v uint64) {
func (e *simpleEncDriver) EncodeUint(v uint64) {
e.encUint(v, simpleVdPosInt)
}
func (e *simpleEncDriver[T]) encUint(v uint64, bd uint8) {
func (e *simpleEncDriver) encUint(v uint64, bd uint8) {
if e.h.EncZeroValuesAsNil && e.e.c != containerMapKey && v == 0 {
e.EncodeNil()
return
}
if v <= math.MaxUint8 {
e.w.writen2(bd, uint8(v))
e.e.encWr.writen2(bd, uint8(v))
} else if v <= math.MaxUint16 {
e.w.writen1(bd + 1)
e.w.writen2(bigen.PutUint16(uint16(v)))
e.e.encWr.writen1(bd + 1)
bigen.writeUint16(e.e.w(), uint16(v))
} else if v <= math.MaxUint32 {
e.w.writen1(bd + 2)
e.w.writen4(bigen.PutUint32(uint32(v)))
e.e.encWr.writen1(bd + 2)
bigen.writeUint32(e.e.w(), uint32(v))
} else { // if v <= math.MaxUint64 {
e.w.writen1(bd + 3)
e.w.writen8(bigen.PutUint64(v))
e.e.encWr.writen1(bd + 3)
bigen.writeUint64(e.e.w(), v)
}
}
func (e *simpleEncDriver[T]) encLen(bd byte, length int) {
func (e *simpleEncDriver) encLen(bd byte, length int) {
if length == 0 {
e.w.writen1(bd)
e.e.encWr.writen1(bd)
} else if length <= math.MaxUint8 {
e.w.writen1(bd + 1)
e.w.writen1(uint8(length))
e.e.encWr.writen1(bd + 1)
e.e.encWr.writen1(uint8(length))
} else if length <= math.MaxUint16 {
e.w.writen1(bd + 2)
e.w.writen2(bigen.PutUint16(uint16(length)))
e.e.encWr.writen1(bd + 2)
bigen.writeUint16(e.e.w(), uint16(length))
} else if int64(length) <= math.MaxUint32 {
e.w.writen1(bd + 3)
e.w.writen4(bigen.PutUint32(uint32(length)))
e.e.encWr.writen1(bd + 3)
bigen.writeUint32(e.e.w(), uint32(length))
} else {
e.w.writen1(bd + 4)
e.w.writen8(bigen.PutUint64(uint64(length)))
e.e.encWr.writen1(bd + 4)
bigen.writeUint64(e.e.w(), uint64(length))
}
}
func (e *simpleEncDriver[T]) EncodeExt(v interface{}, basetype reflect.Type, xtag uint64, ext Ext) {
func (e *simpleEncDriver) EncodeExt(v interface{}, basetype reflect.Type, xtag uint64, ext Ext) {
var bs0, bs []byte
if ext == SelfExt {
bs0 = e.e.blist.get(1024)
bs = bs0
sideEncode(e.h, &e.h.sideEncPool, func(se encoderI) { oneOffEncode(se, v, &bs, basetype, true) })
e.e.sideEncode(v, basetype, &bs)
} else {
bs = ext.WriteExt(v)
}
if bs == nil {
e.writeNilBytes()
e.EncodeNil()
goto END
}
e.encodeExtPreamble(uint8(xtag), len(bs))
e.w.writeb(bs)
e.e.encWr.writeb(bs)
END:
if ext == SelfExt {
e.e.blist.put(bs)
@@ -132,35 +178,25 @@ END:
}
}
func (e *simpleEncDriver[T]) EncodeRawExt(re *RawExt) {
func (e *simpleEncDriver) EncodeRawExt(re *RawExt) {
e.encodeExtPreamble(uint8(re.Tag), len(re.Data))
e.w.writeb(re.Data)
e.e.encWr.writeb(re.Data)
}
func (e *simpleEncDriver[T]) encodeExtPreamble(xtag byte, length int) {
func (e *simpleEncDriver) encodeExtPreamble(xtag byte, length int) {
e.encLen(simpleVdExt, length)
e.w.writen1(xtag)
e.e.encWr.writen1(xtag)
}
func (e *simpleEncDriver[T]) WriteArrayStart(length int) {
func (e *simpleEncDriver) WriteArrayStart(length int) {
e.encLen(simpleVdArray, length)
}
func (e *simpleEncDriver[T]) WriteMapStart(length int) {
func (e *simpleEncDriver) WriteMapStart(length int) {
e.encLen(simpleVdMap, length)
}
func (e *simpleEncDriver[T]) WriteArrayEmpty() {
// e.WriteArrayStart(0) = e.encLen(simpleVdArray, 0)
e.w.writen1(simpleVdArray)
}
func (e *simpleEncDriver[T]) WriteMapEmpty() {
// e.WriteMapStart(0) = e.encLen(simpleVdMap, 0)
e.w.writen1(simpleVdMap)
}
func (e *simpleEncDriver[T]) EncodeString(v string) {
func (e *simpleEncDriver) EncodeString(v string) {
if e.h.EncZeroValuesAsNil && e.e.c != containerMapKey && v == "" {
e.EncodeNil()
return
@@ -170,88 +206,57 @@ func (e *simpleEncDriver[T]) EncodeString(v string) {
} else {
e.encLen(simpleVdString, len(v))
}
e.w.writestr(v)
e.e.encWr.writestr(v)
}
func (e *simpleEncDriver[T]) EncodeStringNoEscape4Json(v string) { e.EncodeString(v) }
func (e *simpleEncDriver[T]) EncodeStringBytesRaw(v []byte) {
func (e *simpleEncDriver) EncodeStringBytesRaw(v []byte) {
// if e.h.EncZeroValuesAsNil && e.c != containerMapKey && v == nil {
e.encLen(simpleVdByteArray, len(v))
e.w.writeb(v)
}
func (e *simpleEncDriver[T]) EncodeBytes(v []byte) {
if v == nil {
e.writeNilBytes()
e.EncodeNil()
return
}
e.EncodeStringBytesRaw(v)
e.encLen(simpleVdByteArray, len(v))
e.e.encWr.writeb(v)
}
func (e *simpleEncDriver[T]) encodeNilBytes() {
b := byte(simpleVdNil)
if e.h.NilCollectionToZeroLength {
b = simpleVdArray
}
e.w.writen1(b)
}
func (e *simpleEncDriver[T]) writeNilOr(v byte) {
if !e.h.NilCollectionToZeroLength {
v = simpleVdNil
}
e.w.writen1(v)
}
func (e *simpleEncDriver[T]) writeNilArray() {
e.writeNilOr(simpleVdArray)
}
func (e *simpleEncDriver[T]) writeNilMap() {
e.writeNilOr(simpleVdMap)
}
func (e *simpleEncDriver[T]) writeNilBytes() {
e.writeNilOr(simpleVdByteArray)
}
func (e *simpleEncDriver[T]) EncodeTime(t time.Time) {
func (e *simpleEncDriver) EncodeTime(t time.Time) {
// if e.h.EncZeroValuesAsNil && e.c != containerMapKey && t.IsZero() {
if t.IsZero() {
e.EncodeNil()
return
}
v, err := t.MarshalBinary()
halt.onerror(err)
e.w.writen2(simpleVdTime, uint8(len(v)))
e.w.writeb(v)
e.e.onerror(err)
e.e.encWr.writen2(simpleVdTime, uint8(len(v)))
e.e.encWr.writeb(v)
}
//------------------------------------
type simpleDecDriver[T decReader] struct {
type simpleDecDriver struct {
h *SimpleHandle
d *decoderBase
r T
bdAndBdread
// bytes bool
_ bool
noBuiltInTypes
// decDriverNoopNumberHelper
decDriverNoopContainerReader
decInit2er
// ds interface{} // must be *decoder[simpleDecDriverM[bytes...]]
decDriverNoopNumberHelper
d Decoder
}
func (d *simpleDecDriver[T]) readNextBd() {
d.bd = d.r.readn1()
func (d *simpleDecDriver) decoder() *Decoder {
return &d.d
}
func (d *simpleDecDriver) descBd() string {
return sprintf("%v (%s)", d.bd, simpledesc(d.bd))
}
func (d *simpleDecDriver) readNextBd() {
d.bd = d.d.decRd.readn1()
d.bdRead = true
}
func (d *simpleDecDriver[T]) advanceNil() (null bool) {
func (d *simpleDecDriver) advanceNil() (null bool) {
if !d.bdRead {
d.readNextBd()
}
@@ -262,7 +267,7 @@ func (d *simpleDecDriver[T]) advanceNil() (null bool) {
return
}
func (d *simpleDecDriver[T]) ContainerType() (vt valueType) {
func (d *simpleDecDriver) ContainerType() (vt valueType) {
if !d.bdRead {
d.readNextBd()
}
@@ -286,90 +291,88 @@ func (d *simpleDecDriver[T]) ContainerType() (vt valueType) {
return valueTypeUnset
}
func (d *simpleDecDriver[T]) TryNil() bool {
func (d *simpleDecDriver) TryNil() bool {
return d.advanceNil()
}
func (d *simpleDecDriver[T]) decFloat() (f float64, ok bool) {
func (d *simpleDecDriver) decFloat() (f float64, ok bool) {
ok = true
switch d.bd {
case simpleVdFloat32:
f = float64(math.Float32frombits(bigen.Uint32(d.r.readn4())))
f = float64(math.Float32frombits(bigen.Uint32(d.d.decRd.readn4())))
case simpleVdFloat64:
f = math.Float64frombits(bigen.Uint64(d.r.readn8()))
f = math.Float64frombits(bigen.Uint64(d.d.decRd.readn8()))
default:
ok = false
}
return
}
func (d *simpleDecDriver[T]) decInteger() (ui uint64, neg, ok bool) {
func (d *simpleDecDriver) decInteger() (ui uint64, neg, ok bool) {
ok = true
switch d.bd {
case simpleVdPosInt:
ui = uint64(d.r.readn1())
ui = uint64(d.d.decRd.readn1())
case simpleVdPosInt + 1:
ui = uint64(bigen.Uint16(d.r.readn2()))
ui = uint64(bigen.Uint16(d.d.decRd.readn2()))
case simpleVdPosInt + 2:
ui = uint64(bigen.Uint32(d.r.readn4()))
ui = uint64(bigen.Uint32(d.d.decRd.readn4()))
case simpleVdPosInt + 3:
ui = uint64(bigen.Uint64(d.r.readn8()))
ui = uint64(bigen.Uint64(d.d.decRd.readn8()))
case simpleVdNegInt:
ui = uint64(d.r.readn1())
ui = uint64(d.d.decRd.readn1())
neg = true
case simpleVdNegInt + 1:
ui = uint64(bigen.Uint16(d.r.readn2()))
ui = uint64(bigen.Uint16(d.d.decRd.readn2()))
neg = true
case simpleVdNegInt + 2:
ui = uint64(bigen.Uint32(d.r.readn4()))
ui = uint64(bigen.Uint32(d.d.decRd.readn4()))
neg = true
case simpleVdNegInt + 3:
ui = uint64(bigen.Uint64(d.r.readn8()))
ui = uint64(bigen.Uint64(d.d.decRd.readn8()))
neg = true
default:
ok = false
// halt.errorf("integer only valid from pos/neg integer1..8. Invalid descriptor: %v", d.bd)
// d.d.errorf("integer only valid from pos/neg integer1..8. Invalid descriptor: %v", d.bd)
}
// DO NOT do this check below, because callers may only want the unsigned value:
//
// if ui > math.MaxInt64 {
// halt.errorf("decIntAny: Integer out of range for signed int64: %v", ui)
// d.d.errorf("decIntAny: Integer out of range for signed int64: %v", ui)
// return
// }
return
}
func (d *simpleDecDriver[T]) DecodeInt64() (i int64) {
func (d *simpleDecDriver) DecodeInt64() (i int64) {
if d.advanceNil() {
return
}
v1, v2, v3 := d.decInteger()
i = decNegintPosintFloatNumberHelper{d}.int64(v1, v2, v3, false)
i = decNegintPosintFloatNumberHelper{&d.d}.int64(d.decInteger())
d.bdRead = false
return
}
func (d *simpleDecDriver[T]) DecodeUint64() (ui uint64) {
func (d *simpleDecDriver) DecodeUint64() (ui uint64) {
if d.advanceNil() {
return
}
ui = decNegintPosintFloatNumberHelper{d}.uint64(d.decInteger())
ui = decNegintPosintFloatNumberHelper{&d.d}.uint64(d.decInteger())
d.bdRead = false
return
}
func (d *simpleDecDriver[T]) DecodeFloat64() (f float64) {
func (d *simpleDecDriver) DecodeFloat64() (f float64) {
if d.advanceNil() {
return
}
v1, v2 := d.decFloat()
f = decNegintPosintFloatNumberHelper{d}.float64(v1, v2, false)
f = decNegintPosintFloatNumberHelper{&d.d}.float64(d.decFloat())
d.bdRead = false
return
}
// bool can be decoded from bool only (single byte).
func (d *simpleDecDriver[T]) DecodeBool() (b bool) {
func (d *simpleDecDriver) DecodeBool() (b bool) {
if d.advanceNil() {
return
}
@@ -377,13 +380,13 @@ func (d *simpleDecDriver[T]) DecodeBool() (b bool) {
} else if d.bd == simpleVdTrue {
b = true
} else {
halt.errorf("cannot decode bool - %s: %x", msgBadDesc, d.bd)
d.d.errorf("cannot decode bool - %s: %x", msgBadDesc, d.bd)
}
d.bdRead = false
return
}
func (d *simpleDecDriver[T]) ReadMapStart() (length int) {
func (d *simpleDecDriver) ReadMapStart() (length int) {
if d.advanceNil() {
return containerLenNil
}
@@ -391,7 +394,7 @@ func (d *simpleDecDriver[T]) ReadMapStart() (length int) {
return d.decLen()
}
func (d *simpleDecDriver[T]) ReadArrayStart() (length int) {
func (d *simpleDecDriver) ReadArrayStart() (length int) {
if d.advanceNil() {
return containerLenNil
}
@@ -399,128 +402,131 @@ func (d *simpleDecDriver[T]) ReadArrayStart() (length int) {
return d.decLen()
}
func (d *simpleDecDriver[T]) uint2Len(ui uint64) int {
func (d *simpleDecDriver) uint2Len(ui uint64) int {
if chkOvf.Uint(ui, intBitsize) {
halt.errorf("overflow integer: %v", ui)
d.d.errorf("overflow integer: %v", ui)
}
return int(ui)
}
func (d *simpleDecDriver[T]) decLen() int {
func (d *simpleDecDriver) decLen() int {
switch d.bd & 7 { // d.bd % 8 {
case 0:
return 0
case 1:
return int(d.r.readn1())
return int(d.d.decRd.readn1())
case 2:
return int(bigen.Uint16(d.r.readn2()))
return int(bigen.Uint16(d.d.decRd.readn2()))
case 3:
return d.uint2Len(uint64(bigen.Uint32(d.r.readn4())))
return d.uint2Len(uint64(bigen.Uint32(d.d.decRd.readn4())))
case 4:
return d.uint2Len(bigen.Uint64(d.r.readn8()))
return d.uint2Len(bigen.Uint64(d.d.decRd.readn8()))
}
halt.errorf("cannot read length: bd%%8 must be in range 0..4. Got: %d", d.bd%8)
d.d.errorf("cannot read length: bd%%8 must be in range 0..4. Got: %d", d.bd%8)
return -1
}
func (d *simpleDecDriver[T]) DecodeStringAsBytes() ([]byte, dBytesAttachState) {
return d.DecodeBytes()
func (d *simpleDecDriver) DecodeStringAsBytes() (s []byte) {
return d.DecodeBytes(nil)
}
func (d *simpleDecDriver[T]) DecodeBytes() (bs []byte, state dBytesAttachState) {
func (d *simpleDecDriver) DecodeBytes(bs []byte) (bsOut []byte) {
d.d.decByteState = decByteStateNone
if d.advanceNil() {
return
}
var cond bool
// check if an "array" of uint8's (see ContainerType for how to infer if an array)
if d.bd >= simpleVdArray && d.bd <= simpleVdArray+4 {
if d.bd >= simpleVdArray && d.bd <= simpleVdMap+4 {
if bs == nil {
d.d.decByteState = decByteStateReuseBuf
bs = d.d.b[:]
}
slen := d.ReadArrayStart()
bs, cond = usableByteSlice(d.d.buf, slen)
var changed bool
if bs, changed = usableByteSlice(bs, slen); changed {
d.d.decByteState = decByteStateNone
}
for i := 0; i < len(bs); i++ {
bs[i] = uint8(chkOvf.UintV(d.DecodeUint64(), 8))
}
for i := len(bs); i < slen; i++ {
bs = append(bs, uint8(chkOvf.UintV(d.DecodeUint64(), 8)))
}
if cond {
d.d.buf = bs
}
state = dBytesAttachBuffer
return
return bs
}
clen := d.decLen()
d.bdRead = false
bs, cond = d.r.readxb(uint(clen))
state = d.d.attachState(cond)
return
if d.d.zerocopy() {
d.d.decByteState = decByteStateZerocopy
return d.d.decRd.rb.readx(uint(clen))
}
if bs == nil {
d.d.decByteState = decByteStateReuseBuf
bs = d.d.b[:]
}
return decByteSlice(d.d.r(), clen, d.d.h.MaxInitLen, bs)
}
func (d *simpleDecDriver[T]) DecodeTime() (t time.Time) {
func (d *simpleDecDriver) DecodeTime() (t time.Time) {
if d.advanceNil() {
return
}
if d.bd != simpleVdTime {
halt.errorf("invalid descriptor for time.Time - expect 0x%x, received 0x%x", simpleVdTime, d.bd)
d.d.errorf("invalid descriptor for time.Time - expect 0x%x, received 0x%x", simpleVdTime, d.bd)
}
d.bdRead = false
clen := uint(d.r.readn1())
b := d.r.readx(clen)
halt.onerror((&t).UnmarshalBinary(b))
clen := uint(d.d.decRd.readn1())
b := d.d.decRd.readx(clen)
d.d.onerror((&t).UnmarshalBinary(b))
return
}
func (d *simpleDecDriver[T]) DecodeExt(rv interface{}, basetype reflect.Type, xtag uint64, ext Ext) {
xbs, _, _, ok := d.decodeExtV(ext != nil, xtag)
if !ok {
func (d *simpleDecDriver) DecodeExt(rv interface{}, basetype reflect.Type, xtag uint64, ext Ext) {
if xtag > 0xff {
d.d.errorf("ext: tag must be <= 0xff; got: %v", xtag)
}
if d.advanceNil() {
return
}
if ext == SelfExt {
sideDecode(d.h, &d.h.sideDecPool, func(sd decoderI) { oneOffDecode(sd, rv, xbs, basetype, true) })
xbs, realxtag1, zerocopy := d.decodeExtV(ext != nil, uint8(xtag))
realxtag := uint64(realxtag1)
if ext == nil {
re := rv.(*RawExt)
re.Tag = realxtag
re.setData(xbs, zerocopy)
} else if ext == SelfExt {
d.d.sideDecode(rv, basetype, xbs)
} else {
ext.ReadExt(rv, xbs)
}
}
func (d *simpleDecDriver[T]) DecodeRawExt(re *RawExt) {
xbs, realxtag, state, ok := d.decodeExtV(false, 0)
if !ok {
return
}
re.Tag = uint64(realxtag)
re.setData(xbs, state >= dBytesAttachViewZerocopy)
}
func (d *simpleDecDriver[T]) decodeExtV(verifyTag bool, xtagIn uint64) (xbs []byte, xtag byte, bstate dBytesAttachState, ok bool) {
if xtagIn > 0xff {
halt.errorf("ext: tag must be <= 0xff; got: %v", xtagIn)
}
if d.advanceNil() {
return
}
tag := uint8(xtagIn)
func (d *simpleDecDriver) decodeExtV(verifyTag bool, tag byte) (xbs []byte, xtag byte, zerocopy bool) {
switch d.bd {
case simpleVdExt, simpleVdExt + 1, simpleVdExt + 2, simpleVdExt + 3, simpleVdExt + 4:
l := d.decLen()
xtag = d.r.readn1()
xtag = d.d.decRd.readn1()
if verifyTag && xtag != tag {
halt.errorf("wrong extension tag. Got %b. Expecting: %v", xtag, tag)
d.d.errorf("wrong extension tag. Got %b. Expecting: %v", xtag, tag)
}
if d.d.bytes {
xbs = d.d.decRd.rb.readx(uint(l))
zerocopy = true
} else {
xbs = decByteSlice(d.d.r(), l, d.d.h.MaxInitLen, d.d.b[:])
}
xbs, ok = d.r.readxb(uint(l))
bstate = d.d.attachState(ok)
case simpleVdByteArray, simpleVdByteArray + 1,
simpleVdByteArray + 2, simpleVdByteArray + 3, simpleVdByteArray + 4:
xbs, bstate = d.DecodeBytes()
xbs = d.DecodeBytes(nil)
default:
halt.errorf("ext - %s - expecting extensions/bytearray, got: 0x%x", msgBadDesc, d.bd)
d.d.errorf("ext - %s - expecting extensions/bytearray, got: 0x%x", msgBadDesc, d.bd)
}
d.bdRead = false
ok = true
return
}
func (d *simpleDecDriver[T]) DecodeNaked() {
func (d *simpleDecDriver) DecodeNaked() {
if !d.bdRead {
d.readNextBd()
}
@@ -560,20 +566,19 @@ func (d *simpleDecDriver[T]) DecodeNaked() {
case simpleVdString, simpleVdString + 1,
simpleVdString + 2, simpleVdString + 3, simpleVdString + 4:
n.v = valueTypeString
n.s = d.d.detach2Str(d.DecodeStringAsBytes())
n.s = d.d.stringZC(d.DecodeStringAsBytes())
case simpleVdByteArray, simpleVdByteArray + 1,
simpleVdByteArray + 2, simpleVdByteArray + 3, simpleVdByteArray + 4:
d.d.fauxUnionReadRawBytes(d, false, d.h.RawToString) //, d.h.ZeroCopy)
d.d.fauxUnionReadRawBytes(false)
case simpleVdExt, simpleVdExt + 1, simpleVdExt + 2, simpleVdExt + 3, simpleVdExt + 4:
n.v = valueTypeExt
l := d.decLen()
n.u = uint64(d.r.readn1())
n.l = d.r.readx(uint(l))
// MARKER: not necessary to detach for extensions
// var useBuf bool
// n.l, useBuf = d.r.readxb(uint(l))
// n.a = d.d.attachState(useBuf)
// n.l = d.d.detach2Bytes(n.l, nil, n.a)
n.u = uint64(d.d.decRd.readn1())
if d.d.bytes {
n.l = d.d.decRd.rb.readx(uint(l))
} else {
n.l = decByteSlice(d.d.r(), l, d.d.h.MaxInitLen, d.d.b[:])
}
case simpleVdArray, simpleVdArray + 1, simpleVdArray + 2,
simpleVdArray + 3, simpleVdArray + 4:
n.v = valueTypeArray
@@ -582,7 +587,7 @@ func (d *simpleDecDriver[T]) DecodeNaked() {
n.v = valueTypeMap
decodeFurther = true
default:
halt.errorf("cannot infer value - %s 0x%x", msgBadDesc, d.bd)
d.d.errorf("cannot infer value - %s 0x%x", msgBadDesc, d.bd)
}
if !decodeFurther {
@@ -590,18 +595,32 @@ func (d *simpleDecDriver[T]) DecodeNaked() {
}
}
func (d *simpleDecDriver[T]) nextValueBytes() (v []byte) {
func (d *simpleDecDriver) nextValueBytes(v0 []byte) (v []byte) {
if !d.bdRead {
d.readNextBd()
}
d.r.startRecording()
d.nextValueBytesBdReadR()
v = d.r.stopRecording()
v = v0
var h = decNextValueBytesHelper{d: &d.d}
var cursor = d.d.rb.c - 1
h.append1(&v, d.bd)
v = d.nextValueBytesBdReadR(v)
d.bdRead = false
h.bytesRdV(&v, cursor)
return
}
func (d *simpleDecDriver[T]) nextValueBytesBdReadR() {
func (d *simpleDecDriver) nextValueBytesR(v0 []byte) (v []byte) {
d.readNextBd()
v = v0
var h = decNextValueBytesHelper{d: &d.d}
h.append1(&v, d.bd)
return d.nextValueBytesBdReadR(v)
}
func (d *simpleDecDriver) nextValueBytesBdReadR(v0 []byte) (v []byte) {
v = v0
var h = decNextValueBytesHelper{d: &d.d}
c := d.bd
var length uint
@@ -610,33 +629,38 @@ func (d *simpleDecDriver[T]) nextValueBytesBdReadR() {
case simpleVdNil, simpleVdFalse, simpleVdTrue, simpleVdString, simpleVdByteArray:
// pass
case simpleVdPosInt, simpleVdNegInt:
d.r.readn1()
h.append1(&v, d.d.decRd.readn1())
case simpleVdPosInt + 1, simpleVdNegInt + 1:
d.r.skip(2)
h.appendN(&v, d.d.decRd.readx(2)...)
case simpleVdPosInt + 2, simpleVdNegInt + 2, simpleVdFloat32:
d.r.skip(4)
h.appendN(&v, d.d.decRd.readx(4)...)
case simpleVdPosInt + 3, simpleVdNegInt + 3, simpleVdFloat64:
d.r.skip(8)
h.appendN(&v, d.d.decRd.readx(8)...)
case simpleVdTime:
c = d.r.readn1()
d.r.skip(uint(c))
c = d.d.decRd.readn1()
h.append1(&v, c)
h.appendN(&v, d.d.decRd.readx(uint(c))...)
default:
switch c & 7 { // c % 8 {
case 0:
length = 0
case 1:
b := d.r.readn1()
b := d.d.decRd.readn1()
length = uint(b)
h.append1(&v, b)
case 2:
x := d.r.readn2()
x := d.d.decRd.readn2()
length = uint(bigen.Uint16(x))
h.appendN(&v, x[:]...)
case 3:
x := d.r.readn4()
x := d.d.decRd.readn4()
length = uint(bigen.Uint32(x))
h.appendN(&v, x[:]...)
case 4:
x := d.r.readn8()
x := d.d.decRd.readn8()
length = uint(bigen.Uint64(x))
h.appendN(&v, x[:]...)
}
bExt := c >= simpleVdExt && c <= simpleVdExt+7
@@ -646,11 +670,11 @@ func (d *simpleDecDriver[T]) nextValueBytesBdReadR() {
bMap := c >= simpleVdMap && c <= simpleVdMap+7
if !(bExt || bStr || bByteArray || bArray || bMap) {
halt.errorf("cannot infer value - %s 0x%x", msgBadDesc, c)
d.d.errorf("cannot infer value - %s 0x%x", msgBadDesc, c)
}
if bExt {
d.r.readn1() // tag
h.append1(&v, d.d.decRd.readn1()) // tag
}
if length == 0 {
@@ -659,91 +683,68 @@ func (d *simpleDecDriver[T]) nextValueBytesBdReadR() {
if bArray {
for i := uint(0); i < length; i++ {
d.readNextBd()
d.nextValueBytesBdReadR()
v = d.nextValueBytesR(v)
}
} else if bMap {
for i := uint(0); i < length; i++ {
d.readNextBd()
d.nextValueBytesBdReadR()
d.readNextBd()
d.nextValueBytesBdReadR()
v = d.nextValueBytesR(v)
v = d.nextValueBytesR(v)
}
} else {
d.r.skip(length)
h.appendN(&v, d.d.decRd.readx(length)...)
}
}
return
}
// ----
//------------------------------------
// SimpleHandle is a Handle for a very simple encoding format.
//
// The following below are similar across all format files (except for the format name).
// simple is a simplistic codec similar to binc, but not as compact.
// - Encoding of a value is always preceded by the descriptor byte (bd)
// - True, false, nil are encoded fully in 1 byte (the descriptor)
// - Integers (intXXX, uintXXX) are encoded in 1, 2, 4 or 8 bytes (plus a descriptor byte).
// There are positive (uintXXX and intXXX >= 0) and negative (intXXX < 0) integers.
// - Floats are encoded in 4 or 8 bytes (plus a descriptor byte)
// - Length of containers (strings, bytes, array, map, extensions)
// are encoded in 0, 1, 2, 4 or 8 bytes.
// Zero-length containers have no length encoded.
// For others, the number of bytes is given by pow(2, bd%3)
// - maps are encoded as [bd] [length] [[key][value]]...
// - arrays are encoded as [bd] [length] [value]...
// - extensions are encoded as [bd] [length] [tag] [byte]...
// - strings/bytearrays are encoded as [bd] [length] [byte]...
// - time.Time are encoded as [bd] [length] [byte]...
//
// We keep them together here, so that we can easily copy and compare.
// ----
func (d *simpleEncDriver[T]) init(hh Handle, shared *encoderBase, enc encoderI) (fp interface{}) {
callMake(&d.w)
d.h = hh.(*SimpleHandle)
d.e = shared
if shared.bytes {
fp = simpleFpEncBytes
} else {
fp = simpleFpEncIO
}
// d.w.init()
d.init2(enc)
return
// The full spec will be published soon.
type SimpleHandle struct {
binaryEncodingType
BasicHandle
// EncZeroValuesAsNil says to encode zero values for numbers, bool, string, etc as nil
EncZeroValuesAsNil bool
}
func (e *simpleEncDriver[T]) writeBytesAsis(b []byte) { e.w.writeb(b) }
// Name returns the name of the handle: simple
func (h *SimpleHandle) Name() string { return "simple" }
func (e *simpleEncDriver[T]) writerEnd() { e.w.end() }
func (h *SimpleHandle) desc(bd byte) string { return simpledesc(bd) }
func (e *simpleEncDriver[T]) resetOutBytes(out *[]byte) {
e.w.resetBytes(*out, out)
func (h *SimpleHandle) newEncDriver() encDriver {
var e = &simpleEncDriver{h: h}
e.e.e = e
e.e.init(h)
e.reset()
return e
}
func (e *simpleEncDriver[T]) resetOutIO(out io.Writer) {
e.w.resetIO(out, e.h.WriterBufferSize, &e.e.blist)
func (h *SimpleHandle) newDecDriver() decDriver {
d := &simpleDecDriver{h: h}
d.d.d = d
d.d.init(h)
d.reset()
return d
}
// ----
func (d *simpleDecDriver[T]) init(hh Handle, shared *decoderBase, dec decoderI) (fp interface{}) {
callMake(&d.r)
d.h = hh.(*SimpleHandle)
d.d = shared
if shared.bytes {
fp = simpleFpDecBytes
} else {
fp = simpleFpDecIO
}
// d.r.init()
d.init2(dec)
return
}
func (d *simpleDecDriver[T]) NumBytesRead() int {
return int(d.r.numread())
}
func (d *simpleDecDriver[T]) resetInBytes(in []byte) {
d.r.resetBytes(in)
}
func (d *simpleDecDriver[T]) resetInIO(r io.Reader) {
d.r.resetIO(r, d.h.ReaderBufferSize, d.h.MaxInitLen, &d.d.blist)
}
// ---- (custom stanza)
func (d *simpleDecDriver[T]) descBd() string {
return sprintf("%v (%s)", d.bd, simpledesc(d.bd))
}
func (d *simpleDecDriver[T]) DecodeFloat32() (f float32) {
return float32(chkOvf.Float32V(d.DecodeFloat64()))
}
var _ decDriver = (*simpleDecDriver)(nil)
var _ encDriver = (*simpleEncDriver)(nil)

View File

@@ -3,14 +3,10 @@
package codec
import (
"io"
)
const maxConsecutiveEmptyWrites = 16 // 2 is sufficient, 16 is enough, 64 is optimal
import "io"
// encWriter abstracts writing to a byte array or to an io.Writer.
type encWriterI interface {
type encWriter interface {
writeb([]byte)
writestr(string)
writeqstr(string) // write string wrapped in quotes ie "..."
@@ -21,11 +17,7 @@ type encWriterI interface {
writen4([4]byte)
writen8([8]byte)
// isBytes() bool
end()
resetIO(w io.Writer, bufsize int, blist *bytesFreeList)
resetBytes(in []byte, out *[]byte)
}
// ---------------------------------------------
@@ -40,18 +32,16 @@ type bufioEncWriter struct {
b [16]byte // scratch buffer and padding (cache-aligned)
}
// MARKER: use setByteAt/byteAt to elide the bounds-checks
// when we are sure that we don't go beyond the bounds.
func (z *bufioEncWriter) resetBytes(in []byte, out *[]byte) {
halt.errorStr("resetBytes is unsupported by bufioEncWriter")
}
func (z *bufioEncWriter) resetIO(w io.Writer, bufsize int, blist *bytesFreeList) {
func (z *bufioEncWriter) reset(w io.Writer, bufsize int, blist *bytesFreelist) {
z.w = w
z.n = 0
// use minimum bufsize of 16, matching the array z.b and accomodating writen methods (where n <= 8)
bufsize = max(16, bufsize) // max(byteBufSize, bufsize)
if bufsize <= 0 {
bufsize = defEncByteBufSize
}
// bufsize must be >= 8, to accomodate writen methods (where n <= 8)
if bufsize <= 8 {
bufsize = 8
}
if cap(z.buf) < bufsize {
if len(z.buf) > 0 && &z.buf[0] != &z.b[0] {
blist.put(z.buf)
@@ -66,19 +56,17 @@ func (z *bufioEncWriter) resetIO(w io.Writer, bufsize int, blist *bytesFreeList)
}
func (z *bufioEncWriter) flushErr() (err error) {
var n int
for i := maxConsecutiveEmptyReads; i > 0; i-- {
n, err = z.w.Write(z.buf[:z.n])
z.n -= n
if z.n == 0 || err != nil {
return
n, err := z.w.Write(z.buf[:z.n])
z.n -= n
if z.n > 0 {
if err == nil {
err = io.ErrShortWrite
}
// at this point: z.n > 0 && err == nil
if n > 0 {
copy(z.buf, z.buf[n:z.n+n])
}
}
return io.ErrShortWrite // OR io.ErrNoProgress: not enough (or no) data written
return err
}
func (z *bufioEncWriter) flush() {
@@ -143,7 +131,6 @@ func (z *bufioEncWriter) writen1(b1 byte) {
// z.buf[z.n] = b1
z.n++
}
func (z *bufioEncWriter) writen2(b1, b2 byte) {
if 2 > len(z.buf)-z.n {
z.flush()
@@ -182,14 +169,8 @@ func (z *bufioEncWriter) endErr() (err error) {
return
}
func (z *bufioEncWriter) end() {
halt.onerror(z.endErr())
}
// ---------------------------------------------
var bytesEncAppenderDefOut = []byte{}
// bytesEncAppender implements encWriter and can write to an byte slice.
type bytesEncAppender struct {
b []byte
@@ -222,18 +203,122 @@ func (z *bytesEncAppender) writen4(b [4]byte) {
func (z *bytesEncAppender) writen8(b [8]byte) {
z.b = append(z.b, b[:]...)
// z.b = append(z.b, b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7])
// z.b = append(z.b, b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]) // prevents inlining encWr.writen4
}
func (z *bytesEncAppender) end() {
func (z *bytesEncAppender) endErr() error {
*(z.out) = z.b
return nil
}
func (z *bytesEncAppender) resetBytes(in []byte, out *[]byte) {
func (z *bytesEncAppender) reset(in []byte, out *[]byte) {
z.b = in[:0]
z.out = out
}
func (z *bytesEncAppender) resetIO(w io.Writer, bufsize int, blist *bytesFreeList) {
halt.errorStr("resetIO is unsupported by bytesEncAppender")
// --------------------------------------------------
type encWr struct {
wb bytesEncAppender
wf *bufioEncWriter
bytes bool // encoding to []byte
// MARKER: these fields below should belong directly in Encoder.
// we pack them here for space efficiency and cache-line optimization.
js bool // is json encoder?
be bool // is binary encoder?
c containerState
calls uint16
seq uint16 // sequencer (e.g. used by binc for symbols, etc)
}
// MARKER: manually inline bytesEncAppender.writenx/writeqstr methods,
// as calling them causes encWr.writenx/writeqstr methods to not be inlined (cost > 80).
//
// i.e. e.g. instead of writing z.wb.writen2(b1, b2), use z.wb.b = append(z.wb.b, b1, b2)
func (z *encWr) writeb(s []byte) {
if z.bytes {
z.wb.writeb(s)
} else {
z.wf.writeb(s)
}
}
func (z *encWr) writestr(s string) {
if z.bytes {
z.wb.writestr(s)
} else {
z.wf.writestr(s)
}
}
// MARKER: Add WriteStr to be called directly by generated code without a genHelper forwarding function.
// Go's inlining model adds cost for forwarding functions, preventing inlining (cost goes above 80 budget).
func (z *encWr) WriteStr(s string) {
if z.bytes {
z.wb.writestr(s)
} else {
z.wf.writestr(s)
}
}
func (z *encWr) writen1(b1 byte) {
if z.bytes {
z.wb.writen1(b1)
} else {
z.wf.writen1(b1)
}
}
func (z *encWr) writen2(b1, b2 byte) {
if z.bytes {
// MARKER: z.wb.writen2(b1, b2)
z.wb.b = append(z.wb.b, b1, b2)
} else {
z.wf.writen2(b1, b2)
}
}
func (z *encWr) writen4(b [4]byte) {
if z.bytes {
// MARKER: z.wb.writen4(b1, b2, b3, b4)
z.wb.b = append(z.wb.b, b[:]...)
// z.wb.writen4(b)
} else {
z.wf.writen4(b)
}
}
func (z *encWr) writen8(b [8]byte) {
if z.bytes {
// z.wb.b = append(z.wb.b, b[:]...)
z.wb.writen8(b)
} else {
z.wf.writen8(b)
}
}
func (z *encWr) writeqstr(s string) {
if z.bytes {
// MARKER: z.wb.writeqstr(s)
z.wb.b = append(append(append(z.wb.b, '"'), s...), '"')
} else {
z.wf.writeqstr(s)
}
}
func (z *encWr) endErr() error {
if z.bytes {
return z.wb.endErr()
}
return z.wf.endErr()
}
func (z *encWr) end() {
halt.onerror(z.endErr())
}
var _ encWriter = (*encWr)(nil)