diff --git a/go.mod b/go.mod index 7fc0fbd2..24da9a66 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,8 @@ require ( github.com/bombsimon/logrusr/v2 v2.0.1 github.com/docker/docker v24.0.9+incompatible github.com/docker/go-connections v0.4.0 - github.com/gin-contrib/cors v1.4.0 - github.com/gin-gonic/gin v1.9.0 + github.com/gin-contrib/cors v1.6.0 + github.com/gin-gonic/gin v1.9.1 github.com/go-logr/logr v1.2.4 github.com/go-redis/redis/v8 v8.11.5 github.com/golang-jwt/jwt/v4 v4.5.0 @@ -16,11 +16,11 @@ require ( github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.15.1 github.com/sirupsen/logrus v1.9.0 - github.com/stretchr/testify v1.8.2 + github.com/stretchr/testify v1.8.4 github.com/swaggo/files v1.0.1 github.com/swaggo/gin-swagger v1.6.0 github.com/swaggo/swag v1.16.1 - golang.org/x/crypto v0.17.0 + golang.org/x/crypto v0.21.0 golang.org/x/oauth2 v0.8.0 golang.org/x/time v0.3.0 gopkg.in/yaml.v2 v2.4.0 @@ -37,15 +37,17 @@ require ( github.com/KyleBanks/depth v1.2.1 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bytedance/sonic v1.8.8 // indirect + github.com/bytedance/sonic v1.11.2 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect + github.com/chenzhuoyu/iasm v0.9.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/docker/distribution v2.8.2+incompatible // indirect github.com/docker/go-units v0.5.0 // indirect github.com/emicklei/go-restful/v3 v3.10.2 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect @@ -53,7 +55,7 @@ require ( github.com/go-openapi/swag v0.22.3 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.13.0 // indirect + github.com/go-playground/validator/v10 v10.19.0 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect @@ -69,10 +71,10 @@ require ( github.com/jinzhu/now v1.1.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.2.4 // indirect - github.com/leodido/go-urn v1.2.4 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/moby/spdystream v0.2.0 // indirect github.com/moby/term v0.5.0 // indirect @@ -83,23 +85,23 @@ require ( github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.0.7 // indirect + github.com/pelletier/go-toml/v2 v2.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.43.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.2.11 // indirect - golang.org/x/arch v0.3.0 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + golang.org/x/arch v0.7.0 // indirect golang.org/x/mod v0.10.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/term v0.15.0 // indirect + golang.org/x/net v0.22.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.9.1 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.30.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.4.0 // indirect diff --git a/go.sum b/go.sum index 38404d11..e9ad5d31 100644 --- a/go.sum +++ b/go.sum @@ -15,15 +15,20 @@ github.com/bombsimon/logrusr/v2 v2.0.1 h1:1VgxVNQMCvjirZIYaT9JYn6sAVGVEcNtRE0y4m github.com/bombsimon/logrusr/v2 v2.0.1/go.mod h1:ByVAX+vHdLGAfdroiMg6q0zgq2FODY2lc5YJvzmOJio= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.8.8 h1:Kj4AYbZSeENfyXicsYppYKO0K2YWab+i2UTSY7Ukz9Q= -github.com/bytedance/sonic v1.8.8/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= +github.com/bytedance/sonic v1.11.2 h1:ywfwo0a/3j9HR8wsYGWsIWl2mvRsI950HyoxiBERw5A= +github.com/bytedance/sonic v1.11.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= +github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= +github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0= +github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -56,15 +61,16 @@ github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJ github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/cors v1.4.0 h1:oJ6gwtUl3lqV0WEIwM/LxPF1QZ5qe2lGWdY2+bz7y0g= -github.com/gin-contrib/cors v1.4.0/go.mod h1:bs9pNM0x/UsmHPBWT2xZz9ROh8xYjYkiURUfmBoMlcs= +github.com/gin-contrib/cors v1.6.0 h1:0Z7D/bVhE6ja07lI8CTjTonp6SB07o8bNuFyRbsBUQg= +github.com/gin-contrib/cors v1.6.0/go.mod h1:cI+h6iOAyxKRtUtC6iF/Si1KSFvGm/gK+kshxlCi8ro= github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= -github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= -github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/go-logr/logr v1.0.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= @@ -83,20 +89,15 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/go-playground/validator/v10 v10.13.0 h1:cFRQdfaSMCOSfGCCLB20MHvuoHb/s5G8L5pu2ppK5AQ= -github.com/go-playground/validator/v10 v10.13.0/go.mod h1:dwu7+CG8/CtBiJFZDz4e+5Upb6OLw04gtBYw0mcG/z4= +github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= +github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= -github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -166,8 +167,9 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= -github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -178,17 +180,15 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= -github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= @@ -215,10 +215,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= -github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us= -github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= +github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -235,7 +233,6 @@ github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJf github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= @@ -256,8 +253,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M= @@ -266,10 +263,8 @@ github.com/swaggo/swag v1.16.1 h1:fTNRhKstPKxcnoKsytm4sahr8FaYzUcT7i1/3nd/fBg= github.com/swaggo/swag v1.16.1/go.mod h1:9/LMvHycG3NFHfR6LwvikHv5iFvmPADQ359cKikGxto= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= -github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= -github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= -github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= @@ -281,16 +276,15 @@ go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= -golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= +golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -317,8 +311,8 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= @@ -345,21 +339,18 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210608053332-aa57babbf139/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -416,9 +407,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -463,6 +453,7 @@ k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5F k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= k8s.io/utils v0.0.0-20230505201702-9f6742963106 h1:EObNQ3TW2D+WptiYXlApGNLVy0zm/JIBVY9i+M4wpAU= k8s.io/utils v0.0.0-20230505201702-9f6742963106/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= sigs.k8s.io/controller-runtime v0.14.6 h1:oxstGVvXGNnMvY7TAESYk+lzr6S3V5VFxQ6d92KcwQA= sigs.k8s.io/controller-runtime v0.14.6/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= diff --git a/vendor/github.com/bytedance/sonic/INTRODUCTION.md b/vendor/github.com/bytedance/sonic/INTRODUCTION.md deleted file mode 100644 index 6b42118a..00000000 --- a/vendor/github.com/bytedance/sonic/INTRODUCTION.md +++ /dev/null @@ -1,48 +0,0 @@ -# Introduction to Sonic -## Background -According to the overall profiling of production services in Bytedance, we found that the overhead of JSON serialization and deserialization is unexpectedly high: the total is near to 10% CPU, and the extreme one accounts for more than 40% CPU. Therefore, **the performance of JSON lib is a key issue for the promotion of machine utilization**. - -## Research -We conducted a series of surveys and benchmarks on open-sourced JSON libraries for Golang, but the result is disappointing: **no silver bullet**. First of all, no one can perform at least the top three across various business scenarios. Even the most widely used [json-iterator](https://github.com/json-iterator/go) will severely degrade in generic (no-schema) or big-volume JSON serialization and deserialization. Secondly, compared with other JSON libraries writing in other languages, their speed is generally much slower. For example, [Simdjson-go](https://github.com/minio/simdjson-go) has a 50% reduction in decoding performance compared to [simdjson](https://github.com/simdjson/simdjson). What's more, we barely found JSON libraries which provide API to modify the underlying values. - -Therefore, we decided to **develop a brand-new JSON library with high performance as well as wide applicability**. - -## Thinking -Before starting our design, we need to figure out some questions: - -### Why is Json-iterator faster than Standard Library? -First of all, the **schema-based processing mechanism** used by the standard library is commendable, in which the parser can obtain meta information in advance when scanning, thereby shortening the time of branch selection. However, its original implementation did not make good use of this mechanism, instead, **it spent a lot of time reflecting to obtain meta info of schema**. Meanwhile, The approach of json-iterator is: Interprete structure as field-by-field encoding and decoding functions, and then assembled and cached them, minimizing the performance loss cost by reflection. But does it work once and for all? No. In practical tests, we found that **the deeper and larger the input JSON got, the smaller the gap between json-iterator and other libraries gradually became** - eventually event got surpassed: -![Scalability](introduction-1.png) - -The reason is that **this implementation transforms into a large number of interface encapsulations and function calls**, followed by function-call losses: -1. **Calling interface involves dynamic addressing of itab** -2. **Assembly functions cannot be inlined**, while Golang's function-call performance is poor (no parameter-passing-by-register) - -#### Is there a way to avoid the function-call overhead of dynamic assembly? -The first thing we thought about was code generation like [easyjson](https://github.com/mailru/easyjson). But it comes with **schema dependency and convenience losses**. To achieve a real drop-in replacement of the standard library, we turned to another technology - **[JIT](https://en.wikipedia.org/wiki/Jit) (just-in-time compiling)**. Because the compiled codec function is an integrated function, which can greatly reduce function calls while ensuring flexibility. - -### Why is Simdjson-go not fast enough? -[SIMD](https://en.wikipedia.org/wiki/SIMD) (Single-Instruction-Multi-Data) is a special set of CPU instructions for the parallel processing of vectorized data. At present, it is supported by most CPUs and widely used in image processing and big data computing. Undoubtedly, SIMD is useful in JSON processing (itoa, char-search, and so on are all suitable scenarios). We can see that simdjson-go is very competitive in large JSON scenarios (>100KB). However, for some extremely small or irregular character strings, **the extra load operation required by SIMD will lead to performance degradation**. Therefore, we need to dedicate ourselves to branch predicting and decide which scenarios should use SIMD and which should not (for example, the string length is less than 16 bytes). - -The second problem comes from the Go compiler itself. In order to ensure the compilation speed, **Golang does very little optimization work during the compilation phase** and cannot directly use compiler backends such as [LLVM](https://en.wikipedia.org/wiki/LLVM) (Low-Level Virtual Machine) for optimization. - -So, **can some crucial calculation functions be written in another language with higher execution efficiency**? -C/Clang is an ideal compilation tool (internal integration LLVM). But the key is how to embed the optimized assembly into Golang. - -### How to use Gjson well? -We also found that [gjson](https://github.com/tidwall/gjson) has a huge advantage in single-key lookup scenarios. This is because its lookup is implemented by a **lazy-load mechanism**, which subtlely skips passing-by values and effectively reduces a lot of unnecessary parsing. Practical application has proved that making good use of this feature in product can indeed bring benefits. But when it comes to multi-key lookup, Gjson does worse event than std, which is a side effect of its skipping mechanism - **searching for the same path leads to repeated parsing** (skip is also a lightweight parsing). Therefore, the accurate adaptation of practical scenarios is the key. - -## Design -Based on the above questions, our design is easy to implement: - -1. Aiming at the function-call overhead cost by the codec dynamic-assembly, **`JIT` tech is used to assemble opcodes (asm) corresponding to the schema at runtime**, which is finally cached into the off-heap memory in the form of Golang functions. -2. For practical scenarios where big data and small data coexist, we **use pre-conditional judgment** (string size, floating precision, etc.) **to combine `SIMD` with scalar instructions** to achieve the best adaptation. -3. As for insufficiency in compiling optimization of go language, we decided to **use `C/Clang` to write and compile core computational functions**, and **developed a set of [asm2asm](https://github.com/chenzhuoyu/asm2asm) tools to translate the fully optimized x86 assembly into plan9** and finally load it into Golang runtime. -4. Giving the big speed gap between parsing and skipping, the **`lazy-load` mechanism** is certainly used in our AST parser, but in **a more adaptive and efficient way to reduce the overhead of multiple-key queries**. -![design](introduction-2.png) - -In detail, we conducted some further optimization: -1. Since the native-asm functions cannot be inlined in Golang, we found that its cost even exceeded the improvement brought by the optimization of the C compiler. So we reimplemented a set of lightweight function-calls in JIT: - - `Global-function-table + static offset` for calling instruction - - **Pass parameters using registers** -2. `Sync.Map` was used to cache the codecs at first, but for our **quasi-static** (read far more than write), **fewer elements** (usually no more than a few dozen) scenarios, its performance is not optimal, so we reimplement a high-performance and concurrent-safe cache with `open-addressing-hash + RCU` tech. \ No newline at end of file diff --git a/vendor/github.com/bytedance/sonic/Makefile b/vendor/github.com/bytedance/sonic/Makefile index 8cc0acf1..c672c313 100644 --- a/vendor/github.com/bytedance/sonic/Makefile +++ b/vendor/github.com/bytedance/sonic/Makefile @@ -23,12 +23,12 @@ CPU_avx := amd64 CPU_avx2 := amd64 CPU_sse := amd64 -TMPL_avx := fastint_amd64_test fastfloat_amd64_test native_amd64_test native_export_amd64 -TMPL_avx2 := fastint_amd64_test fastfloat_amd64_test native_amd64_test native_export_amd64 -TMPL_sse := fastint_amd64_test fastfloat_amd64_test native_amd64_test native_export_amd64 +TMPL_avx := fastint_amd64_test fastfloat_amd64_test native_amd64_test recover_amd64_test +TMPL_avx2 := fastint_amd64_test fastfloat_amd64_test native_amd64_test recover_amd64_test +TMPL_sse := fastint_amd64_test fastfloat_amd64_test native_amd64_test recover_amd64_test -CFLAGS_avx := -msse -mno-sse4 -mavx -mpclmul -mno-avx2 -DUSE_AVX=1 -DUSE_AVX2=0 -CFLAGS_avx2 := -msse -mno-sse4 -mavx -mpclmul -mavx2 -DUSE_AVX=1 -DUSE_AVX2=1 +CFLAGS_avx := -msse -mno-sse4 -mavx -mpclmul -mno-avx2 -mstack-alignment=0 -DUSE_AVX=1 -DUSE_AVX2=0 +CFLAGS_avx2 := -msse -mno-sse4 -mavx -mpclmul -mavx2 -mstack-alignment=0 -DUSE_AVX=1 -DUSE_AVX2=1 CFLAGS_sse := -msse -mno-sse4 -mno-avx -mno-avx2 -mpclmul CC_amd64 := clang @@ -66,7 +66,7 @@ define build_arch $(eval @cpu := $(value CPU_$(1))) $(eval @deps := $(foreach tmpl,$(value TMPL_$(1)),${OUT_DIR}/$(1)/${tmpl}.go)) $(eval @asmin := ${TMP_DIR}/$(1)/native.s) - $(eval @asmout := ${OUT_DIR}/$(1)/native_${@cpu}.s) + $(eval @asmout := ${OUT_DIR}/$(1)/native_text_${@cpu}.go) $(eval @stubin := ${OUT_DIR}/native_${@cpu}.tmpl) $(eval @stubout := ${OUT_DIR}/$(1)/native_${@cpu}.go) @@ -75,8 +75,7 @@ $(1): ${@asmout} ${@deps} ${@asmout}: ${@stubout} ${NATIVE_SRC} mkdir -p ${TMP_DIR}/$(1) $${CC_${@cpu}} $${CFLAGS} $${CFLAGS_$(1)} -S -o ${TMP_DIR}/$(1)/native.s ${SRC_FILE} - python3 $${ASM2ASM_${@cpu}} ${@asmout} ${TMP_DIR}/$(1)/native.s - asmfmt -w ${@asmout} + python3 $${ASM2ASM_${@cpu}} -r ${@stubout} ${TMP_DIR}/$(1)/native.s $(eval $(call \ build_tmpl, \ diff --git a/vendor/github.com/bytedance/sonic/README.md b/vendor/github.com/bytedance/sonic/README.md index 9cc42c46..a74fc496 100644 --- a/vendor/github.com/bytedance/sonic/README.md +++ b/vendor/github.com/bytedance/sonic/README.md @@ -1,20 +1,31 @@ # Sonic +English | [中文](README_ZH_CN.md) + A blazingly fast JSON serializing & deserializing library, accelerated by JIT (just-in-time compiling) and SIMD (single-instruction-multiple-data). ## Requirement -- Go 1.15~1.20 -- Linux/MacOS/Windows + +- Go 1.16~1.22 +- Linux / MacOS / Windows(need go1.17 above) - Amd64 ARCH ## Features + - Runtime object binding without code generation - Complete APIs for JSON value manipulation - Fast, fast, fast! +## APIs + +see [go.dev](https://pkg.go.dev/github.com/bytedance/sonic) + ## Benchmarks + For **all sizes** of json and **all scenarios** of usage, **Sonic performs best**. + - [Medium](https://github.com/bytedance/sonic/blob/main/decoder/testdata_test.go#L19) (13KB, 300+ key, 6 layers) + ```powershell goversion: 1.17.1 goos: darwin @@ -74,22 +85,29 @@ BenchmarkSetOne_Jsoniter-16 79475 ns/op 163.8 BenchmarkSetOne_Parallel_Sonic-16 850.9 ns/op 15305.31 MB/s 1584 B/op 17 allocs/op BenchmarkSetOne_Parallel_Sjson-16 18194 ns/op 715.77 MB/s 52247 B/op 9 allocs/op BenchmarkSetOne_Parallel_Jsoniter-16 33560 ns/op 388.05 MB/s 45892 B/op 964 allocs/op +BenchmarkLoadNode/LoadAll()-16 11384 ns/op 1143.93 MB/s 6307 B/op 25 allocs/op +BenchmarkLoadNode_Parallel/LoadAll()-16 5493 ns/op 2370.68 MB/s 7145 B/op 25 allocs/op +BenchmarkLoadNode/Interface()-16 17722 ns/op 734.85 MB/s 13323 B/op 88 allocs/op +BenchmarkLoadNode_Parallel/Interface()-16 10330 ns/op 1260.70 MB/s 15178 B/op 88 allocs/op ``` + - [Small](https://github.com/bytedance/sonic/blob/main/testdata/small.go) (400B, 11 keys, 3 layers) -![small benchmarks](bench-small.png) +![small benchmarks](./docs/imgs/bench-small.png) - [Large](https://github.com/bytedance/sonic/blob/main/testdata/twitter.json) (635KB, 10000+ key, 6 layers) -![large benchmarks](bench-large.png) +![large benchmarks](./docs/imgs/bench-large.png) -See [bench.sh](https://github.com/bytedance/sonic/blob/main/bench.sh) for benchmark codes. +See [bench.sh](https://github.com/bytedance/sonic/blob/main/scripts/bench.sh) for benchmark codes. ## How it works -See [INTRODUCTION.md](INTRODUCTION.md). + +See [INTRODUCTION.md](./docs/INTRODUCTION.md). ## Usage ### Marshal/Unmarshal Default behaviors are mostly consistent with `encoding/json`, except HTML escaping form (see [Escape HTML](https://github.com/bytedance/sonic/blob/main/README.md#escape-html)) and `SortKeys` feature (optional support see [Sort Keys](https://github.com/bytedance/sonic/blob/main/README.md#sort-keys)) that is **NOT** in conformity to [RFC8259](https://datatracker.ietf.org/doc/html/rfc8259). + ```go import "github.com/bytedance/sonic" @@ -101,34 +119,41 @@ err := sonic.Unmarshal(output, &data) ``` ### Streaming IO -Sonic supports decoding json from `io.Reader` or encoding objects into `io.`Writer`, aims at handling multiple values as well as reducing memory consumption. + +Sonic supports decoding json from `io.Reader` or encoding objects into `io.Writer`, aims at handling multiple values as well as reducing memory consumption. + - encoder -```go -import "github.com/bytedance/sonic/encoder" +```go var o1 = map[string]interface{}{ "a": "b", } var o2 = 1 var w = bytes.NewBuffer(nil) -var enc = encoder.NewStreamEncoder(w) +var enc = sonic.ConfigDefault.NewEncoder(w) enc.Encode(o1) enc.Encode(o2) -println(w.String()) // "{"a":"b"}\n1" +fmt.Println(w.String()) +// Output: +// {"a":"b"} +// 1 ``` + - decoder -```go -import "github.com/bytedance/sonic/decoder" +```go var o = map[string]interface{}{} var r = strings.NewReader(`{"a":"b"}{"1":"2"}`) -var dec = decoder.NewStreamDecoder(r) +var dec = sonic.ConfigDefault.NewDecoder(r) dec.Decode(&o) dec.Decode(&o) -fmt.Printf("%+v", o) // map[1:2 a:b] +fmt.Printf("%+v", o) +// Output: +// map[1:2 a:b] ``` ### Use Number/Use Int64 + ```go import "github.com/bytedance/sonic/decoder" @@ -157,7 +182,9 @@ fm := root.Interface().(float64) // jn == jm ``` ### Sort Keys + On account of the performance loss from sorting (roughly 10%), sonic doesn't enable this feature by default. If your component depends on it to work (like [zstd](https://github.com/facebook/zstd)), Use it like this: + ```go import "github.com/bytedance/sonic" import "github.com/bytedance/sonic/encoder" @@ -170,19 +197,26 @@ v, err := encoder.Encode(m, encoder.SortMapKeys) var root := sonic.Get(JSON) err := root.SortKeys() ``` + ### Escape HTML + On account of the performance loss (roughly 15%), sonic doesn't enable this feature by default. You can use `encoder.EscapeHTML` option to open this feature (align with `encoding/json.HTMLEscape`). + ```go import "github.com/bytedance/sonic" v := map[string]string{"&&":"<>"} ret, err := Encode(v, EscapeHTML) // ret == `{"\u0026\u0026":{"X":"\u003c\u003e"}}` ``` + ### Compact Format + Sonic encodes primitive objects (struct/map...) as compact-format JSON by default, except marshaling `json.RawMessage` or `json.Marshaler`: sonic ensures validating their output JSON but **DONOT** compacting them for performance concerns. We provide the option `encoder.CompactMarshaler` to add compacting process. ### Print Error + If there invalid syntax in input JSON, sonic will return `decoder.SyntaxError`, which supports pretty-printing of error position + ```go import "github.com/bytedance/sonic" import "github.com/bytedance/sonic/decoder" @@ -208,7 +242,9 @@ if err != nil { ``` #### Mismatched Types [Sonic v1.6.0] + If there a **mismatch-typed** value for a given key, sonic will report `decoder.MismatchTypeError` (if there are many, report the last one), but still skip wrong the value and keep decoding next JSON. + ```go import "github.com/bytedance/sonic" import "github.com/bytedance/sonic/decoder" @@ -221,10 +257,15 @@ err := UnmarshalString(`{"A":"1","B":1}`, &data) println(err.Error()) // Mismatch type int with value string "at index 5: mismatched type with value\n\n\t{\"A\":\"1\",\"B\":1}\n\t.....^.........\n" fmt.Printf("%+v", data) // {A:0 B:1} ``` + ### Ast.Node + Sonic/ast.Node is a completely self-contained AST for JSON. It implements serialization and deserialization both and provides robust APIs for obtaining and modification of generic data. + #### Get/Index + Search partial JSON by given paths, which must be non-negative integer or string, or nil + ```go import "github.com/bytedance/sonic" @@ -238,10 +279,13 @@ raw := root.Raw() // == string(input) root, err := sonic.Get(input, "key1", 1, "key2") sub := root.Get("key3").Index(2).Int64() // == 3 ``` + **Tip**: since `Index()` uses offset to locate data, which is much faster than scanning like `Get()`, we suggest you use it as much as possible. And sonic also provides another API `IndexOrGet()` to underlying use offset as well as ensure the key is matched. #### Set/Unset + Modify the json content by Set()/Unset() + ```go import "github.com/bytedance/sonic" @@ -258,7 +302,9 @@ println(root.Get("key4").Check()) // "value not exist" ``` #### Serialize + To encode `ast.Node` as json, use `MarshalJson()` or `json.Marshal()` (MUST pass the node's pointer) + ```go import ( "encoding/json" @@ -272,6 +318,7 @@ println(string(buf) == string(exp)) // true ``` #### APIs + - validation: `Check()`, `Error()`, `Valid()`, `Exist()` - searching: `Index()`, `Get()`, `IndexPair()`, `IndexOrGet()`, `GetByPath()` - go-type casting: `Int64()`, `Float64()`, `String()`, `Number()`, `Bool()`, `Map[UseNumber|UseNode]()`, `Array[UseNumber|UseNode]()`, `Interface[UseNumber|UseNode]()` @@ -279,13 +326,55 @@ println(string(buf) == string(exp)) // true - iteration: `Values()`, `Properties()`, `ForEach()`, `SortKeys()` - modification: `Set()`, `SetByIndex()`, `Add()` +### Ast.Visitor + +Sonic provides an advanced API for fully parsing JSON into non-standard types (neither `struct` not `map[string]interface{}`) without using any intermediate representation (`ast.Node` or `interface{}`). For example, you might have the following types which are like `interface{}` but actually not `interface{}`: + +```go +type UserNode interface {} + +// the following types implement the UserNode interface. +type ( + UserNull struct{} + UserBool struct{ Value bool } + UserInt64 struct{ Value int64 } + UserFloat64 struct{ Value float64 } + UserString struct{ Value string } + UserObject struct{ Value map[string]UserNode } + UserArray struct{ Value []UserNode } +) +``` + +Sonic provides the following API to return **the preorder traversal of a JSON AST**. The `ast.Visitor` is a SAX style interface which is used in some C++ JSON library. You should implement `ast.Visitor` by yourself and pass it to `ast.Preorder()` method. In your visitor you can make your custom types to represent JSON values. There may be an O(n) space container (such as stack) in your visitor to record the object / array hierarchy. + +```go +func Preorder(str string, visitor Visitor, opts *VisitorOptions) error + +type Visitor interface { + OnNull() error + OnBool(v bool) error + OnString(v string) error + OnInt64(v int64, n json.Number) error + OnFloat64(v float64, n json.Number) error + OnObjectBegin(capacity int) error + OnObjectKey(key string) error + OnObjectEnd() error + OnArrayBegin(capacity int) error + OnArrayEnd() error +} +``` + +See [ast/visitor.go](https://github.com/bytedance/sonic/blob/main/ast/visitor.go) for detailed usage. We also implement a demo visitor for `UserNode` in [ast/visitor_test.go](https://github.com/bytedance/sonic/blob/main/ast/visitor_test.go). + ## Compatibility + Sonic **DOES NOT** ensure to support all environments, due to the difficulty of developing high-performance codes. For developers who use sonic to build their applications in different environments, we have the following suggestions: - Developing on **Mac M1**: Make sure you have Rosetta 2 installed on your machine, and set `GOARCH=amd64` when building your application. Rosetta 2 can automatically translate x86 binaries to arm64 binaries and run x86 applications on Mac M1. - Developing on **Linux arm64**: You can install qemu and use the `qemu-x86_64 -cpu max` command to convert x86 binaries to amr64 binaries for applications built with sonic. The qemu can achieve a similar transfer effect to Rosetta 2 on Mac M1. For developers who want to use sonic on Linux arm64 without qemu, or those who want to handle JSON strictly consistent with `encoding/json`, we provide some compatible APIs as `sonic.API` + - `ConfigDefault`: the sonic's default config (`EscapeHTML=false`,`SortKeys=false`...) to run on sonic-supporting environment. It will fall back to `encoding/json` with the corresponding config, and some options like `SortKeys=false` will be invalid. - `ConfigStd`: the std-compatible config (`EscapeHTML=true`,`SortKeys=true`...) to run on sonic-supporting environment. It will fall back to `encoding/json`. - `ConfigFastest`: the fastest config (`NoQuoteTextMarshaler=true`) to run on sonic-supporting environment. It will fall back to `encoding/json` with the corresponding config, and some options will be invalid. @@ -293,7 +382,9 @@ For developers who want to use sonic on Linux arm64 without qemu, or those who w ## Tips ### Pretouch + Since Sonic uses [golang-asm](https://github.com/twitchyliquid64/golang-asm) as a JIT assembler, which is NOT very suitable for runtime compiling, first-hit running of a huge schema may cause request-timeout or even process-OOM. For better stability, we advise **using `Pretouch()` for huge-schema or compact-memory applications** before `Marshal()/Unmarshal()`. + ```go import ( "reflect" @@ -308,7 +399,7 @@ func init() { err := sonic.Pretouch(reflect.TypeOf(v)) // with more CompileOption... - err := sonic.Pretouch(reflect.TypeOf(v), + err := sonic.Pretouch(reflect.TypeOf(v), // If the type is too deep nesting (nesting depth > option.DefaultMaxInlineDepth), // you can set compile recursive loops in Pretouch for better stability in JIT. option.WithCompileRecursiveDepth(loop), @@ -319,17 +410,23 @@ func init() { ``` ### Copy string -When decoding **string values without any escaped characters**, sonic references them from the origin JSON buffer instead of mallocing a new buffer to copy. This helps a lot for CPU performance but may leave the whole JSON buffer in memory as long as the decoded objects are being used. In practice, we found the extra memory introduced by referring JSON buffer is usually 20% ~ 80% of decoded objects. Once an application holds these objects for a long time (for example, cache the decoded objects for reusing), its in-use memory on the server may go up. We provide the option `decoder.CopyString()` for users to choose not to reference the JSON buffer, which may cause a decline in CPU performance to some degree. + +When decoding **string values without any escaped characters**, sonic references them from the origin JSON buffer instead of mallocing a new buffer to copy. This helps a lot for CPU performance but may leave the whole JSON buffer in memory as long as the decoded objects are being used. In practice, we found the extra memory introduced by referring JSON buffer is usually 20% ~ 80% of decoded objects. Once an application holds these objects for a long time (for example, cache the decoded objects for reusing), its in-use memory on the server may go up. - `Config.CopyString`/`decoder.CopyString()`: We provide the option for `Decode()` / `Unmarshal()` users to choose not to reference the JSON buffer, which may cause a decline in CPU performance to some degree. + +- `GetFromStringNoCopy()`: For memory safty, `sonic.Get()` / `sonic.GetFromString()` now copies return JSON. If users want to get json more quickly and not care about memory usage, you can use `GetFromStringNoCopy()` to return a JSON direclty referenced from source. ### Pass string or []byte? + For alignment to `encoding/json`, we provide API to pass `[]byte` as an argument, but the string-to-bytes copy is conducted at the same time considering safety, which may lose performance when the origin JSON is huge. Therefore, you can use `UnmarshalString()` and `GetFromString()` to pass a string, as long as your origin data is a string or **nocopy-cast** is safe for your []byte. We also provide API `MarshalString()` for convenient **nocopy-cast** of encoded JSON []byte, which is safe since sonic's output bytes is always duplicated and unique. ### Accelerate `encoding.TextMarshaler` -To ensure data security, sonic.Encoder quotes and escapes string values from `encoding.TextMarshaler` interfaces by default, which may degrade performance much if most of your data is in form of them. We provide `encoder.NoQuoteTextMarshaler` to skip these operations, which means you **MUST** ensure their output string escaped and quoted following [RFC8259](https://datatracker.ietf.org/doc/html/rfc8259). +To ensure data security, sonic.Encoder quotes and escapes string values from `encoding.TextMarshaler` interfaces by default, which may degrade performance much if most of your data is in form of them. We provide `encoder.NoQuoteTextMarshaler` to skip these operations, which means you **MUST** ensure their output string escaped and quoted following [RFC8259](https://datatracker.ietf.org/doc/html/rfc8259). ### Better performance for generic data + In **fully-parsed** scenario, `Unmarshal()` performs better than `Get()`+`Node.Interface()`. But if you only have a part of the schema for specific json, you can combine `Get()` and `Unmarshal()` together: + ```go import "github.com/bytedance/sonic" @@ -337,7 +434,9 @@ node, err := sonic.GetFromString(_TwitterJson, "statuses", 3, "user") var user User // your partial schema... err = sonic.UnmarshalString(node.Raw(), &user) ``` + Even if you don't have any schema, use `ast.Node` as the container of generic values instead of `map` or `interface`: + ```go import "github.com/bytedance/sonic" @@ -348,12 +447,25 @@ err = user.Check() // err = user.LoadAll() // only call this when you want to use 'user' concurrently... go someFunc(user) ``` + Why? Because `ast.Node` stores its children using `array`: + - `Array`'s performance is **much better** than `Map` when Inserting (Deserialize) and Scanning (Serialize) data; - **Hashing** (`map[x]`) is not as efficient as **Indexing** (`array[x]`), which `ast.Node` can conduct on **both array and object**; - Using `Interface()`/`Map()` means Sonic must parse all the underlying values, while `ast.Node` can parse them **on demand**. **CAUTION:** `ast.Node` **DOESN'T** ensure concurrent security directly, due to its **lazy-load** design. However, you can call `Node.Load()`/`Node.LoadAll()` to achieve that, which may bring performance reduction while it still works faster than converting to `map` or `interface{}` +### Ast.Node or Ast.Visitor? + +For generic data, `ast.Node` should be enough for your needs in most cases. + +However, `ast.Node` is designed for partially processing JSON string. It has some special designs such as lazy-load which might not be suitable for directly parsing the whole JSON string like `Unmarshal()`. Although `ast.Node` is better then `map` or `interface{}`, it's also a kind of intermediate representation after all if your final types are customized and you have to convert the above types to your custom types after parsing. + +For better performance, in previous case the `ast.Visitor` will be the better choice. It performs JSON decoding like `Unmarshal()` and you can directly use your final types to represents a JSON AST without any intermediate representations. + +But `ast.Visitor` is not a very handy API. You might need to write a lot of code to implement your visitor and carefully maintain the tree hierarchy during decoding. Please read the comments in [ast/visitor.go](https://github.com/bytedance/sonic/blob/main/ast/visitor.go) carefully if you decide to use this API. + ## Community + Sonic is a subproject of [CloudWeGo](https://www.cloudwego.io/). We are committed to building a cloud native ecosystem. diff --git a/vendor/github.com/bytedance/sonic/README_ZH_CN.md b/vendor/github.com/bytedance/sonic/README_ZH_CN.md new file mode 100644 index 00000000..d0341ab7 --- /dev/null +++ b/vendor/github.com/bytedance/sonic/README_ZH_CN.md @@ -0,0 +1,469 @@ +# Sonic + +[English](README.md) | 中文 + +一个速度奇快的 JSON 序列化/反序列化库,由 JIT (即时编译)和 SIMD (单指令流多数据流)加速。 + +## 依赖 + +- Go 1.16~1.22 +- Linux / MacOS / Windows(需要 Go1.17 以上) +- Amd64 架构 + +## 接口 + +详见 [go.dev](https://pkg.go.dev/github.com/bytedance/sonic) + +## 特色 + +- 运行时对象绑定,无需代码生成 +- 完备的 JSON 操作 API +- 快,更快,还要更快! + +## 基准测试 + +对于**所有大小**的 json 和**所有使用场景**, **Sonic 表现均为最佳**。 + +- [中型](https://github.com/bytedance/sonic/blob/main/decoder/testdata_test.go#L19) (13kB, 300+ 键, 6 层) + +```powershell +goversion: 1.17.1 +goos: darwin +goarch: amd64 +cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz +BenchmarkEncoder_Generic_Sonic-16 32393 ns/op 402.40 MB/s 11965 B/op 4 allocs/op +BenchmarkEncoder_Generic_Sonic_Fast-16 21668 ns/op 601.57 MB/s 10940 B/op 4 allocs/op +BenchmarkEncoder_Generic_JsonIter-16 42168 ns/op 309.12 MB/s 14345 B/op 115 allocs/op +BenchmarkEncoder_Generic_GoJson-16 65189 ns/op 199.96 MB/s 23261 B/op 16 allocs/op +BenchmarkEncoder_Generic_StdLib-16 106322 ns/op 122.60 MB/s 49136 B/op 789 allocs/op +BenchmarkEncoder_Binding_Sonic-16 6269 ns/op 2079.26 MB/s 14173 B/op 4 allocs/op +BenchmarkEncoder_Binding_Sonic_Fast-16 5281 ns/op 2468.16 MB/s 12322 B/op 4 allocs/op +BenchmarkEncoder_Binding_JsonIter-16 20056 ns/op 649.93 MB/s 9488 B/op 2 allocs/op +BenchmarkEncoder_Binding_GoJson-16 8311 ns/op 1568.32 MB/s 9481 B/op 1 allocs/op +BenchmarkEncoder_Binding_StdLib-16 16448 ns/op 792.52 MB/s 9479 B/op 1 allocs/op +BenchmarkEncoder_Parallel_Generic_Sonic-16 6681 ns/op 1950.93 MB/s 12738 B/op 4 allocs/op +BenchmarkEncoder_Parallel_Generic_Sonic_Fast-16 4179 ns/op 3118.99 MB/s 10757 B/op 4 allocs/op +BenchmarkEncoder_Parallel_Generic_JsonIter-16 9861 ns/op 1321.84 MB/s 14362 B/op 115 allocs/op +BenchmarkEncoder_Parallel_Generic_GoJson-16 18850 ns/op 691.52 MB/s 23278 B/op 16 allocs/op +BenchmarkEncoder_Parallel_Generic_StdLib-16 45902 ns/op 283.97 MB/s 49174 B/op 789 allocs/op +BenchmarkEncoder_Parallel_Binding_Sonic-16 1480 ns/op 8810.09 MB/s 13049 B/op 4 allocs/op +BenchmarkEncoder_Parallel_Binding_Sonic_Fast-16 1209 ns/op 10785.23 MB/s 11546 B/op 4 allocs/op +BenchmarkEncoder_Parallel_Binding_JsonIter-16 6170 ns/op 2112.58 MB/s 9504 B/op 2 allocs/op +BenchmarkEncoder_Parallel_Binding_GoJson-16 3321 ns/op 3925.52 MB/s 9496 B/op 1 allocs/op +BenchmarkEncoder_Parallel_Binding_StdLib-16 3739 ns/op 3486.49 MB/s 9480 B/op 1 allocs/op + +BenchmarkDecoder_Generic_Sonic-16 66812 ns/op 195.10 MB/s 57602 B/op 723 allocs/op +BenchmarkDecoder_Generic_Sonic_Fast-16 54523 ns/op 239.07 MB/s 49786 B/op 313 allocs/op +BenchmarkDecoder_Generic_StdLib-16 124260 ns/op 104.90 MB/s 50869 B/op 772 allocs/op +BenchmarkDecoder_Generic_JsonIter-16 91274 ns/op 142.81 MB/s 55782 B/op 1068 allocs/op +BenchmarkDecoder_Generic_GoJson-16 88569 ns/op 147.17 MB/s 66367 B/op 973 allocs/op +BenchmarkDecoder_Binding_Sonic-16 32557 ns/op 400.38 MB/s 28302 B/op 137 allocs/op +BenchmarkDecoder_Binding_Sonic_Fast-16 28649 ns/op 455.00 MB/s 24999 B/op 34 allocs/op +BenchmarkDecoder_Binding_StdLib-16 111437 ns/op 116.97 MB/s 10576 B/op 208 allocs/op +BenchmarkDecoder_Binding_JsonIter-16 35090 ns/op 371.48 MB/s 14673 B/op 385 allocs/op +BenchmarkDecoder_Binding_GoJson-16 28738 ns/op 453.59 MB/s 22039 B/op 49 allocs/op +BenchmarkDecoder_Parallel_Generic_Sonic-16 12321 ns/op 1057.91 MB/s 57233 B/op 723 allocs/op +BenchmarkDecoder_Parallel_Generic_Sonic_Fast-16 10644 ns/op 1224.64 MB/s 49362 B/op 313 allocs/op +BenchmarkDecoder_Parallel_Generic_StdLib-16 57587 ns/op 226.35 MB/s 50874 B/op 772 allocs/op +BenchmarkDecoder_Parallel_Generic_JsonIter-16 38666 ns/op 337.12 MB/s 55789 B/op 1068 allocs/op +BenchmarkDecoder_Parallel_Generic_GoJson-16 30259 ns/op 430.79 MB/s 66370 B/op 974 allocs/op +BenchmarkDecoder_Parallel_Binding_Sonic-16 5965 ns/op 2185.28 MB/s 27747 B/op 137 allocs/op +BenchmarkDecoder_Parallel_Binding_Sonic_Fast-16 5170 ns/op 2521.31 MB/s 24715 B/op 34 allocs/op +BenchmarkDecoder_Parallel_Binding_StdLib-16 27582 ns/op 472.58 MB/s 10576 B/op 208 allocs/op +BenchmarkDecoder_Parallel_Binding_JsonIter-16 13571 ns/op 960.51 MB/s 14685 B/op 385 allocs/op +BenchmarkDecoder_Parallel_Binding_GoJson-16 10031 ns/op 1299.51 MB/s 22111 B/op 49 allocs/op + +BenchmarkGetOne_Sonic-16 3276 ns/op 3975.78 MB/s 24 B/op 1 allocs/op +BenchmarkGetOne_Gjson-16 9431 ns/op 1380.81 MB/s 0 B/op 0 allocs/op +BenchmarkGetOne_Jsoniter-16 51178 ns/op 254.46 MB/s 27936 B/op 647 allocs/op +BenchmarkGetOne_Parallel_Sonic-16 216.7 ns/op 60098.95 MB/s 24 B/op 1 allocs/op +BenchmarkGetOne_Parallel_Gjson-16 1076 ns/op 12098.62 MB/s 0 B/op 0 allocs/op +BenchmarkGetOne_Parallel_Jsoniter-16 17741 ns/op 734.06 MB/s 27945 B/op 647 allocs/op +BenchmarkSetOne_Sonic-16 9571 ns/op 1360.61 MB/s 1584 B/op 17 allocs/op +BenchmarkSetOne_Sjson-16 36456 ns/op 357.22 MB/s 52180 B/op 9 allocs/op +BenchmarkSetOne_Jsoniter-16 79475 ns/op 163.86 MB/s 45862 B/op 964 allocs/op +BenchmarkSetOne_Parallel_Sonic-16 850.9 ns/op 15305.31 MB/s 1584 B/op 17 allocs/op +BenchmarkSetOne_Parallel_Sjson-16 18194 ns/op 715.77 MB/s 52247 B/op 9 allocs/op +BenchmarkSetOne_Parallel_Jsoniter-16 33560 ns/op 388.05 MB/s 45892 B/op 964 allocs/op +BenchmarkLoadNode/LoadAll()-16 11384 ns/op 1143.93 MB/s 6307 B/op 25 allocs/op +BenchmarkLoadNode_Parallel/LoadAll()-16 5493 ns/op 2370.68 MB/s 7145 B/op 25 allocs/op +BenchmarkLoadNode/Interface()-16 17722 ns/op 734.85 MB/s 13323 B/op 88 allocs/op +BenchmarkLoadNode_Parallel/Interface()-16 10330 ns/op 1260.70 MB/s 15178 B/op 88 allocs/op +``` + +- [小型](https://github.com/bytedance/sonic/blob/main/testdata/small.go) (400B, 11 个键, 3 层) +![small benchmarks](./docs/imgs/bench-small.png) +- [大型](https://github.com/bytedance/sonic/blob/main/testdata/twitter.json) (635kB, 10000+ 个键, 6 层) +![large benchmarks](./docs/imgs/bench-large.png) + +要查看基准测试代码,请参阅 [bench.sh](https://github.com/bytedance/sonic/blob/main/scripts/bench.sh) 。 + +## 工作原理 + +请参阅 [INTRODUCTION_ZH_CN.md](./docs/INTRODUCTION_ZH_CN.md). + +## 使用方式 + +### 序列化/反序列化 + +默认的行为基本上与 `encoding/json` 相一致,除了 HTML 转义形式(参见 [Escape HTML](https://github.com/bytedance/sonic/blob/main/README.md#escape-html)) 和 `SortKeys` 功能(参见 [Sort Keys](https://github.com/bytedance/sonic/blob/main/README.md#sort-keys))**没有**遵循 [RFC8259](https://datatracker.ietf.org/doc/html/rfc8259) 。 + + ```go +import "github.com/bytedance/sonic" + +var data YourSchema +// Marshal +output, err := sonic.Marshal(&data) +// Unmarshal +err := sonic.Unmarshal(output, &data) + ``` + +### 流式输入输出 + +Sonic 支持解码 `io.Reader` 中输入的 json,或将对象编码为 json 后输出至 `io.Writer`,以处理多个值并减少内存消耗。 + +- 编码器 + +```go +var o1 = map[string]interface{}{ + "a": "b", +} +var o2 = 1 +var w = bytes.NewBuffer(nil) +var enc = sonic.ConfigDefault.NewEncoder(w) +enc.Encode(o1) +enc.Encode(o2) +fmt.Println(w.String()) +// Output: +// {"a":"b"} +// 1 +``` + +- 解码器 + +```go +var o = map[string]interface{}{} +var r = strings.NewReader(`{"a":"b"}{"1":"2"}`) +var dec = sonic.ConfigDefault.NewDecoder(r) +dec.Decode(&o) +dec.Decode(&o) +fmt.Printf("%+v", o) +// Output: +// map[1:2 a:b] +``` + +### 使用 `Number` / `int64` + +```go +import "github.com/bytedance/sonic/decoder" + +var input = `1` +var data interface{} + +// default float64 +dc := decoder.NewDecoder(input) +dc.Decode(&data) // data == float64(1) +// use json.Number +dc = decoder.NewDecoder(input) +dc.UseNumber() +dc.Decode(&data) // data == json.Number("1") +// use int64 +dc = decoder.NewDecoder(input) +dc.UseInt64() +dc.Decode(&data) // data == int64(1) + +root, err := sonic.GetFromString(input) +// Get json.Number +jn := root.Number() +jm := root.InterfaceUseNumber().(json.Number) // jn == jm +// Get float64 +fn := root.Float64() +fm := root.Interface().(float64) // jn == jm + ``` + +### 对键排序 + +考虑到排序带来的性能损失(约 10% ), sonic 默认不会启用这个功能。如果你的组件依赖这个行为(如 [zstd](https://github.com/facebook/zstd)) ,可以仿照下面的例子: + +```go +import "github.com/bytedance/sonic" +import "github.com/bytedance/sonic/encoder" + +// Binding map only +m := map[string]interface{}{} +v, err := encoder.Encode(m, encoder.SortMapKeys) + +// Or ast.Node.SortKeys() before marshal +var root := sonic.Get(JSON) +err := root.SortKeys() +``` + +### HTML 转义 + +考虑到性能损失(约15%), sonic 默认不会启用这个功能。你可以使用 `encoder.EscapeHTML` 选项来开启(与 `encoding/json.HTMLEscape` 行为一致)。 + +```go +import "github.com/bytedance/sonic" + +v := map[string]string{"&&":"<>"} +ret, err := Encode(v, EscapeHTML) // ret == `{"\u0026\u0026":{"X":"\u003c\u003e"}}` +``` + +### 紧凑格式 + +Sonic 默认将基本类型( `struct` , `map` 等)编码为紧凑格式的 JSON ,除非使用 `json.RawMessage` or `json.Marshaler` 进行编码: sonic 确保输出的 JSON 合法,但出于性能考虑,**不会**加工成紧凑格式。我们提供选项 `encoder.CompactMarshaler` 来添加此过程, + +### 打印错误 + +如果输入的 JSON 存在无效的语法,sonic 将返回 `decoder.SyntaxError`,该错误支持错误位置的美化输出。 + +```go +import "github.com/bytedance/sonic" +import "github.com/bytedance/sonic/decoder" + +var data interface{} +err := sonic.UnmarshalString("[[[}]]", &data) +if err != nil { + /* One line by default */ + println(e.Error()) // "Syntax error at index 3: invalid char\n\n\t[[[}]]\n\t...^..\n" + /* Pretty print */ + if e, ok := err.(decoder.SyntaxError); ok { + /*Syntax error at index 3: invalid char + + [[[}]] + ...^.. + */ + print(e.Description()) + } else if me, ok := err.(*decoder.MismatchTypeError); ok { + // decoder.MismatchTypeError is new to Sonic v1.6.0 + print(me.Description()) + } +} +``` + +#### 类型不匹配 [Sonic v1.6.0] + +如果给定键中存在**类型不匹配**的值, sonic 会抛出 `decoder.MismatchTypeError` (如果有多个,只会报告最后一个),但仍会跳过错误的值并解码下一个 JSON 。 + +```go +import "github.com/bytedance/sonic" +import "github.com/bytedance/sonic/decoder" + +var data = struct{ + A int + B int +}{} +err := UnmarshalString(`{"A":"1","B":1}`, &data) +println(err.Error()) // Mismatch type int with value string "at index 5: mismatched type with value\n\n\t{\"A\":\"1\",\"B\":1}\n\t.....^.........\n" +fmt.Printf("%+v", data) // {A:0 B:1} +``` + +### `Ast.Node` + +Sonic/ast.Node 是完全独立的 JSON 抽象语法树库。它实现了序列化和反序列化,并提供了获取和修改通用数据的鲁棒的 API。 + +#### 查找/索引 + +通过给定的路径搜索 JSON 片段,路径必须为非负整数,字符串或 `nil` 。 + +```go +import "github.com/bytedance/sonic" + +input := []byte(`{"key1":[{},{"key2":{"key3":[1,2,3]}}]}`) + +// no path, returns entire json +root, err := sonic.Get(input) +raw := root.Raw() // == string(input) + +// multiple paths +root, err := sonic.Get(input, "key1", 1, "key2") +sub := root.Get("key3").Index(2).Int64() // == 3 +``` + +**注意**:由于 `Index()` 使用偏移量来定位数据,比使用扫描的 `Get()` 要快的多,建议尽可能的使用 `Index` 。 Sonic 也提供了另一个 API, `IndexOrGet()` ,以偏移量为基础并且也确保键的匹配。 + +#### 修改 + +使用 `Set()` / `Unset()` 修改 json 的内容 + +```go +import "github.com/bytedance/sonic" + +// Set +exist, err := root.Set("key4", NewBool(true)) // exist == false +alias1 := root.Get("key4") +println(alias1.Valid()) // true +alias2 := root.Index(1) +println(alias1 == alias2) // true + +// Unset +exist, err := root.UnsetByIndex(1) // exist == true +println(root.Get("key4").Check()) // "value not exist" +``` + +#### 序列化 + +要将 `ast.Node` 编码为 json ,使用 `MarshalJson()` 或者 `json.Marshal()` (必须传递指向节点的指针) + +```go +import ( + "encoding/json" + "github.com/bytedance/sonic" +) + +buf, err := root.MarshalJson() +println(string(buf)) // {"key1":[{},{"key2":{"key3":[1,2,3]}}]} +exp, err := json.Marshal(&root) // WARN: use pointer +println(string(buf) == string(exp)) // true +``` + +#### APIs + +- 合法性检查: `Check()`, `Error()`, `Valid()`, `Exist()` +- 索引: `Index()`, `Get()`, `IndexPair()`, `IndexOrGet()`, `GetByPath()` +- 转换至 go 内置类型: `Int64()`, `Float64()`, `String()`, `Number()`, `Bool()`, `Map[UseNumber|UseNode]()`, `Array[UseNumber|UseNode]()`, `Interface[UseNumber|UseNode]()` +- go 类型打包: `NewRaw()`, `NewNumber()`, `NewNull()`, `NewBool()`, `NewString()`, `NewObject()`, `NewArray()` +- 迭代: `Values()`, `Properties()`, `ForEach()`, `SortKeys()` +- 修改: `Set()`, `SetByIndex()`, `Add()` + +### `Ast.Visitor` + +Sonic 提供了一个高级的 API 用于直接全量解析 JSON 到非标准容器里 (既不是 `struct` 也不是 `map[string]interface{}`) 且不需要借助任何中间表示 (`ast.Node` 或 `interface{}`)。举个例子,你可能定义了下述的类型,它们看起来像 `interface{}`,但实际上并不是: + +```go +type UserNode interface {} + +// the following types implement the UserNode interface. +type ( + UserNull struct{} + UserBool struct{ Value bool } + UserInt64 struct{ Value int64 } + UserFloat64 struct{ Value float64 } + UserString struct{ Value string } + UserObject struct{ Value map[string]UserNode } + UserArray struct{ Value []UserNode } +) +``` + +Sonic 提供了下述的 API 来返回 **“对 JSON AST 的前序遍历”**。`ast.Visitor` 是一个 SAX 风格的接口,这在某些 C++ 的 JSON 解析库中被使用到。你需要自己实现一个 `ast.Visitor`,将它传递给 `ast.Preorder()` 方法。在你的实现中你可以使用自定义的类型来表示 JSON 的值。在你的 `ast.Visitor` 中,可能需要有一个 O(n) 空间复杂度的容器(比如说栈)来记录 object / array 的层级。 + +```go +func Preorder(str string, visitor Visitor, opts *VisitorOptions) error + +type Visitor interface { + OnNull() error + OnBool(v bool) error + OnString(v string) error + OnInt64(v int64, n json.Number) error + OnFloat64(v float64, n json.Number) error + OnObjectBegin(capacity int) error + OnObjectKey(key string) error + OnObjectEnd() error + OnArrayBegin(capacity int) error + OnArrayEnd() error +} +``` + +详细用法参看 [ast/visitor.go](https://github.com/bytedance/sonic/blob/main/ast/visitor.go),我们还为 `UserNode` 实现了一个示例 `ast.Visitor`,你可以在 [ast/visitor_test.go](https://github.com/bytedance/sonic/blob/main/ast/visitor_test.go) 中找到它。 + +## 兼容性 + +由于开发高性能代码的困难性, Sonic **不**保证对所有环境的支持。对于在不同环境中使用 Sonic 构建应用程序的开发者,我们有以下建议: + +- 在 **Mac M1** 上开发:确保在您的计算机上安装了 Rosetta 2,并在构建时设置 `GOARCH=amd64` 。 Rosetta 2 可以自动将 x86 二进制文件转换为 arm64 二进制文件,并在 Mac M1 上运行 x86 应用程序。 +- 在 **Linux arm64** 上开发:您可以安装 qemu 并使用 `qemu-x86_64 -cpu max` 命令来将 x86 二进制文件转换为 arm64 二进制文件。qemu可以实现与Mac M1上的Rosetta 2类似的转换效果。 + +对于希望在不使用 qemu 下使用 sonic 的开发者,或者希望处理 JSON 时与 `encoding/JSON` 严格保持一致的开发者,我们在 `sonic.API` 中提供了一些兼容性 API + +- `ConfigDefault`: 在支持 sonic 的环境下 sonic 的默认配置(`EscapeHTML=false`,`SortKeys=false`等)。行为与具有相应配置的 `encoding/json` 一致,一些选项,如 `SortKeys=false` 将无效。 +- `ConfigStd`: 在支持 sonic 的环境下与标准库兼容的配置(`EscapeHTML=true`,`SortKeys=true`等)。行为与 `encoding/json` 一致。 +- `ConfigFastest`: 在支持 sonic 的环境下运行最快的配置(`NoQuoteTextMarshaler=true`)。行为与具有相应配置的 `encoding/json` 一致,某些选项将无效。 + +## 注意事项 + +### 预热 + +由于 Sonic 使用 [golang-asm](https://github.com/twitchyliquid64/golang-asm) 作为 JIT 汇编器,这个库并不适用于运行时编译,第一次运行一个大型模式可能会导致请求超时甚至进程内存溢出。为了更好地稳定性,我们建议在运行大型模式或在内存有限的应用中,在使用 `Marshal()/Unmarshal()` 前运行 `Pretouch()`。 + +```go +import ( + "reflect" + "github.com/bytedance/sonic" + "github.com/bytedance/sonic/option" +) + +func init() { + var v HugeStruct + + // For most large types (nesting depth <= option.DefaultMaxInlineDepth) + err := sonic.Pretouch(reflect.TypeOf(v)) + + // with more CompileOption... + err := sonic.Pretouch(reflect.TypeOf(v), + // If the type is too deep nesting (nesting depth > option.DefaultMaxInlineDepth), + // you can set compile recursive loops in Pretouch for better stability in JIT. + option.WithCompileRecursiveDepth(loop), + // For a large nested struct, try to set a smaller depth to reduce compiling time. + option.WithCompileMaxInlineDepth(depth), + ) +} +``` + +### 拷贝字符串 + +当解码 **没有转义字符的字符串**时, sonic 会从原始的 JSON 缓冲区内引用而不是复制到新的一个缓冲区中。这对 CPU 的性能方面很有帮助,但是可能因此在解码后对象仍在使用的时候将整个 JSON 缓冲区保留在内存中。实践中我们发现,通过引用 JSON 缓冲区引入的额外内存通常是解码后对象的 20% 至 80% ,一旦应用长期保留这些对象(如缓存以备重用),服务器所使用的内存可能会增加。我们提供了选项 `decoder.CopyString()` 供用户选择,不引用 JSON 缓冲区。这可能在一定程度上降低 CPU 性能。 + +### 传递字符串还是字节数组? + +为了和 `encoding/json` 保持一致,我们提供了传递 `[]byte` 作为参数的 API ,但考虑到安全性,字符串到字节的复制是同时进行的,这在原始 JSON 非常大时可能会导致性能损失。因此,你可以使用 `UnmarshalString()` 和 `GetFromString()` 来传递字符串,只要你的原始数据是字符串,或**零拷贝类型转换**对于你的字节数组是安全的。我们也提供了 `MarshalString()` 的 API ,以便对编码的 JSON 字节数组进行**零拷贝类型转换**,因为 sonic 输出的字节始终是重复并且唯一的,所以这样是安全的。 + +### 加速 `encoding.TextMarshaler` + +为了保证数据安全性, `sonic.Encoder` 默认会对来自 `encoding.TextMarshaler` 接口的字符串进行引用和转义,如果大部分数据都是这种形式那可能会导致很大的性能损失。我们提供了 `encoder.NoQuoteTextMarshaler` 选项来跳过这些操作,但你**必须**保证他们的输出字符串依照 [RFC8259](https://datatracker.ietf.org/doc/html/rfc8259) 进行了转义和引用。 + +### 泛型的性能优化 + +在 **完全解析**的场景下, `Unmarshal()` 表现得比 `Get()`+`Node.Interface()` 更好。但是如果你只有特定 JSON 的部分模式,你可以将 `Get()` 和 `Unmarshal()` 结合使用: + +```go +import "github.com/bytedance/sonic" + +node, err := sonic.GetFromString(_TwitterJson, "statuses", 3, "user") +var user User // your partial schema... +err = sonic.UnmarshalString(node.Raw(), &user) +``` + +甚至如果你没有任何模式,可以用 `ast.Node` 代替 `map` 或 `interface` 作为泛型的容器: + +```go +import "github.com/bytedance/sonic" + +root, err := sonic.GetFromString(_TwitterJson) +user := root.GetByPath("statuses", 3, "user") // === root.Get("status").Index(3).Get("user") +err = user.Check() + +// err = user.LoadAll() // only call this when you want to use 'user' concurrently... +go someFunc(user) +``` + +为什么?因为 `ast.Node` 使用 `array` 来存储其子节点: + +- 在插入(反序列化)和扫描(序列化)数据时,`Array` 的性能比 `Map` **好得多**; +- **哈希**(`map[x]`)的效率不如**索引**(`array[x]`)高效,而 `ast.Node` 可以在数组和对象上使用索引; +- 使用 `Interface()` / `Map()` 意味着 sonic 必须解析所有的底层值,而 `ast.Node` 可以**按需解析**它们。 + +**注意**:由于 `ast.Node` 的惰性加载设计,其**不能**直接保证并发安全性,但你可以调用 `Node.Load()` / `Node.LoadAll()` 来实现并发安全。尽管可能会带来性能损失,但仍比转换成 `map` 或 `interface{}` 更为高效。 + +### 使用 `ast.Node` 还是 `ast.Visitor`? + +对于泛型数据的解析,`ast.Node` 在大多数场景上应该能够满足你的需求。 + +然而,`ast.Node` 是一种针对部分解析 JSON 而设计的泛型容器,它包含一些特殊设计,比如惰性加载,如果你希望像 `Unmarshal()` 那样直接解析整个 JSON,这些设计可能并不合适。尽管 `ast.Node` 相较于 `map` 或 `interface{}` 来说是更好的一种泛型容器,但它毕竟也是一种中间表示,如果你的最终类型是自定义的,你还得在解析完成后将上述类型转化成你自定义的类型。 + +在上述场景中,如果想要有更极致的性能,`ast.Visitor` 会是更好的选择。它采用和 `Unmarshal()` 类似的形式解析 JSON,并且你可以直接使用你的最终类型去表示 JSON AST,而不需要经过额外的任何中间表示。 + +但是,`ast.Visitor` 并不是一个很易用的 API。你可能需要写大量的代码去实现自己的 `ast.Visitor`,并且需要在解析过程中仔细维护树的层级。如果你决定要使用这个 API,请先仔细阅读 [ast/visitor.go](https://github.com/bytedance/sonic/blob/main/ast/visitor.go) 中的注释。 + +## 社区 + +Sonic 是 [CloudWeGo](https://www.cloudwego.io/) 下的一个子项目。我们致力于构建云原生生态系统。 diff --git a/vendor/github.com/bytedance/sonic/api.go b/vendor/github.com/bytedance/sonic/api.go index a042476f..54d9a216 100644 --- a/vendor/github.com/bytedance/sonic/api.go +++ b/vendor/github.com/bytedance/sonic/api.go @@ -20,6 +20,7 @@ import ( `io` `github.com/bytedance/sonic/ast` + `github.com/bytedance/sonic/internal/rt` ) // Config is a combination of sonic/encoder.Options and sonic/decoder.Options @@ -68,7 +69,14 @@ type Config struct { // ValidateString indicates decoder and encoder to valid string values: decoder will return errors // when unescaped control chars(\u0000-\u001f) in the string value of JSON. - ValidateString bool + ValidateString bool + + // NoValidateJSONMarshaler indicates that the encoder should not validate the output string + // after encoding the JSONMarshaler to JSON. + NoValidateJSONMarshaler bool + + // NoEncoderNewline indicates that the encoder should not add a newline after every message + NoEncoderNewline bool } var ( @@ -87,6 +95,7 @@ var ( // ConfigFastest is the fastest config of APIs, aiming at speed. ConfigFastest = Config{ NoQuoteTextMarshaler: true, + NoValidateJSONMarshaler: true, }.Froze() ) @@ -165,22 +174,41 @@ func UnmarshalString(buf string, val interface{}) error { return ConfigDefault.UnmarshalFromString(buf, val) } -// Get searches the given path from json, -// and returns its representing ast.Node. +// Get searches and locates the given path from src json, +// and returns a ast.Node representing the partially json. // // Each path arg must be integer or string: // - Integer is target index(>=0), means searching current node as array. // - String is target key, means searching current node as object. // // -// Note, the api expects the json is well-formed at least, -// otherwise it may return unexpected result. +// Notice: It expects the src json is **Well-formed** and **Immutable** when calling, +// otherwise it may return unexpected result. +// Considering memory safty, the returned JSON is **Copied** from the input func Get(src []byte, path ...interface{}) (ast.Node, error) { - return GetFromString(string(src), path...) + return GetCopyFromString(rt.Mem2Str(src), path...) } -// GetFromString is same with Get except src is string, -// which can reduce unnecessary memory copy. +// GetFromString is same with Get except src is string. +// +// WARNING: The returned JSON is **Referenced** from the input. +// Caching or long-time holding the returned node may cause OOM. +// If your src is big, consider use GetFromStringCopy(). func GetFromString(src string, path ...interface{}) (ast.Node, error) { return ast.NewSearcher(src).GetByPath(path...) -} \ No newline at end of file +} + +// GetCopyFromString is same with Get except src is string +func GetCopyFromString(src string, path ...interface{}) (ast.Node, error) { + return ast.NewSearcher(src).GetByPathCopy(path...) +} + +// Valid reports whether data is a valid JSON encoding. +func Valid(data []byte) bool { + return ConfigDefault.Valid(data) +} + +// Valid reports whether data is a valid JSON encoding. +func ValidString(data string) bool { + return ConfigDefault.Valid(rt.Str2Mem(data)) +} diff --git a/vendor/github.com/bytedance/sonic/ast/api_amd64.go b/vendor/github.com/bytedance/sonic/ast/api_amd64.go index 0e902be0..fa437563 100644 --- a/vendor/github.com/bytedance/sonic/ast/api_amd64.go +++ b/vendor/github.com/bytedance/sonic/ast/api_amd64.go @@ -1,4 +1,4 @@ -// +build amd64,go1.15,!go1.21 +// +build amd64,go1.16,!go1.23 /* * Copyright 2022 ByteDance Inc. @@ -37,6 +37,7 @@ func quote(buf *[]byte, val string) { *buf = append(*buf, '"') if len(val) == 0 { *buf = append(*buf, '"') + return } sp := rt.IndexChar(val, 0) @@ -86,7 +87,13 @@ func encodeBase64(src []byte) string { func (self *Parser) decodeValue() (val types.JsonState) { sv := (*rt.GoString)(unsafe.Pointer(&self.s)) - self.p = native.Value(sv.Ptr, sv.Len, self.p, &val, 0) + flag := types.F_USE_NUMBER + if self.dbuf != nil { + flag = 0 + val.Dbuf = self.dbuf + val.Dcap = types.MaxDigitNums + } + self.p = native.Value(sv.Ptr, sv.Len, self.p, &val, uint64(flag)) return } @@ -124,27 +131,3 @@ func (self *Parser) getByPath(path ...interface{}) (int, types.ParsingError) { } return start, 0 } - -func (self *Searcher) GetByPath(path ...interface{}) (Node, error) { - var err types.ParsingError - var start int - - self.parser.p = 0 - start, err = self.parser.getByPath(path...) - if err != 0 { - // for compatibility with old version - if err == types.ERR_NOT_FOUND { - return Node{}, ErrNotExist - } - if err == types.ERR_UNSUPPORT_TYPE { - panic("path must be either int(>=0) or string") - } - return Node{}, self.parser.syntaxError(err) - } - - t := switchRawType(self.parser.s[start]) - if t == _V_NONE { - return Node{}, self.parser.ExportError(err) - } - return newRawNode(self.parser.s[start:self.parser.p], t), nil -} \ No newline at end of file diff --git a/vendor/github.com/bytedance/sonic/ast/api_compat.go b/vendor/github.com/bytedance/sonic/ast/api_compat.go index b18b5ae8..9244e76e 100644 --- a/vendor/github.com/bytedance/sonic/ast/api_compat.go +++ b/vendor/github.com/bytedance/sonic/ast/api_compat.go @@ -1,4 +1,4 @@ -// +build !amd64 go1.21 +// +build !amd64 !go1.16 go1.23 /* * Copyright 2022 ByteDance Inc. @@ -21,12 +21,15 @@ package ast import ( `encoding/base64` `encoding/json` - `fmt` `github.com/bytedance/sonic/internal/native/types` `github.com/bytedance/sonic/internal/rt` ) +func init() { + println("WARNING: sonic only supports Go1.16~1.22 && CPU amd64, but your environment is not suitable") +} + func quote(buf *[]byte, val string) { quoteString(buf, val) } @@ -49,7 +52,7 @@ func encodeBase64(src []byte) string { } func (self *Parser) decodeValue() (val types.JsonState) { - e, v := decodeValue(self.s, self.p) + e, v := decodeValue(self.s, self.p, self.dbuf == nil) if e < 0 { return v } @@ -84,37 +87,25 @@ func (self *Node) encodeInterface(buf *[]byte) error { return nil } -func (self *Searcher) GetByPath(path ...interface{}) (Node, error) { - self.parser.p = 0 - +func (self *Parser) getByPath(path ...interface{}) (int, types.ParsingError) { var err types.ParsingError for _, p := range path { if idx, ok := p.(int); ok && idx >= 0 { - if err = self.parser.searchIndex(idx); err != 0 { - return Node{}, self.parser.ExportError(err) + if err = self.searchIndex(idx); err != 0 { + return -1, err } } else if key, ok := p.(string); ok { - if err = self.parser.searchKey(key); err != 0 { - return Node{}, self.parser.ExportError(err) + if err = self.searchKey(key); err != 0 { + return -1, err } } else { panic("path must be either int(>=0) or string") } } - var start = self.parser.p - if start, err = self.parser.skip(); err != 0 { - return Node{}, self.parser.ExportError(err) - } - ns := len(self.parser.s) - if self.parser.p > ns || start >= ns || start>=self.parser.p { - return Node{}, fmt.Errorf("skip %d char out of json boundary", start) - } - - t := switchRawType(self.parser.s[start]) - if t == _V_NONE { - return Node{}, self.parser.ExportError(err) + var start int + if start, err = self.skip(); err != 0 { + return -1, err } - - return newRawNode(self.parser.s[start:self.parser.p], t), nil -} \ No newline at end of file + return start, 0 +} diff --git a/vendor/github.com/bytedance/sonic/ast/buffer.go b/vendor/github.com/bytedance/sonic/ast/buffer.go new file mode 100644 index 00000000..bccbf481 --- /dev/null +++ b/vendor/github.com/bytedance/sonic/ast/buffer.go @@ -0,0 +1,409 @@ +/** + * Copyright 2023 ByteDance Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ast + +import ( + `sort` + `unsafe` +) + +type nodeChunk [_DEFAULT_NODE_CAP]Node + +type linkedNodes struct { + head nodeChunk + tail []*nodeChunk + size int +} + +func (self *linkedNodes) Cap() int { + if self == nil { + return 0 + } + return (len(self.tail)+1)*_DEFAULT_NODE_CAP +} + +func (self *linkedNodes) Len() int { + if self == nil { + return 0 + } + return self.size +} + +func (self *linkedNodes) At(i int) (*Node) { + if self == nil { + return nil + } + if i >= 0 && i= _DEFAULT_NODE_CAP && i= self.size || target < 0 || target >= self.size { + return + } + // reserve source + n := *self.At(source) + if source < target { + // move every element (source,target] one step back + for i:=source; itarget; i-- { + *self.At(i) = *self.At(i-1) + } + } + // set target + *self.At(target) = n +} + +func (self *linkedNodes) Pop() { + if self == nil || self.size == 0 { + return + } + self.Set(self.size-1, Node{}) + self.size-- +} + +func (self *linkedPairs) Pop() { + if self == nil || self.size == 0 { + return + } + self.Set(self.size-1, Pair{}) + self.size-- +} + +func (self *linkedNodes) Push(v Node) { + self.Set(self.size, v) +} + +func (self *linkedNodes) Set(i int, v Node) { + if i < _DEFAULT_NODE_CAP { + self.head[i] = v + if self.size <= i { + self.size = i+1 + } + return + } + a, b := i/_DEFAULT_NODE_CAP-1, i%_DEFAULT_NODE_CAP + if a < 0 { + self.head[b] = v + } else { + self.growTailLength(a+1) + var n = &self.tail[a] + if *n == nil { + *n = new(nodeChunk) + } + (*n)[b] = v + } + if self.size <= i { + self.size = i+1 + } +} + +func (self *linkedNodes) growTailLength(l int) { + if l <= len(self.tail) { + return + } + c := cap(self.tail) + for c < l { + c += 1 + c>>_APPEND_GROW_SHIFT + } + if c == cap(self.tail) { + self.tail = self.tail[:l] + return + } + tmp := make([]*nodeChunk, l, c) + copy(tmp, self.tail) + self.tail = tmp +} + +func (self *linkedNodes) ToSlice(con []Node) { + if len(con) < self.size { + return + } + i := (self.size-1) + a, b := i/_DEFAULT_NODE_CAP-1, i%_DEFAULT_NODE_CAP + if a < 0 { + copy(con, self.head[:b+1]) + return + } else { + copy(con, self.head[:]) + con = con[_DEFAULT_NODE_CAP:] + } + + for i:=0; i>_APPEND_GROW_SHIFT + self.tail = make([]*nodeChunk, a+1, c) + } + self.tail = self.tail[:a+1] + + for i:=0; i= 0 && i < _DEFAULT_NODE_CAP && i= _DEFAULT_NODE_CAP && i>_APPEND_GROW_SHIFT + } + if c == cap(self.tail) { + self.tail = self.tail[:l] + return + } + tmp := make([]*pairChunk, l, c) + copy(tmp, self.tail) + self.tail = tmp +} + +// linear search +func (self *linkedPairs) Get(key string) (*Pair, int) { + for i:=0; i>_APPEND_GROW_SHIFT + self.tail = make([]*pairChunk, a+1, c) + } + self.tail = self.tail[:a+1] + + for i:=0; i len(b) { + l = len(b) + } + for i := d; i < l; i++ { + if a[i] == b[i] { + continue + } + return a[i] < b[i] + } + return len(a) < len(b) +} + +type parseObjectStack struct { + parser Parser + v linkedPairs +} + +type parseArrayStack struct { + parser Parser + v linkedNodes +} + +func newLazyArray(p *Parser) Node { + s := new(parseArrayStack) + s.parser = *p + return Node{ + t: _V_ARRAY_LAZY, + p: unsafe.Pointer(s), + } +} + +func newLazyObject(p *Parser) Node { + s := new(parseObjectStack) + s.parser = *p + return Node{ + t: _V_OBJECT_LAZY, + p: unsafe.Pointer(s), + } +} + +func (self *Node) getParserAndArrayStack() (*Parser, *parseArrayStack) { + stack := (*parseArrayStack)(self.p) + return &stack.parser, stack +} + +func (self *Node) getParserAndObjectStack() (*Parser, *parseObjectStack) { + stack := (*parseObjectStack)(self.p) + return &stack.parser, stack +} + diff --git a/vendor/github.com/bytedance/sonic/ast/decode.go b/vendor/github.com/bytedance/sonic/ast/decode.go index 6a5f6fea..3e08bfcb 100644 --- a/vendor/github.com/bytedance/sonic/ast/decode.go +++ b/vendor/github.com/bytedance/sonic/ast/decode.go @@ -220,7 +220,7 @@ func decodeFloat64(src string, pos int) (ret int, v float64, err error) { return ret, v, nil } -func decodeValue(src string, pos int) (ret int, v types.JsonState) { +func decodeValue(src string, pos int, skipnum bool) (ret int, v types.JsonState) { pos = skipBlank(src, pos) if pos < 0 { return pos, types.JsonState{Vt: types.ValueType(pos)} @@ -256,20 +256,30 @@ func decodeValue(src string, pos int) (ret int, v types.JsonState) { } return ret, types.JsonState{Vt: types.V_FALSE} case '-', '+', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - var iv int64 - ret, iv, _ = decodeInt64(src, pos) - if ret >= 0 { - return ret, types.JsonState{Vt: types.V_INTEGER, Iv: iv, Ep: pos} - } else if ret != -int(types.ERR_INVALID_NUMBER_FMT) { - return ret, types.JsonState{Vt: types.ValueType(ret)} - } - var fv float64 - ret, fv, _ = decodeFloat64(src, pos) - if ret >= 0 { - return ret, types.JsonState{Vt: types.V_DOUBLE, Dv: fv, Ep: pos} + if skipnum { + ret = skipNumber(src, pos) + if ret >= 0 { + return ret, types.JsonState{Vt: types.V_DOUBLE, Iv: 0, Ep: pos} + } else { + return ret, types.JsonState{Vt: types.ValueType(ret)} + } } else { - return ret, types.JsonState{Vt: types.ValueType(ret)} + var iv int64 + ret, iv, _ = decodeInt64(src, pos) + if ret >= 0 { + return ret, types.JsonState{Vt: types.V_INTEGER, Iv: iv, Ep: pos} + } else if ret != -int(types.ERR_INVALID_NUMBER_FMT) { + return ret, types.JsonState{Vt: types.ValueType(ret)} + } + var fv float64 + ret, fv, _ = decodeFloat64(src, pos) + if ret >= 0 { + return ret, types.JsonState{Vt: types.V_DOUBLE, Dv: fv, Ep: pos} + } else { + return ret, types.JsonState{Vt: types.ValueType(ret)} + } } + default: return -int(types.ERR_INVALID_CHAR), types.JsonState{Vt:-types.ValueType(types.ERR_INVALID_CHAR)} } diff --git a/vendor/github.com/bytedance/sonic/ast/encode.go b/vendor/github.com/bytedance/sonic/ast/encode.go index 1187e30c..956809c2 100644 --- a/vendor/github.com/bytedance/sonic/ast/encode.go +++ b/vendor/github.com/bytedance/sonic/ast/encode.go @@ -19,8 +19,6 @@ package ast import ( `sync` `unicode/utf8` - - `github.com/bytedance/sonic/internal/rt` ) const ( @@ -165,18 +163,18 @@ func (self *Node) encodeFalse(buf *[]byte) error { } func (self *Node) encodeNumber(buf *[]byte) error { - str := rt.StrFrom(self.p, self.v) + str := self.toString() *buf = append(*buf, str...) return nil } func (self *Node) encodeString(buf *[]byte) error { - if self.v == 0 { + if self.l == 0 { *buf = append(*buf, '"', '"') return nil } - quote(buf, rt.StrFrom(self.p, self.v)) + quote(buf, self.toString()) return nil } @@ -195,16 +193,17 @@ func (self *Node) encodeArray(buf *[]byte) error { *buf = append(*buf, '[') - var p = (*Node)(self.p) - err := p.encode(buf) - if err != nil { - return err - } - for i := 1; i < nb; i++ { - *buf = append(*buf, ',') - p = p.unsafe_next() - err := p.encode(buf) - if err != nil { + var started bool + for i := 0; i < nb; i++ { + n := self.nodeAt(i) + if !n.Exists() { + continue + } + if started { + *buf = append(*buf, ',') + } + started = true + if err := n.encode(buf); err != nil { return err } } @@ -240,20 +239,21 @@ func (self *Node) encodeObject(buf *[]byte) error { *buf = append(*buf, '{') - var p = (*Pair)(self.p) - err := p.encode(buf) - if err != nil { - return err - } - for i := 1; i < nb; i++ { - *buf = append(*buf, ',') - p = p.unsafe_next() - err := p.encode(buf) - if err != nil { + var started bool + for i := 0; i < nb; i++ { + n := self.pairAt(i) + if n == nil || !n.Value.Exists() { + continue + } + if started { + *buf = append(*buf, ',') + } + started = true + if err := n.encode(buf); err != nil { return err } } *buf = append(*buf, '}') return nil -} \ No newline at end of file +} diff --git a/vendor/github.com/bytedance/sonic/ast/error.go b/vendor/github.com/bytedance/sonic/ast/error.go index f4c441ae..00a04468 100644 --- a/vendor/github.com/bytedance/sonic/ast/error.go +++ b/vendor/github.com/bytedance/sonic/ast/error.go @@ -8,23 +8,55 @@ import ( `github.com/bytedance/sonic/internal/native/types` ) -func (self *Parser) syntaxError(err types.ParsingError) SyntaxError { - return SyntaxError{ - Pos : self.p, - Src : self.s, - Code: err, + +func newError(err types.ParsingError, msg string) *Node { + return &Node{ + t: V_ERROR, + l: uint(err), + p: unsafe.Pointer(&msg), } } +// Error returns error message if the node is invalid +func (self Node) Error() string { + if self.t != V_ERROR { + return "" + } else { + return *(*string)(self.p) + } +} + func newSyntaxError(err SyntaxError) *Node { msg := err.Description() return &Node{ t: V_ERROR, - v: int64(err.Code), + l: uint(err.Code), p: unsafe.Pointer(&msg), } } +func (self *Parser) syntaxError(err types.ParsingError) SyntaxError { + return SyntaxError{ + Pos : self.p, + Src : self.s, + Code: err, + } +} + +func unwrapError(err error) *Node { + if se, ok := err.(*Node); ok { + return se + }else if sse, ok := err.(Node); ok { + return &sse + } else { + msg := err.Error() + return &Node{ + t: V_ERROR, + p: unsafe.Pointer(&msg), + } + } +} + type SyntaxError struct { Pos int Src string diff --git a/vendor/github.com/bytedance/sonic/ast/iterator.go b/vendor/github.com/bytedance/sonic/ast/iterator.go index 03a25b4e..64e1e5a9 100644 --- a/vendor/github.com/bytedance/sonic/ast/iterator.go +++ b/vendor/github.com/bytedance/sonic/ast/iterator.go @@ -32,7 +32,11 @@ func (self *Node) Values() (ListIterator, error) { if err := self.should(types.V_ARRAY, "an array"); err != nil { return ListIterator{}, err } - return ListIterator{Iterator{p: self}}, nil + return self.values(), nil +} + +func (self *Node) values() ListIterator { + return ListIterator{Iterator{p: self}} } // Properties returns iterator for object's children traversal @@ -40,7 +44,11 @@ func (self *Node) Properties() (ObjectIterator, error) { if err := self.should(types.V_OBJECT, "an object"); err != nil { return ObjectIterator{}, err } - return ObjectIterator{Iterator{p: self}}, nil + return self.properties(), nil +} + +func (self *Node) properties() ObjectIterator { + return ObjectIterator{Iterator{p: self}} } type Iterator struct { @@ -82,26 +90,54 @@ type ObjectIterator struct { Iterator } +func (self *ListIterator) next() *Node { +next_start: + if !self.HasNext() { + return nil + } else { + n := self.p.nodeAt(self.i) + self.i++ + if !n.Exists() { + goto next_start + } + return n + } +} + // Next scans through children of underlying V_ARRAY, // copies each child to v, and returns .HasNext(). func (self *ListIterator) Next(v *Node) bool { - if !self.HasNext() { + n := self.next() + if n == nil { return false + } + *v = *n + return true +} + +func (self *ObjectIterator) next() *Pair { +next_start: + if !self.HasNext() { + return nil } else { - *v, self.i = *self.p.nodeAt(self.i), self.i + 1 - return true + n := self.p.pairAt(self.i) + self.i++ + if n == nil || !n.Value.Exists() { + goto next_start + } + return n } } // Next scans through children of underlying V_OBJECT, // copies each child to v, and returns .HasNext(). func (self *ObjectIterator) Next(p *Pair) bool { - if !self.HasNext() { + n := self.next() + if n == nil { return false - } else { - *p, self.i = *self.p.pairAt(self.i), self.i + 1 - return true } + *p = *n + return true } // Sequence represents scanning path of single-layer nodes. @@ -129,36 +165,39 @@ type Scanner func(path Sequence, node *Node) bool // // Especailly, if the node is not V_ARRAY or V_OBJECT, // the node itself will be returned and Sequence.Index == -1. +// +// NOTICE: A unsetted node WON'T trigger sc, but its index still counts into Path.Index func (self *Node) ForEach(sc Scanner) error { switch self.itype() { case types.V_ARRAY: - ns, err := self.UnsafeArray() + iter, err := self.Values() if err != nil { return err } - for i := range ns { - if !sc(Sequence{i, nil}, &ns[i]) { - return err + v := iter.next() + for v != nil { + if !sc(Sequence{iter.i-1, nil}, v) { + return nil } + v = iter.next() } case types.V_OBJECT: - ns, err := self.UnsafeMap() + iter, err := self.Properties() if err != nil { return err } - for i := range ns { - if !sc(Sequence{i, &ns[i].Key}, &ns[i].Value) { - return err + v := iter.next() + for v != nil { + if !sc(Sequence{iter.i-1, &v.Key}, &v.Value) { + return nil } + v = iter.next() } default: + if self.Check() != nil { + return self + } sc(Sequence{-1, nil}, self) } - return self.Check() + return nil } - -type PairSlice []Pair - -func (self PairSlice) Sort() { - radixQsort(self, 0, maxDepth(len(self))) -} \ No newline at end of file diff --git a/vendor/github.com/bytedance/sonic/ast/node.go b/vendor/github.com/bytedance/sonic/ast/node.go index 6b5ad8a3..9637659b 100644 --- a/vendor/github.com/bytedance/sonic/ast/node.go +++ b/vendor/github.com/bytedance/sonic/ast/node.go @@ -21,20 +21,11 @@ import ( `fmt` `strconv` `unsafe` - `reflect` `github.com/bytedance/sonic/internal/native/types` `github.com/bytedance/sonic/internal/rt` ) -const ( - _CAP_BITS = 32 - _LEN_MASK = 1 << _CAP_BITS - 1 - - _NODE_SIZE = unsafe.Sizeof(Node{}) - _PAIR_SIZE = unsafe.Sizeof(Pair{}) -) - const ( _V_NONE types.ValueType = 0 _V_NODE_BASE types.ValueType = 1 << 5 @@ -51,40 +42,36 @@ const ( const ( V_NONE = 0 V_ERROR = 1 - V_NULL = 2 - V_TRUE = 3 - V_FALSE = 4 - V_ARRAY = 5 - V_OBJECT = 6 - V_STRING = 7 + V_NULL = int(types.V_NULL) + V_TRUE = int(types.V_TRUE) + V_FALSE = int(types.V_FALSE) + V_ARRAY = int(types.V_ARRAY) + V_OBJECT = int(types.V_OBJECT) + V_STRING = int(types.V_STRING) V_NUMBER = int(_V_NUMBER) V_ANY = int(_V_ANY) ) -var ( - byteType = rt.UnpackType(reflect.TypeOf(byte(0))) -) - type Node struct { - v int64 t types.ValueType + l uint p unsafe.Pointer } // UnmarshalJSON is just an adapter to json.Unmarshaler. // If you want better performance, use Searcher.GetByPath() directly func (self *Node) UnmarshalJSON(data []byte) (err error) { - *self, err = NewSearcher(string(data)).GetByPath() - return + *self = NewRaw(string(data)) + return self.Check() } /** Node Type Accessor **/ // Type returns json type represented by the node // It will be one of belows: -// V_NONE = 0 (empty node) +// V_NONE = 0 (empty node, key not exists) // V_ERROR = 1 (error node) -// V_NULL = 2 (json value `null`) +// V_NULL = 2 (json value `null`, key exists) // V_TRUE = 3 (json value `true`) // V_FALSE = 4 (json value `false`) // V_ARRAY = 5 (json value array) @@ -102,7 +89,7 @@ func (self Node) itype() types.ValueType { // Exists returns false only if the self is nil or empty node V_NONE func (self *Node) Exists() bool { - return self != nil && self.t != _V_NONE + return self.Valid() && self.t != _V_NONE } // Valid reports if self is NOT V_ERROR or nil @@ -114,7 +101,7 @@ func (self *Node) Valid() bool { } // Check checks if the node itself is valid, and return: -// - ErrNotFound If the node is nil +// - ErrNotExist If the node is nil // - Its underlying error If the node is V_ERROR func (self *Node) Check() error { if self == nil { @@ -126,15 +113,6 @@ func (self *Node) Check() error { } } -// Error returns error message if the node is invalid -func (self Node) Error() string { - if self.t != V_ERROR { - return "" - } else { - return *(*string)(self.p) - } -} - // IsRaw returns true if node's underlying value is raw json func (self Node) IsRaw() bool { return self.t&_V_RAW != 0 @@ -152,11 +130,14 @@ func (self *Node) isAny() bool { // Raw returns json representation of the node, func (self *Node) Raw() (string, error) { + if self == nil { + return "", ErrNotExist + } if !self.IsRaw() { buf, err := self.MarshalJSON() return rt.Mem2Str(buf), err } - return rt.StrFrom(self.p, self.v), nil + return self.toString(), nil } func (self *Node) checkRaw() error { @@ -166,7 +147,7 @@ func (self *Node) checkRaw() error { if self.IsRaw() { self.parseRaw(false) } - return nil + return self.Check() } // Bool returns bool value represented by this node, @@ -181,14 +162,14 @@ func (self *Node) Bool() (bool, error) { case types.V_FALSE : return false, nil case types.V_NULL : return false, nil case _V_NUMBER : - if i, err := numberToInt64(self); err == nil { + if i, err := self.toInt64(); err == nil { return i != 0, nil - } else if f, err := numberToFloat64(self); err == nil { + } else if f, err := self.toFloat64(); err == nil { return f != 0, nil } else { return false, err } - case types.V_STRING: return strconv.ParseBool(rt.StrFrom(self.p, self.v)) + case types.V_STRING: return strconv.ParseBool(self.toString()) case _V_ANY : any := self.packAny() switch v := any.(type) { @@ -229,9 +210,9 @@ func (self *Node) Int64() (int64, error) { } switch self.t { case _V_NUMBER, types.V_STRING : - if i, err := numberToInt64(self); err == nil { + if i, err := self.toInt64(); err == nil { return i, nil - } else if f, err := numberToFloat64(self); err == nil { + } else if f, err := self.toFloat64(); err == nil { return int64(f), nil } else { return 0, err @@ -283,7 +264,7 @@ func (self *Node) StrictInt64() (int64, error) { return 0, err } switch self.t { - case _V_NUMBER : return numberToInt64(self) + case _V_NUMBER : return self.toInt64() case _V_ANY : any := self.packAny() switch v := any.(type) { @@ -325,12 +306,12 @@ func (self *Node) Number() (json.Number, error) { return json.Number(""), err } switch self.t { - case _V_NUMBER : return toNumber(self) , nil + case _V_NUMBER : return self.toNumber(), nil case types.V_STRING : - if _, err := numberToInt64(self); err == nil { - return toNumber(self), nil - } else if _, err := numberToFloat64(self); err == nil { - return toNumber(self), nil + if _, err := self.toInt64(); err == nil { + return self.toNumber(), nil + } else if _, err := self.toFloat64(); err == nil { + return self.toNumber(), nil } else { return json.Number(""), err } @@ -372,7 +353,7 @@ func (self *Node) StrictNumber() (json.Number, error) { return json.Number(""), err } switch self.t { - case _V_NUMBER : return toNumber(self) , nil + case _V_NUMBER : return self.toNumber() , nil case _V_ANY : if v, ok := self.packAny().(json.Number); ok { return v, nil @@ -394,7 +375,7 @@ func (self *Node) String() (string, error) { case types.V_NULL : return "" , nil case types.V_TRUE : return "true" , nil case types.V_FALSE : return "false", nil - case types.V_STRING, _V_NUMBER : return rt.StrFrom(self.p, self.v), nil + case types.V_STRING, _V_NUMBER : return self.toString(), nil case _V_ANY : any := self.packAny() switch v := any.(type) { @@ -426,7 +407,7 @@ func (self *Node) StrictString() (string, error) { return "", err } switch self.t { - case types.V_STRING : return rt.StrFrom(self.p, self.v), nil + case types.V_STRING : return self.toString(), nil case _V_ANY : if v, ok := self.packAny().(string); ok { return v, nil @@ -445,7 +426,7 @@ func (self *Node) Float64() (float64, error) { return 0.0, err } switch self.t { - case _V_NUMBER, types.V_STRING : return numberToFloat64(self) + case _V_NUMBER, types.V_STRING : return self.toFloat64() case types.V_TRUE : return 1.0, nil case types.V_FALSE : return 0.0, nil case types.V_NULL : return 0.0, nil @@ -494,7 +475,7 @@ func (self *Node) StrictFloat64() (float64, error) { return 0.0, err } switch self.t { - case _V_NUMBER : return numberToFloat64(self) + case _V_NUMBER : return self.toFloat64() case _V_ANY : any := self.packAny() switch v := any.(type) { @@ -509,15 +490,13 @@ func (self *Node) StrictFloat64() (float64, error) { /** Sequencial Value Methods **/ // Len returns children count of a array|object|string node -// For partially loaded node, it also works but only counts the parsed children +// WARN: For partially loaded node, it also works but only counts the parsed children func (self *Node) Len() (int, error) { if err := self.checkRaw(); err != nil { return 0, err } - if self.t == types.V_ARRAY || self.t == types.V_OBJECT || self.t == _V_ARRAY_LAZY || self.t == _V_OBJECT_LAZY { - return int(self.v & _LEN_MASK), nil - } else if self.t == types.V_STRING { - return int(self.v), nil + if self.t == types.V_ARRAY || self.t == types.V_OBJECT || self.t == _V_ARRAY_LAZY || self.t == _V_OBJECT_LAZY || self.t == types.V_STRING { + return int(self.l), nil } else if self.t == _V_NONE || self.t == types.V_NULL { return 0, nil } else { @@ -526,7 +505,7 @@ func (self *Node) Len() (int, error) { } func (self Node) len() int { - return int(self.v & _LEN_MASK) + return int(self.l) } // Cap returns malloc capacity of a array|object node for children @@ -534,47 +513,44 @@ func (self *Node) Cap() (int, error) { if err := self.checkRaw(); err != nil { return 0, err } - if self.t == types.V_ARRAY || self.t == types.V_OBJECT || self.t == _V_ARRAY_LAZY || self.t == _V_OBJECT_LAZY { - return int(self.v >> _CAP_BITS), nil - } else if self.t == _V_NONE || self.t == types.V_NULL { - return 0, nil - } else { - return 0, ErrUnsupportType + switch self.t { + case types.V_ARRAY: return (*linkedNodes)(self.p).Cap(), nil + case types.V_OBJECT: return (*linkedPairs)(self.p).Cap(), nil + case _V_ARRAY_LAZY: return (*parseArrayStack)(self.p).v.Cap(), nil + case _V_OBJECT_LAZY: return (*parseObjectStack)(self.p).v.Cap(), nil + case _V_NONE, types.V_NULL: return 0, nil + default: return 0, ErrUnsupportType } } -func (self Node) cap() int { - return int(self.v >> _CAP_BITS) -} - // Set sets the node of given key under self, and reports if the key has existed. // // If self is V_NONE or V_NULL, it becomes V_OBJECT and sets the node at the key. func (self *Node) Set(key string, node Node) (bool, error) { - if self != nil && (self.t == _V_NONE || self.t == types.V_NULL) { - *self = NewObject([]Pair{{key, node}}) - return false, nil + if err := self.Check(); err != nil { + return false, err } - if err := node.Check(); err != nil { return false, err } + + if self.t == _V_NONE || self.t == types.V_NULL { + *self = NewObject([]Pair{{key, node}}) + return false, nil + } else if self.itype() != types.V_OBJECT { + return false, ErrUnsupportType + } p := self.Get(key) + if !p.Exists() { - l := self.len() - c := self.cap() - if l == c { - // TODO: maybe change append size in future - c += _DEFAULT_NODE_CAP - mem := unsafe_NewArray(_PAIR_TYPE, c) - memmove(mem, self.p, _PAIR_SIZE * uintptr(l)) - self.p = mem + // self must be fully-loaded here + if self.len() == 0 { + *self = newObject(new(linkedPairs)) } - v := self.pairAt(l) - v.Key = key - v.Value = node - self.setCapAndLen(c, l+1) + s := (*linkedPairs)(self.p) + s.Push(Pair{key, node}) + self.l++ return false, nil } else if err := p.Check(); err != nil { @@ -590,17 +566,22 @@ func (self *Node) SetAny(key string, val interface{}) (bool, error) { return self.Set(key, NewAny(val)) } -// Unset remove the node of given key under object parent, and reports if the key has existed. +// Unset REMOVE (soft) the node of given key under object parent, and reports if the key has existed. func (self *Node) Unset(key string) (bool, error) { - self.must(types.V_OBJECT, "an object") + if err := self.should(types.V_OBJECT, "an object"); err != nil { + return false, err + } + // NOTICE: must get acurate length before deduct + if err := self.skipAllKey(); err != nil { + return false, err + } p, i := self.skipKey(key) if !p.Exists() { return false, nil } else if err := p.Check(); err != nil { return false, err } - - self.removePair(i) + self.removePairAt(i) return true, nil } @@ -608,10 +589,18 @@ func (self *Node) Unset(key string) (bool, error) { // // The index must be within self's children. func (self *Node) SetByIndex(index int, node Node) (bool, error) { + if err := self.Check(); err != nil { + return false, err + } if err := node.Check(); err != nil { return false, err } + if index == 0 && (self.t == _V_NONE || self.t == types.V_NULL) { + *self = NewArray([]Node{node}) + return false, nil + } + p := self.Index(index) if !p.Exists() { return false, ErrNotExist @@ -628,14 +617,28 @@ func (self *Node) SetAnyByIndex(index int, val interface{}) (bool, error) { return self.SetByIndex(index, NewAny(val)) } -// UnsetByIndex remove the node of given index +// UnsetByIndex REOMVE (softly) the node of given index. +// +// WARN: this will change address of elements, which is a dangerous action. +// Use Unset() for object or Pop() for array instead. func (self *Node) UnsetByIndex(index int) (bool, error) { + if err := self.checkRaw(); err != nil { + return false, err + } + var p *Node it := self.itype() + if it == types.V_ARRAY { - p = self.Index(index) - }else if it == types.V_OBJECT { - pr := self.skipIndexPair(index) + if err := self.skipAllIndex(); err != nil { + return false, err + } + p = self.nodeAt(index) + } else if it == types.V_OBJECT { + if err := self.skipAllKey(); err != nil { + return false, err + } + pr := self.pairAt(index) if pr == nil { return false, ErrNotExist } @@ -648,6 +651,12 @@ func (self *Node) UnsetByIndex(index int) (bool, error) { return false, ErrNotExist } + // last elem + if index == self.len() - 1 { + return true, self.Pop() + } + + // not last elem, self.len() change but linked-chunk not change if it == types.V_ARRAY { self.removeNode(index) }else if it == types.V_OBJECT { @@ -660,28 +669,110 @@ func (self *Node) UnsetByIndex(index int) (bool, error) { // // If self is V_NONE or V_NULL, it becomes V_ARRAY and sets the node at index 0. func (self *Node) Add(node Node) error { + if err := self.Check(); err != nil { + return err + } + if self != nil && (self.t == _V_NONE || self.t == types.V_NULL) { *self = NewArray([]Node{node}) return nil } - if err := self.should(types.V_ARRAY, "an array"); err != nil { return err } - if err := self.skipAllIndex(); err != nil { + + s, err := self.unsafeArray() + if err != nil { return err } - var p rt.GoSlice - p.Cap = self.cap() - p.Len = self.len() - p.Ptr = self.p + // Notice: array won't have unset node in tail + s.Push(node) + self.l++ + return nil +} - s := *(*[]Node)(unsafe.Pointer(&p)) - s = append(s, node) +// Pop remove the last child of the V_Array or V_Object node. +func (self *Node) Pop() error { + if err := self.checkRaw(); err != nil { + return err + } + + if it := self.itype(); it == types.V_ARRAY { + s, err := self.unsafeArray() + if err != nil { + return err + } + // remove tail unset nodes + for i := s.Len()-1; i >= 0; i-- { + if s.At(i).Exists() { + s.Pop() + self.l-- + break + } + s.Pop() + } + + } else if it == types.V_OBJECT { + s, err := self.unsafeMap() + if err != nil { + return err + } + // remove tail unset nodes + for i := s.Len()-1; i >= 0; i-- { + if p := s.At(i); p != nil && p.Value.Exists() { + s.Pop() + self.l-- + break + } + s.Pop() + } + + } else { + return ErrUnsupportType + } - self.p = unsafe.Pointer(&s[0]) - self.setCapAndLen(cap(s), len(s)) + return nil +} + +// Move moves the child at src index to dst index, +// meanwhile slides sliblings from src+1 to dst. +// +// WARN: this will change address of elements, which is a dangerous action. +func (self *Node) Move(dst, src int) error { + if err := self.should(types.V_ARRAY, "an array"); err != nil { + return err + } + + s, err := self.unsafeArray() + if err != nil { + return err + } + + // check if any unset node exists + if l := s.Len(); self.len() != l { + di, si := dst, src + // find real pos of src and dst + for i := 0; i < l; i++ { + if s.At(i).Exists() { + di-- + si-- + } + if di == -1 { + dst = i + di-- + } + if si == -1 { + src = i + si-- + } + if di == -2 && si == -2 { + break + } + } + } + + s.MoveOne(src, dst) return nil } @@ -760,19 +851,30 @@ func (self *Node) IndexPair(idx int) *Pair { return self.skipIndexPair(idx) } +func (self *Node) indexOrGet(idx int, key string) (*Node, int) { + if err := self.should(types.V_OBJECT, "an object"); err != nil { + return unwrapError(err), idx + } + + pr := self.skipIndexPair(idx) + if pr != nil && pr.Key == key { + return &pr.Value, idx + } + + return self.skipKey(key) +} + // IndexOrGet firstly use idx to index a value and check if its key matches // If not, then use the key to search value func (self *Node) IndexOrGet(idx int, key string) *Node { - if err := self.should(types.V_OBJECT, "an object"); err != nil { - return unwrapError(err) - } + node, _ := self.indexOrGet(idx, key) + return node +} - pr := self.skipIndexPair(idx) - if pr != nil && pr.Key == key { - return &pr.Value - } - n, _ := self.skipKey(key) - return n +// IndexOrGetWithIdx attempts to retrieve a node by index and key, returning the node and its correct index. +// If the key does not match at the given index, it searches by key and returns the node with its updated index. +func (self *Node) IndexOrGetWithIdx(idx int, key string) (*Node, int) { + return self.indexOrGet(idx, key) } /** Generic Value Converters **/ @@ -837,30 +939,74 @@ func (self *Node) MapUseNode() (map[string]Node, error) { // MapUnsafe exports the underlying pointer to its children map // WARN: don't use it unless you know what you are doing -func (self *Node) UnsafeMap() ([]Pair, error) { - if err := self.should(types.V_OBJECT, "an object"); err != nil { - return nil, err - } +// +// Deprecated: this API now returns copied nodes instead of directly reference, +// func (self *Node) UnsafeMap() ([]Pair, error) { +// if err := self.should(types.V_OBJECT, "an object"); err != nil { +// return nil, err +// } +// if err := self.skipAllKey(); err != nil { +// return nil, err +// } +// return self.toGenericObjectUsePair() +// } + +//go:nocheckptr +func (self *Node) unsafeMap() (*linkedPairs, error) { if err := self.skipAllKey(); err != nil { return nil, err } - s := rt.Ptr2SlicePtr(self.p, int(self.len()), self.cap()) - return *(*[]Pair)(s), nil + if self.p == nil { + *self = newObject(new(linkedPairs)) + } + return (*linkedPairs)(self.p), nil } // SortKeys sorts children of a V_OBJECT node in ascending key-order. // If recurse is true, it recursively sorts children's children as long as a V_OBJECT node is found. -func (self *Node) SortKeys(recurse bool) (err error) { - ps, err := self.UnsafeMap() +func (self *Node) SortKeys(recurse bool) error { + // check raw node first + if err := self.checkRaw(); err != nil { + return err + } + if self.itype() == types.V_OBJECT { + return self.sortKeys(recurse) + } else if self.itype() == types.V_ARRAY { + var err error + err2 := self.ForEach(func(path Sequence, node *Node) bool { + it := node.itype() + if it == types.V_ARRAY || it == types.V_OBJECT { + err = node.SortKeys(recurse) + if err != nil { + return false + } + } + return true + }) + if err != nil { + return err + } + return err2 + } else { + return nil + } +} + +func (self *Node) sortKeys(recurse bool) (err error) { + // check raw node first + if err := self.checkRaw(); err != nil { + return err + } + ps, err := self.unsafeMap() if err != nil { return err } - PairSlice(ps).Sort() + ps.Sort() if recurse { var sc Scanner sc = func(path Sequence, node *Node) bool { if node.itype() == types.V_OBJECT { - if err := node.SortKeys(recurse); err != nil { + if err := node.sortKeys(recurse); err != nil { return false } } @@ -871,7 +1017,9 @@ func (self *Node) SortKeys(recurse bool) (err error) { } return true } - self.ForEach(sc) + if err := self.ForEach(sc); err != nil { + return err + } } return nil } @@ -936,15 +1084,27 @@ func (self *Node) ArrayUseNode() ([]Node, error) { // ArrayUnsafe exports the underlying pointer to its children array // WARN: don't use it unless you know what you are doing -func (self *Node) UnsafeArray() ([]Node, error) { - if err := self.should(types.V_ARRAY, "an array"); err != nil { - return nil, err - } +// +// Deprecated: this API now returns copied nodes instead of directly reference, +// which has no difference with ArrayUseNode +// func (self *Node) UnsafeArray() ([]Node, error) { +// if err := self.should(types.V_ARRAY, "an array"); err != nil { +// return nil, err +// } +// if err := self.skipAllIndex(); err != nil { +// return nil, err +// } +// return self.toGenericArrayUseNode() +// } + +func (self *Node) unsafeArray() (*linkedNodes, error) { if err := self.skipAllIndex(); err != nil { return nil, err } - s := rt.Ptr2SlicePtr(self.p, self.len(), self.cap()) - return *(*[]Node)(s), nil + if self.p == nil { + *self = newArray(new(linkedNodes)) + } + return (*linkedNodes)(self.p), nil } // Interface loads all children under all pathes from this node, @@ -961,9 +1121,9 @@ func (self *Node) Interface() (interface{}, error) { case types.V_FALSE : return false, nil case types.V_ARRAY : return self.toGenericArray() case types.V_OBJECT : return self.toGenericObject() - case types.V_STRING : return rt.StrFrom(self.p, self.v), nil + case types.V_STRING : return self.toString(), nil case _V_NUMBER : - v, err := numberToFloat64(self) + v, err := self.toFloat64() if err != nil { return nil, err } @@ -1005,8 +1165,8 @@ func (self *Node) InterfaceUseNumber() (interface{}, error) { case types.V_FALSE : return false, nil case types.V_ARRAY : return self.toGenericArrayUseNumber() case types.V_OBJECT : return self.toGenericObjectUseNumber() - case types.V_STRING : return rt.StrFrom(self.p, self.v), nil - case _V_NUMBER : return toNumber(self), nil + case types.V_STRING : return self.toString(), nil + case _V_NUMBER : return self.toNumber(), nil case _V_ARRAY_LAZY : if err := self.loadAllIndex(); err != nil { return nil, err @@ -1092,9 +1252,8 @@ func (self *Node) LoadAll() error { // Load loads the node's children as parsed. // After calling it, only the node itself can be used on concurrency (not include its children) func (self *Node) Load() error { - if self.IsRaw() { - self.parseRaw(false) - return self.Load() + if err := self.checkRaw(); err != nil { + return err } switch self.t { @@ -1109,39 +1268,6 @@ func (self *Node) Load() error { /**---------------------------------- Internal Helper Methods ----------------------------------**/ -var ( - _NODE_TYPE = rt.UnpackEface(Node{}).Type - _PAIR_TYPE = rt.UnpackEface(Pair{}).Type -) - -func (self *Node) setCapAndLen(cap int, len int) { - if self.t == types.V_ARRAY || self.t == types.V_OBJECT || self.t == _V_ARRAY_LAZY || self.t == _V_OBJECT_LAZY { - self.v = int64(len&_LEN_MASK | cap<<_CAP_BITS) - } else { - panic("value does not have a length") - } -} - -func (self *Node) unsafe_next() *Node { - return (*Node)(unsafe.Pointer(uintptr(unsafe.Pointer(self)) + _NODE_SIZE)) -} - -func (self *Pair) unsafe_next() *Pair { - return (*Pair)(unsafe.Pointer(uintptr(unsafe.Pointer(self)) + _PAIR_SIZE)) -} - -func (self *Node) must(t types.ValueType, s string) { - if err := self.checkRaw(); err != nil { - panic(err) - } - if err := self.Check(); err != nil { - panic(err) - } - if self.itype() != t { - panic("value cannot be represented as " + s) - } -} - func (self *Node) should(t types.ValueType, s string) error { if err := self.checkRaw(); err != nil { return err @@ -1153,37 +1279,51 @@ func (self *Node) should(t types.ValueType, s string) error { } func (self *Node) nodeAt(i int) *Node { - var p = self.p + var p *linkedNodes if self.isLazy() { _, stack := self.getParserAndArrayStack() - p = *(*unsafe.Pointer)(unsafe.Pointer(&stack.v)) + p = &stack.v + } else { + p = (*linkedNodes)(self.p) + if l := p.Len(); l != self.len() { + // some nodes got unset, iterate to skip them + for j:=0; j 0 { /* linear search */ var p *Pair + var i int if lazy { s := (*parseObjectStack)(self.p) - p = &s.v[0] + p, i = s.v.Get(key) } else { - p = (*Pair)(self.p) + p, i = (*linkedPairs)(self.p).Get(key) } - if p.Key == key { - return &p.Value, 0 - } - for i := 1; i < nb; i++ { - p = p.unsafe_next() - if p.Key == key { - return &p.Value, i - } + if p != nil { + return &p.Value, i } } @@ -1311,7 +1446,7 @@ func (self *Node) loadAllIndex() error { var err types.ParsingError parser, stack := self.getParserAndArrayStack() parser.noLazy = true - *self, err = parser.decodeArray(stack.v) + *self, err = parser.decodeArray(&stack.v) if err != 0 { return parser.ExportError(err) } @@ -1325,7 +1460,7 @@ func (self *Node) loadAllKey() error { var err types.ParsingError parser, stack := self.getParserAndObjectStack() parser.noLazy = true - *self, err = parser.decodeObject(stack.v) + *self, err = parser.decodeObject(&stack.v) if err != 0 { return parser.ExportError(err) } @@ -1333,63 +1468,50 @@ func (self *Node) loadAllKey() error { } func (self *Node) removeNode(i int) { - nb := self.len() - 1 node := self.nodeAt(i) - if i == nb { - self.setCapAndLen(self.cap(), nb) - *node = Node{} + if node == nil { return } - - from := self.nodeAt(i + 1) - memmove(unsafe.Pointer(node), unsafe.Pointer(from), _NODE_SIZE * uintptr(nb - i)) - - last := self.nodeAt(nb) - *last = Node{} - - self.setCapAndLen(self.cap(), nb) + *node = Node{} + // NOTICE: not be consistent with linkedNode.Len() + self.l-- } func (self *Node) removePair(i int) { - nb := self.len() - 1 - node := self.pairAt(i) - if i == nb { - self.setCapAndLen(self.cap(), nb) - *node = Pair{} + last := self.pairAt(i) + if last == nil { return } - - from := self.pairAt(i + 1) - memmove(unsafe.Pointer(node), unsafe.Pointer(from), _PAIR_SIZE * uintptr(nb - i)) - - last := self.pairAt(nb) *last = Pair{} - - self.setCapAndLen(self.cap(), nb) + // NOTICE: should be consistent with linkedPair.Len() + self.l-- +} + +func (self *Node) removePairAt(i int) { + p := (*linkedPairs)(self.p).At(i) + if p == nil { + return + } + *p = Pair{} + // NOTICE: should be consistent with linkedPair.Len() + self.l-- } func (self *Node) toGenericArray() ([]interface{}, error) { nb := self.len() - ret := make([]interface{}, nb) if nb == 0 { - return ret, nil + return []interface{}{}, nil } - + ret := make([]interface{}, 0, nb) + /* convert each item */ - var p = (*Node)(self.p) - x, err := p.Interface() - if err != nil { - return nil, err - } - ret[0] = x - - for i := 1; i < nb; i++ { - p = p.unsafe_next() - x, err := p.Interface() + it := self.values() + for v := it.next(); v != nil; v = it.next() { + vv, err := v.Interface() if err != nil { return nil, err } - ret[i] = x + ret = append(ret, vv) } /* all done */ @@ -1398,26 +1520,19 @@ func (self *Node) toGenericArray() ([]interface{}, error) { func (self *Node) toGenericArrayUseNumber() ([]interface{}, error) { nb := self.len() - ret := make([]interface{}, nb) if nb == 0 { - return ret, nil + return []interface{}{}, nil } + ret := make([]interface{}, 0, nb) /* convert each item */ - var p = (*Node)(self.p) - x, err := p.InterfaceUseNumber() - if err != nil { - return nil, err - } - ret[0] = x - - for i := 1; i < nb; i++ { - p = p.unsafe_next() - x, err := p.InterfaceUseNumber() + it := self.values() + for v := it.next(); v != nil; v = it.next() { + vv, err := v.InterfaceUseNumber() if err != nil { return nil, err } - ret[i] = x + ret = append(ret, vv) } /* all done */ @@ -1426,50 +1541,32 @@ func (self *Node) toGenericArrayUseNumber() ([]interface{}, error) { func (self *Node) toGenericArrayUseNode() ([]Node, error) { var nb = self.len() - var out = make([]Node, nb) if nb == 0 { - return out, nil + return []Node{}, nil } - var p = (*Node)(self.p) - out[0] = *p - if err := p.Check(); err != nil { - return nil, err - } - - for i := 1; i < nb; i++ { - p = p.unsafe_next() - if err := p.Check(); err != nil { - return nil, err - } - out[i] = *p - } + var s = (*linkedNodes)(self.p) + var out = make([]Node, nb) + s.ToSlice(out) return out, nil } func (self *Node) toGenericObject() (map[string]interface{}, error) { nb := self.len() - ret := make(map[string]interface{}, nb) if nb == 0 { - return ret, nil + return map[string]interface{}{}, nil } + ret := make(map[string]interface{}, nb) /* convert each item */ - var p = (*Pair)(self.p) - x, err := p.Value.Interface() - if err != nil { - return nil, err - } - ret[p.Key] = x - - for i := 1; i < nb; i++ { - p = p.unsafe_next() - x, err := p.Value.Interface() + it := self.properties() + for v := it.next(); v != nil; v = it.next() { + vv, err := v.Value.Interface() if err != nil { return nil, err } - ret[p.Key] = x + ret[v.Key] = vv } /* all done */ @@ -1479,26 +1576,19 @@ func (self *Node) toGenericObject() (map[string]interface{}, error) { func (self *Node) toGenericObjectUseNumber() (map[string]interface{}, error) { nb := self.len() - ret := make(map[string]interface{}, nb) if nb == 0 { - return ret, nil + return map[string]interface{}{}, nil } + ret := make(map[string]interface{}, nb) /* convert each item */ - var p = (*Pair)(self.p) - x, err := p.Value.InterfaceUseNumber() - if err != nil { - return nil, err - } - ret[p.Key] = x - - for i := 1; i < nb; i++ { - p = p.unsafe_next() - x, err := p.Value.InterfaceUseNumber() + it := self.properties() + for v := it.next(); v != nil; v = it.next() { + vv, err := v.Value.InterfaceUseNumber() if err != nil { return nil, err } - ret[p.Key] = x + ret[v.Key] = vv } /* all done */ @@ -1507,24 +1597,13 @@ func (self *Node) toGenericObjectUseNumber() (map[string]interface{}, error) { func (self *Node) toGenericObjectUseNode() (map[string]Node, error) { var nb = self.len() - var out = make(map[string]Node, nb) if nb == 0 { - return out, nil - } - - var p = (*Pair)(self.p) - out[p.Key] = p.Value - if err := p.Value.Check(); err != nil { - return nil, err + return map[string]Node{}, nil } - for i := 1; i < nb; i++ { - p = p.unsafe_next() - if err := p.Value.Check(); err != nil { - return nil, err - } - out[p.Key] = p.Value - } + var s = (*linkedPairs)(self.p) + var out = make(map[string]Node, nb) + s.ToMap(out) /* all done */ return out, nil @@ -1536,15 +1615,12 @@ var ( nullNode = Node{t: types.V_NULL} trueNode = Node{t: types.V_TRUE} falseNode = Node{t: types.V_FALSE} - - emptyArrayNode = Node{t: types.V_ARRAY} - emptyObjectNode = Node{t: types.V_OBJECT} ) // NewRaw creates a node of raw json. // If the input json is invalid, NewRaw returns a error Node. func NewRaw(json string) Node { - parser := NewParser(json) + parser := NewParserObj(json) start, err := parser.skip() if err != 0 { return *newError(err, err.Message()) @@ -1567,7 +1643,6 @@ func NewAny(any interface{}) Node { default: return Node{ t: _V_ANY, - v: 0, p: unsafe.Pointer(&any), } } @@ -1585,7 +1660,6 @@ func NewBytes(src []byte) Node { // NewNull creates a node of type V_NULL func NewNull() Node { return Node{ - v: 0, p: nil, t: types.V_NULL, } @@ -1600,7 +1674,6 @@ func NewBool(v bool) Node { t = types.V_TRUE } return Node{ - v: 0, p: nil, t: t, } @@ -1610,26 +1683,30 @@ func NewBool(v bool) Node { // v must be a decimal string complying with RFC8259 func NewNumber(v string) Node { return Node{ - v: int64(len(v) & _LEN_MASK), + l: uint(len(v)), p: rt.StrPtr(v), t: _V_NUMBER, } } -func toNumber(node *Node) json.Number { - return json.Number(rt.StrFrom(node.p, node.v)) +func (node Node) toNumber() json.Number { + return json.Number(rt.StrFrom(node.p, int64(node.l))) +} + +func (self Node) toString() string { + return rt.StrFrom(self.p, int64(self.l)) } -func numberToFloat64(node *Node) (float64, error) { - ret,err := toNumber(node).Float64() +func (node Node) toFloat64() (float64, error) { + ret, err := node.toNumber().Float64() if err != nil { return 0, err } return ret, nil } -func numberToInt64(node *Node) (int64, error) { - ret,err := toNumber(node).Int64() +func (node Node) toInt64() (int64, error) { + ret,err := node.toNumber().Int64() if err != nil { return 0, err } @@ -1640,7 +1717,7 @@ func newBytes(v []byte) Node { return Node{ t: types.V_STRING, p: mem2ptr(v), - v: int64(len(v) & _LEN_MASK), + l: uint(len(v)), } } @@ -1652,103 +1729,65 @@ func NewString(v string) Node { return Node{ t: types.V_STRING, p: rt.StrPtr(v), - v: int64(len(v) & _LEN_MASK), + l: uint(len(v)), } } // NewArray creates a node of type V_ARRAY, // using v as its underlying children func NewArray(v []Node) Node { + s := new(linkedNodes) + s.FromSlice(v) + return newArray(s) +} + +func newArray(v *linkedNodes) Node { return Node{ t: types.V_ARRAY, - v: int64(len(v)&_LEN_MASK | cap(v)<<_CAP_BITS), - p: *(*unsafe.Pointer)(unsafe.Pointer(&v)), + l: uint(v.Len()), + p: unsafe.Pointer(v), } } -func (self *Node) setArray(v []Node) { +func (self *Node) setArray(v *linkedNodes) { self.t = types.V_ARRAY - self.setCapAndLen(cap(v), len(v)) - self.p = *(*unsafe.Pointer)(unsafe.Pointer(&v)) + self.l = uint(v.Len()) + self.p = unsafe.Pointer(v) } // NewObject creates a node of type V_OBJECT, // using v as its underlying children func NewObject(v []Pair) Node { - return Node{ - t: types.V_OBJECT, - v: int64(len(v)&_LEN_MASK | cap(v)<<_CAP_BITS), - p: *(*unsafe.Pointer)(unsafe.Pointer(&v)), - } -} - -func (self *Node) setObject(v []Pair) { - self.t = types.V_OBJECT - self.setCapAndLen(cap(v), len(v)) - self.p = *(*unsafe.Pointer)(unsafe.Pointer(&v)) -} - -type parseObjectStack struct { - parser Parser - v []Pair -} - -type parseArrayStack struct { - parser Parser - v []Node -} - -func newLazyArray(p *Parser, v []Node) Node { - s := new(parseArrayStack) - s.parser = *p - s.v = v - return Node{ - t: _V_ARRAY_LAZY, - v: int64(len(v)&_LEN_MASK | cap(v)<<_CAP_BITS), - p: unsafe.Pointer(s), - } -} - -func (self *Node) setLazyArray(p *Parser, v []Node) { - s := new(parseArrayStack) - s.parser = *p - s.v = v - self.t = _V_ARRAY_LAZY - self.setCapAndLen(cap(v), len(v)) - self.p = (unsafe.Pointer)(s) + s := new(linkedPairs) + s.FromSlice(v) + return newObject(s) } -func newLazyObject(p *Parser, v []Pair) Node { - s := new(parseObjectStack) - s.parser = *p - s.v = v +func newObject(v *linkedPairs) Node { return Node{ - t: _V_OBJECT_LAZY, - v: int64(len(v)&_LEN_MASK | cap(v)<<_CAP_BITS), - p: unsafe.Pointer(s), + t: types.V_OBJECT, + l: uint(v.Len()), + p: unsafe.Pointer(v), } } -func (self *Node) setLazyObject(p *Parser, v []Pair) { - s := new(parseObjectStack) - s.parser = *p - s.v = v - self.t = _V_OBJECT_LAZY - self.setCapAndLen(cap(v), len(v)) - self.p = (unsafe.Pointer)(s) +func (self *Node) setObject(v *linkedPairs) { + self.t = types.V_OBJECT + self.l = uint(v.Len()) + self.p = unsafe.Pointer(v) } func newRawNode(str string, typ types.ValueType) Node { return Node{ t: _V_RAW | typ, p: rt.StrPtr(str), - v: int64(len(str) & _LEN_MASK), + l: uint(len(str)), } } func (self *Node) parseRaw(full bool) { - raw := rt.StrFrom(self.p, self.v) - parser := NewParser(raw) + raw := self.toString() + parser := NewParserObj(raw) if full { parser.noLazy = true parser.skipValue = false @@ -1760,14 +1799,6 @@ func (self *Node) parseRaw(full bool) { } } -func newError(err types.ParsingError, msg string) *Node { - return &Node{ - t: V_ERROR, - v: int64(err), - p: unsafe.Pointer(&msg), - } -} - var typeJumpTable = [256]types.ValueType{ '"' : types.V_STRING, '-' : _V_NUMBER, @@ -1791,18 +1822,3 @@ var typeJumpTable = [256]types.ValueType{ func switchRawType(c byte) types.ValueType { return typeJumpTable[c] } - -func unwrapError(err error) *Node { - if se, ok := err.(*Node); ok { - return se - }else if sse, ok := err.(Node); ok { - return &sse - } else { - msg := err.Error() - return &Node{ - t: V_ERROR, - v: 0, - p: unsafe.Pointer(&msg), - } - } -} \ No newline at end of file diff --git a/vendor/github.com/bytedance/sonic/ast/parser.go b/vendor/github.com/bytedance/sonic/ast/parser.go index 0a8e7b06..3e5309c1 100644 --- a/vendor/github.com/bytedance/sonic/ast/parser.go +++ b/vendor/github.com/bytedance/sonic/ast/parser.go @@ -18,11 +18,15 @@ package ast import ( `fmt` + `github.com/bytedance/sonic/internal/native/types` `github.com/bytedance/sonic/internal/rt` ) -const _DEFAULT_NODE_CAP int = 16 +const ( + _DEFAULT_NODE_CAP int = 8 + _APPEND_GROW_SHIFT = 1 +) const ( _ERR_NOT_FOUND types.ParsingError = 33 @@ -30,7 +34,10 @@ const ( ) var ( + // ErrNotExist means both key and value doesn't exist ErrNotExist error = newError(_ERR_NOT_FOUND, "value not exists") + + // ErrUnsupportType means API on the node is unsupported ErrUnsupportType error = newError(_ERR_UNSUPPORT_TYPE, "unsupported type") ) @@ -39,6 +46,7 @@ type Parser struct { s string noLazy bool skipValue bool + dbuf *byte } /** Parser Private Methods **/ @@ -107,7 +115,7 @@ func (self *Parser) lspace(sp int) int { return sp } -func (self *Parser) decodeArray(ret []Node) (Node, types.ParsingError) { +func (self *Parser) decodeArray(ret *linkedNodes) (Node, types.ParsingError) { sp := self.p ns := len(self.s) @@ -119,7 +127,7 @@ func (self *Parser) decodeArray(ret []Node) (Node, types.ParsingError) { /* check for empty array */ if self.s[self.p] == ']' { self.p++ - return emptyArrayNode, 0 + return Node{t: types.V_ARRAY}, 0 } /* allocate array space and parse every element */ @@ -149,7 +157,7 @@ func (self *Parser) decodeArray(ret []Node) (Node, types.ParsingError) { } /* add the value to result */ - ret = append(ret, val) + ret.Push(val) self.p = self.lspace(self.p) /* check for EOF */ @@ -160,17 +168,17 @@ func (self *Parser) decodeArray(ret []Node) (Node, types.ParsingError) { /* check for the next character */ switch self.s[self.p] { case ',' : self.p++ - case ']' : self.p++; return NewArray(ret), 0 - default: - if val.isLazy() { - return newLazyArray(self, ret), 0 - } - return Node{}, types.ERR_INVALID_CHAR + case ']' : self.p++; return newArray(ret), 0 + default: + // if val.isLazy() { + // return newLazyArray(self, ret), 0 + // } + return Node{}, types.ERR_INVALID_CHAR } } } -func (self *Parser) decodeObject(ret []Pair) (Node, types.ParsingError) { +func (self *Parser) decodeObject(ret *linkedPairs) (Node, types.ParsingError) { sp := self.p ns := len(self.s) @@ -182,7 +190,7 @@ func (self *Parser) decodeObject(ret []Pair) (Node, types.ParsingError) { /* check for empty object */ if self.s[self.p] == '}' { self.p++ - return emptyObjectNode, 0 + return Node{t: types.V_OBJECT}, 0 } /* decode each pair */ @@ -235,7 +243,8 @@ func (self *Parser) decodeObject(ret []Pair) (Node, types.ParsingError) { } /* add the value to result */ - ret = append(ret, Pair{Key: key, Value: val}) + // FIXME: ret's address may change here, thus previous referred node in ret may be invalid !! + ret.Push(Pair{Key: key, Value: val}) self.p = self.lspace(self.p) /* check for EOF */ @@ -246,11 +255,11 @@ func (self *Parser) decodeObject(ret []Pair) (Node, types.ParsingError) { /* check for the next character */ switch self.s[self.p] { case ',' : self.p++ - case '}' : self.p++; return NewObject(ret), 0 + case '}' : self.p++; return newObject(ret), 0 default: - if val.isLazy() { - return newLazyObject(self, ret), 0 - } + // if val.isLazy() { + // return newLazyObject(self, ret), 0 + // } return Node{}, types.ERR_INVALID_CHAR } } @@ -290,15 +299,23 @@ func (self *Parser) Parse() (Node, types.ParsingError) { case types.V_FALSE : return falseNode, 0 case types.V_STRING : return self.decodeString(val.Iv, val.Ep) case types.V_ARRAY: + if p := skipBlank(self.s, self.p); p >= self.p && self.s[p] == ']' { + self.p = p + 1 + return Node{t: types.V_ARRAY}, 0 + } if self.noLazy { - return self.decodeArray(make([]Node, 0, _DEFAULT_NODE_CAP)) + return self.decodeArray(new(linkedNodes)) } - return newLazyArray(self, make([]Node, 0, _DEFAULT_NODE_CAP)), 0 + return newLazyArray(self), 0 case types.V_OBJECT: + if p := skipBlank(self.s, self.p); p >= self.p && self.s[p] == '}' { + self.p = p + 1 + return Node{t: types.V_OBJECT}, 0 + } if self.noLazy { - return self.decodeObject(make([]Pair, 0, _DEFAULT_NODE_CAP)) + return self.decodeObject(new(linkedPairs)) } - return newLazyObject(self, make([]Pair, 0, _DEFAULT_NODE_CAP)), 0 + return newLazyObject(self), 0 case types.V_DOUBLE : return NewNumber(self.s[val.Ep:self.p]), 0 case types.V_INTEGER : return NewNumber(self.s[val.Ep:self.p]), 0 default : return Node{}, types.ParsingError(-val.Vt) @@ -429,7 +446,7 @@ func (self *Node) skipNextNode() *Node { } parser, stack := self.getParserAndArrayStack() - ret := stack.v + ret := &stack.v sp := parser.p ns := len(parser.s) @@ -458,7 +475,8 @@ func (self *Node) skipNextNode() *Node { } /* add the value to result */ - ret = append(ret, val) + ret.Push(val) + self.l++ parser.p = parser.lspace(parser.p) /* check for EOF */ @@ -470,12 +488,11 @@ func (self *Node) skipNextNode() *Node { switch parser.s[parser.p] { case ',': parser.p++ - self.setLazyArray(parser, ret) - return &ret[len(ret)-1] + return ret.At(ret.Len()-1) case ']': parser.p++ self.setArray(ret) - return &ret[len(ret)-1] + return ret.At(ret.Len()-1) default: return newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR)) } @@ -487,7 +504,7 @@ func (self *Node) skipNextPair() (*Pair) { } parser, stack := self.getParserAndObjectStack() - ret := stack.v + ret := &stack.v sp := parser.p ns := len(parser.s) @@ -541,7 +558,8 @@ func (self *Node) skipNextPair() (*Pair) { } /* add the value to result */ - ret = append(ret, Pair{Key: key, Value: val}) + ret.Push(Pair{Key: key, Value: val}) + self.l++ parser.p = parser.lspace(parser.p) /* check for EOF */ @@ -553,12 +571,11 @@ func (self *Node) skipNextPair() (*Pair) { switch parser.s[parser.p] { case ',': parser.p++ - self.setLazyObject(parser, ret) - return &ret[len(ret)-1] + return ret.At(ret.Len()-1) case '}': parser.p++ self.setObject(ret) - return &ret[len(ret)-1] + return ret.At(ret.Len()-1) default: return &Pair{key, *newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))} } @@ -601,10 +618,30 @@ func LoadsUseNumber(src string) (int, interface{}, error) { } } +// NewParser returns pointer of new allocated parser func NewParser(src string) *Parser { return &Parser{s: src} } +// NewParser returns new allocated parser +func NewParserObj(src string) Parser { + return Parser{s: src} +} + +// decodeNumber controls if parser decodes the number values instead of skip them +// WARN: once you set decodeNumber(true), please set decodeNumber(false) before you drop the parser +// otherwise the memory CANNOT be reused +func (self *Parser) decodeNumber(decode bool) { + if !decode && self.dbuf != nil { + types.FreeDbuf(self.dbuf) + self.dbuf = nil + return + } + if decode && self.dbuf == nil { + self.dbuf = types.NewDbuf() + } +} + // ExportError converts types.ParsingError to std Error func (self *Parser) ExportError(err types.ParsingError) error { if err == _ERR_NOT_FOUND { @@ -615,4 +652,4 @@ func (self *Parser) ExportError(err types.ParsingError) error { Src : self.s, Code: err, }.Description()) -} \ No newline at end of file +} diff --git a/vendor/github.com/bytedance/sonic/ast/search.go b/vendor/github.com/bytedance/sonic/ast/search.go index bb6fceaa..7108e7ea 100644 --- a/vendor/github.com/bytedance/sonic/ast/search.go +++ b/vendor/github.com/bytedance/sonic/ast/search.go @@ -16,6 +16,11 @@ package ast +import ( + `github.com/bytedance/sonic/internal/rt` + `github.com/bytedance/sonic/internal/native/types` +) + type Searcher struct { parser Parser } @@ -28,3 +33,48 @@ func NewSearcher(str string) *Searcher { }, } } + +// GetByPathCopy search in depth from top json and returns a **Copied** json node at the path location +func (self *Searcher) GetByPathCopy(path ...interface{}) (Node, error) { + return self.getByPath(true, path...) +} + +// GetByPathNoCopy search in depth from top json and returns a **Referenced** json node at the path location +// +// WARN: this search directly refer partial json from top json, which has faster speed, +// may consumes more memory. +func (self *Searcher) GetByPath(path ...interface{}) (Node, error) { + return self.getByPath(false, path...) +} + +func (self *Searcher) getByPath(copystring bool, path ...interface{}) (Node, error) { + var err types.ParsingError + var start int + + self.parser.p = 0 + start, err = self.parser.getByPath(path...) + if err != 0 { + // for compatibility with old version + if err == types.ERR_NOT_FOUND { + return Node{}, ErrNotExist + } + if err == types.ERR_UNSUPPORT_TYPE { + panic("path must be either int(>=0) or string") + } + return Node{}, self.parser.syntaxError(err) + } + + t := switchRawType(self.parser.s[start]) + if t == _V_NONE { + return Node{}, self.parser.ExportError(err) + } + + // copy string to reducing memory usage + var raw string + if copystring { + raw = rt.Mem2Str([]byte(self.parser.s[start:self.parser.p])) + } else { + raw = self.parser.s[start:self.parser.p] + } + return newRawNode(raw, t), nil +} diff --git a/vendor/github.com/bytedance/sonic/ast/sort.go b/vendor/github.com/bytedance/sonic/ast/sort.go deleted file mode 100644 index 0a9f1455..00000000 --- a/vendor/github.com/bytedance/sonic/ast/sort.go +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright 2021 ByteDance Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package ast - -// Algorithm 3-way Radix Quicksort, d means the radix. -// Reference: https://algs4.cs.princeton.edu/51radix/Quick3string.java.html -func radixQsort(kvs PairSlice, d, maxDepth int) { - for len(kvs) > 11 { - // To avoid the worst case of quickSort (time: O(n^2)), use introsort here. - // Reference: https://en.wikipedia.org/wiki/Introsort and - // https://github.com/golang/go/issues/467 - if maxDepth == 0 { - heapSort(kvs, 0, len(kvs)) - return - } - maxDepth-- - - p := pivot(kvs, d) - lt, i, gt := 0, 0, len(kvs) - for i < gt { - c := byteAt(kvs[i].Key, d) - if c < p { - swap(kvs, lt, i) - i++ - lt++ - } else if c > p { - gt-- - swap(kvs, i, gt) - } else { - i++ - } - } - - // kvs[0:lt] < v = kvs[lt:gt] < kvs[gt:len(kvs)] - // Native implemention: - // radixQsort(kvs[:lt], d, maxDepth) - // if p > -1 { - // radixQsort(kvs[lt:gt], d+1, maxDepth) - // } - // radixQsort(kvs[gt:], d, maxDepth) - // Optimize as follows: make recursive calls only for the smaller parts. - // Reference: https://www.geeksforgeeks.org/quicksort-tail-call-optimization-reducing-worst-case-space-log-n/ - if p == -1 { - if lt > len(kvs) - gt { - radixQsort(kvs[gt:], d, maxDepth) - kvs = kvs[:lt] - } else { - radixQsort(kvs[:lt], d, maxDepth) - kvs = kvs[gt:] - } - } else { - ml := maxThree(lt, gt-lt, len(kvs)-gt) - if ml == lt { - radixQsort(kvs[lt:gt], d+1, maxDepth) - radixQsort(kvs[gt:], d, maxDepth) - kvs = kvs[:lt] - } else if ml == gt-lt { - radixQsort(kvs[:lt], d, maxDepth) - radixQsort(kvs[gt:], d, maxDepth) - kvs = kvs[lt:gt] - d += 1 - } else { - radixQsort(kvs[:lt], d, maxDepth) - radixQsort(kvs[lt:gt], d+1, maxDepth) - kvs = kvs[gt:] - } - } - } - insertRadixSort(kvs, d) -} - -func insertRadixSort(kvs PairSlice, d int) { - for i := 1; i < len(kvs); i++ { - for j := i; j > 0 && lessFrom(kvs[j].Key, kvs[j-1].Key, d); j-- { - swap(kvs, j, j-1) - } - } -} - -func pivot(kvs PairSlice, d int) int { - m := len(kvs) >> 1 - if len(kvs) > 40 { - // Tukey's ``Ninther,'' median of three mediankvs of three. - t := len(kvs) / 8 - return medianThree( - medianThree(byteAt(kvs[0].Key, d), byteAt(kvs[t].Key, d), byteAt(kvs[2*t].Key, d)), - medianThree(byteAt(kvs[m].Key, d), byteAt(kvs[m-t].Key, d), byteAt(kvs[m+t].Key, d)), - medianThree(byteAt(kvs[len(kvs)-1].Key, d), - byteAt(kvs[len(kvs)-1-t].Key, d), - byteAt(kvs[len(kvs)-1-2*t].Key, d))) - } - return medianThree(byteAt(kvs[0].Key, d), byteAt(kvs[m].Key, d), byteAt(kvs[len(kvs)-1].Key, d)) -} - -func medianThree(i, j, k int) int { - if i > j { - i, j = j, i - } // i < j - if k < i { - return i - } - if k > j { - return j - } - return k -} - -func maxThree(i, j, k int) int { - max := i - if max < j { - max = j - } - if max < k { - max = k - } - return max -} - -// maxDepth returns a threshold at which quicksort should switch -// to heapsort. It returnkvs 2*ceil(lg(n+1)). -func maxDepth(n int) int { - var depth int - for i := n; i > 0; i >>= 1 { - depth++ - } - return depth * 2 -} - -// siftDown implements the heap property on kvs[lo:hi]. -// first is an offset into the array where the root of the heap lies. -func siftDown(kvs PairSlice, lo, hi, first int) { - root := lo - for { - child := 2*root + 1 - if child >= hi { - break - } - if child+1 < hi && kvs[first+child].Key < kvs[first+child+1].Key { - child++ - } - if kvs[first+root].Key >= kvs[first+child].Key { - return - } - swap(kvs, first+root, first+child) - root = child - } -} - -func heapSort(kvs PairSlice, a, b int) { - first := a - lo := 0 - hi := b - a - - // Build heap with the greatest element at top. - for i := (hi - 1) / 2; i >= 0; i-- { - siftDown(kvs, i, hi, first) - } - - // Pop elements, the largest first, into end of kvs. - for i := hi - 1; i >= 0; i-- { - swap(kvs, first, first+i) - siftDown(kvs, lo, i, first) - } -} - -// Note that Pair.Key is NOT pointed to Pair.m when map key is integer after swap -func swap(kvs PairSlice, a, b int) { - kvs[a].Key, kvs[b].Key = kvs[b].Key, kvs[a].Key - kvs[a].Value, kvs[b].Value = kvs[b].Value, kvs[a].Value -} - -// Compare two strings from the pos d. -func lessFrom(a, b string, d int) bool { - l := len(a) - if l > len(b) { - l = len(b) - } - for i := d; i < l; i++ { - if a[i] == b[i] { - continue - } - return a[i] < b[i] - } - return len(a) < len(b) -} - -func byteAt(b string, p int) int { - if p < len(b) { - return int(b[p]) - } - return -1 -} diff --git a/vendor/github.com/bytedance/sonic/ast/stubs_go120.go b/vendor/github.com/bytedance/sonic/ast/stubs_go120.go index bd6fff68..6f830529 100644 --- a/vendor/github.com/bytedance/sonic/ast/stubs_go120.go +++ b/vendor/github.com/bytedance/sonic/ast/stubs_go120.go @@ -52,4 +52,4 @@ var ( ) //go:linkname unquoteBytes encoding/json.unquoteBytes -func unquoteBytes(s []byte) (t []byte, ok bool) \ No newline at end of file +func unquoteBytes(s []byte) (t []byte, ok bool) diff --git a/vendor/github.com/bytedance/sonic/ast/visitor.go b/vendor/github.com/bytedance/sonic/ast/visitor.go new file mode 100644 index 00000000..4019c31a --- /dev/null +++ b/vendor/github.com/bytedance/sonic/ast/visitor.go @@ -0,0 +1,315 @@ +/* + * Copyright 2021 ByteDance Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ast + +import ( + `encoding/json` + + `github.com/bytedance/sonic/internal/native/types` +) + +// Visitor handles the callbacks during preorder traversal of a JSON AST. +// +// According to the JSON RFC8259, a JSON AST can be defined by +// the following rules without seperator / whitespace tokens. +// +// JSON-AST = value +// value = false / null / true / object / array / number / string +// object = begin-object [ member *( member ) ] end-object +// member = string value +// array = begin-array [ value *( value ) ] end-array +// +type Visitor interface { + + // OnNull handles a JSON null value. + OnNull() error + + // OnBool handles a JSON true / false value. + OnBool(v bool) error + + // OnString handles a JSON string value. + OnString(v string) error + + // OnInt64 handles a JSON number value with int64 type. + OnInt64(v int64, n json.Number) error + + // OnFloat64 handles a JSON number value with float64 type. + OnFloat64(v float64, n json.Number) error + + // OnObjectBegin handles the beginning of a JSON object value with a + // suggested capacity that can be used to make your custom object container. + // + // After this point the visitor will receive a sequence of callbacks like + // [string, value, string, value, ......, ObjectEnd]. + // + // Note: + // 1. This is a recursive definition which means the value can + // also be a JSON object / array described by a sequence of callbacks. + // 2. The suggested capacity will be 0 if current object is empty. + // 3. Currently sonic use a fixed capacity for non-empty object (keep in + // sync with ast.Node) which might not be very suitable. This may be + // improved in future version. + OnObjectBegin(capacity int) error + + // OnObjectKey handles a JSON object key string in member. + OnObjectKey(key string) error + + // OnObjectEnd handles the ending of a JSON object value. + OnObjectEnd() error + + // OnArrayBegin handles the beginning of a JSON array value with a + // suggested capacity that can be used to make your custom array container. + // + // After this point the visitor will receive a sequence of callbacks like + // [value, value, value, ......, ArrayEnd]. + // + // Note: + // 1. This is a recursive definition which means the value can + // also be a JSON object / array described by a sequence of callbacks. + // 2. The suggested capacity will be 0 if current array is empty. + // 3. Currently sonic use a fixed capacity for non-empty array (keep in + // sync with ast.Node) which might not be very suitable. This may be + // improved in future version. + OnArrayBegin(capacity int) error + + // OnArrayEnd handles the ending of a JSON array value. + OnArrayEnd() error +} + +// VisitorOptions contains all Visitor's options. The default value is an +// empty VisitorOptions{}. +type VisitorOptions struct { + // OnlyNumber indicates parser to directly return number value without + // conversion, then the first argument of OnInt64 / OnFloat64 will always + // be zero. + OnlyNumber bool +} + +var defaultVisitorOptions = &VisitorOptions{} + +// Preorder decodes the whole JSON string and callbacks each AST node to visitor +// during preorder traversal. Any visitor method with an error returned will +// break the traversal and the given error will be directly returned. The opts +// argument can be reused after every call. +func Preorder(str string, visitor Visitor, opts *VisitorOptions) error { + if opts == nil { + opts = defaultVisitorOptions + } + // process VisitorOptions first to guarantee that all options will be + // constant during decoding and make options more readable. + var ( + optDecodeNumber = !opts.OnlyNumber + ) + + tv := &traverser{ + parser: Parser{ + s: str, + noLazy: true, + skipValue: false, + }, + visitor: visitor, + } + + if optDecodeNumber { + tv.parser.decodeNumber(true) + } + + err := tv.decodeValue() + + if optDecodeNumber { + tv.parser.decodeNumber(false) + } + return err +} + +type traverser struct { + parser Parser + visitor Visitor +} + +// NOTE: keep in sync with (*Parser).Parse method. +func (self *traverser) decodeValue() error { + switch val := self.parser.decodeValue(); val.Vt { + case types.V_EOF: + return types.ERR_EOF + case types.V_NULL: + return self.visitor.OnNull() + case types.V_TRUE: + return self.visitor.OnBool(true) + case types.V_FALSE: + return self.visitor.OnBool(false) + case types.V_STRING: + return self.decodeString(val.Iv, val.Ep) + case types.V_DOUBLE: + return self.visitor.OnFloat64(val.Dv, + json.Number(self.parser.s[val.Ep:self.parser.p])) + case types.V_INTEGER: + return self.visitor.OnInt64(val.Iv, + json.Number(self.parser.s[val.Ep:self.parser.p])) + case types.V_ARRAY: + return self.decodeArray() + case types.V_OBJECT: + return self.decodeObject() + default: + return types.ParsingError(-val.Vt) + } +} + +// NOTE: keep in sync with (*Parser).decodeArray method. +func (self *traverser) decodeArray() error { + sp := self.parser.p + ns := len(self.parser.s) + + /* check for EOF */ + self.parser.p = self.parser.lspace(sp) + if self.parser.p >= ns { + return types.ERR_EOF + } + + /* check for empty array */ + if self.parser.s[self.parser.p] == ']' { + self.parser.p++ + if err := self.visitor.OnArrayBegin(0); err != nil { + return err + } + return self.visitor.OnArrayEnd() + } + + /* allocate array space and parse every element */ + if err := self.visitor.OnArrayBegin(_DEFAULT_NODE_CAP); err != nil { + return err + } + for { + /* decode the value */ + if err := self.decodeValue(); err != nil { + return err + } + self.parser.p = self.parser.lspace(self.parser.p) + + /* check for EOF */ + if self.parser.p >= ns { + return types.ERR_EOF + } + + /* check for the next character */ + switch self.parser.s[self.parser.p] { + case ',': + self.parser.p++ + case ']': + self.parser.p++ + return self.visitor.OnArrayEnd() + default: + return types.ERR_INVALID_CHAR + } + } +} + +// NOTE: keep in sync with (*Parser).decodeObject method. +func (self *traverser) decodeObject() error { + sp := self.parser.p + ns := len(self.parser.s) + + /* check for EOF */ + self.parser.p = self.parser.lspace(sp) + if self.parser.p >= ns { + return types.ERR_EOF + } + + /* check for empty object */ + if self.parser.s[self.parser.p] == '}' { + self.parser.p++ + if err := self.visitor.OnObjectBegin(0); err != nil { + return err + } + return self.visitor.OnObjectEnd() + } + + /* allocate object space and decode each pair */ + if err := self.visitor.OnObjectBegin(_DEFAULT_NODE_CAP); err != nil { + return err + } + for { + var njs types.JsonState + var err types.ParsingError + + /* decode the key */ + if njs = self.parser.decodeValue(); njs.Vt != types.V_STRING { + return types.ERR_INVALID_CHAR + } + + /* extract the key */ + idx := self.parser.p - 1 + key := self.parser.s[njs.Iv:idx] + + /* check for escape sequence */ + if njs.Ep != -1 { + if key, err = unquote(key); err != 0 { + return err + } + } + + if err := self.visitor.OnObjectKey(key); err != nil { + return err + } + + /* expect a ':' delimiter */ + if err = self.parser.delim(); err != 0 { + return err + } + + /* decode the value */ + if err := self.decodeValue(); err != nil { + return err + } + + self.parser.p = self.parser.lspace(self.parser.p) + + /* check for EOF */ + if self.parser.p >= ns { + return types.ERR_EOF + } + + /* check for the next character */ + switch self.parser.s[self.parser.p] { + case ',': + self.parser.p++ + case '}': + self.parser.p++ + return self.visitor.OnObjectEnd() + default: + return types.ERR_INVALID_CHAR + } + } +} + +// NOTE: keep in sync with (*Parser).decodeString method. +func (self *traverser) decodeString(iv int64, ep int) error { + p := self.parser.p - 1 + s := self.parser.s[iv:p] + + /* fast path: no escape sequence */ + if ep == -1 { + return self.visitor.OnString(s) + } + + /* unquote the string */ + out, err := unquote(s) + if err != 0 { + return err + } + return self.visitor.OnString(out) +} diff --git a/vendor/github.com/bytedance/sonic/bench-arm.sh b/vendor/github.com/bytedance/sonic/bench-arm.sh deleted file mode 100644 index b47d6278..00000000 --- a/vendor/github.com/bytedance/sonic/bench-arm.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -pwd=$(pwd) -export SONIC_NO_ASYNC_GC=1 - -cd $pwd/ast -go test -benchmem -run=^$ -benchtime=1000000x -bench "^(BenchmarkGet.*|BenchmarkSet.*)$" - -go test -benchmem -run=^$ -benchtime=10000x -bench "^(BenchmarkParser_.*|BenchmarkEncode.*)$" - -go test -benchmem -run=^$ -benchtime=10000000x -bench "^(BenchmarkNodeGetByPath|BenchmarkStructGetByPath|BenchmarkNodeIndex|BenchmarkStructIndex|BenchmarkSliceIndex|BenchmarkMapIndex|BenchmarkNodeGet|BenchmarkSliceGet|BenchmarkMapGet|BenchmarkNodeSet|BenchmarkMapSet|BenchmarkNodeSetByIndex|BenchmarkSliceSetByIndex|BenchmarkStructSetByIndex|BenchmarkNodeUnset|BenchmarkMapUnset|BenchmarkNodUnsetByIndex|BenchmarkSliceUnsetByIndex|BenchmarkNodeAdd|BenchmarkSliceAdd|BenchmarkMapAdd)$" - -unset SONIC_NO_ASYNC_GC -cd $pwd \ No newline at end of file diff --git a/vendor/github.com/bytedance/sonic/bench-large.png b/vendor/github.com/bytedance/sonic/bench-large.png deleted file mode 100644 index 8a8785ec..00000000 Binary files a/vendor/github.com/bytedance/sonic/bench-large.png and /dev/null differ diff --git a/vendor/github.com/bytedance/sonic/bench-small.png b/vendor/github.com/bytedance/sonic/bench-small.png deleted file mode 100644 index 7bdab77c..00000000 Binary files a/vendor/github.com/bytedance/sonic/bench-small.png and /dev/null differ diff --git a/vendor/github.com/bytedance/sonic/bench.py b/vendor/github.com/bytedance/sonic/bench.py deleted file mode 100644 index 1d4c3573..00000000 --- a/vendor/github.com/bytedance/sonic/bench.py +++ /dev/null @@ -1,134 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2022 ByteDance Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import tempfile -import os -import subprocess -import argparse - -gbench_prefix = "SONIC_NO_ASYNC_GC=1 go test -benchmem -run=none " - -def run(cmd): - print(cmd) - if os.system(cmd): - print ("Failed to run cmd: %s"%(cmd)) - exit(1) - -def run_s(cmd): - print (cmd) - try: - res = os.popen(cmd) - except subprocess.CalledProcessError as e: - if e.returncode: - print (e.output) - exit(1) - return res.read() - -def run_r(cmd): - print (cmd) - try: - cmds = cmd.split(' ') - data = subprocess.check_output(cmds, stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as e: - if e.returncode: - print (e.output) - exit(1) - return data.decode("utf-8") - -def compare(args): - # detech current branch. - # result = run_r("git branch") - current_branch = run_s("git status | head -n1 | sed 's/On branch //'") - # for br in result.split('\n'): - # if br.startswith("* "): - # current_branch = br.lstrip('* ') - # break - - if not current_branch: - print ("Failed to detech current branch") - return None - - # get the current diff - (fd, diff) = tempfile.mkstemp() - run("git diff > %s"%diff) - - # early return if currrent is main branch. - print ("Current branch: %s"%(current_branch)) - if current_branch == "main": - print ("Cannot compare at the main branch.Please build a new branch") - return None - - # benchmark current branch - (fd, target) = tempfile.mkstemp(".target.txt") - run("%s %s ./... 2>&1 | tee %s" %(gbench_prefix, args, target)) - - # trying to switch to the latest main branch - run("git checkout -- .") - if current_branch != "main": - run("git checkout main") - run("git pull --allow-unrelated-histories origin main") - - # benchmark main branch - (fd, main) = tempfile.mkstemp(".main.txt") - run("%s %s ./... 2>&1 | tee %s" %(gbench_prefix, args, main)) - - # diff the result - # benchstat = "go get golang.org/x/perf/cmd/benchstat && go install golang.org/x/perf/cmd/benchstat" - run( "benchstat -sort=delta %s %s"%(main, target)) - run("git checkout -- .") - - # restore branch - if current_branch != "main": - run("git checkout %s"%(current_branch)) - run("patch -p1 < %s" % (diff)) - return target - -def main(): - argparser = argparse.ArgumentParser(description='Tools to test the performance. Example: ./bench.py -b Decoder_Generic_Sonic -c') - argparser.add_argument('-b', '--bench', dest='filter', required=False, - help='Specify the filter for golang benchmark') - argparser.add_argument('-c', '--compare', dest='compare', action='store_true', required=False, - help='Compare with the main benchmarking') - argparser.add_argument('-t', '--times', dest='times', required=False, - help='benchmark the times') - argparser.add_argument('-r', '--repeat_times', dest='count', required=False, - help='benchmark the count') - args = argparser.parse_args() - - if args.filter: - gbench_args = "-bench=%s"%(args.filter) - else: - gbench_args = "-bench=." - - if args.times: - gbench_args += " -benchtime=%s"%(args.times) - - if args.count: - gbench_args += " -count=%s"%(args.count) - else: - gbench_args += " -count=10" - - if args.compare: - target = compare(gbench_args) - else: - target = None - - if not target: - (fd, target) = tempfile.mkstemp(".target.txt") - run("%s %s ./... 2>&1 | tee %s" %(gbench_prefix, gbench_args, target)) - -if __name__ == "__main__": - main() diff --git a/vendor/github.com/bytedance/sonic/bench.sh b/vendor/github.com/bytedance/sonic/bench.sh deleted file mode 100644 index 701986b5..00000000 --- a/vendor/github.com/bytedance/sonic/bench.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -pwd=$(pwd) -export SONIC_NO_ASYNC_GC=1 - -cd $pwd/encoder -go test -benchmem -run=^$ -benchtime=100000x -bench "^(BenchmarkEncoder_.*)$" - -cd $pwd/decoder -go test -benchmem -run=^$ -benchtime=100000x -bench "^(BenchmarkDecoder_.*)$" - -cd $pwd/ast -go test -benchmem -run=^$ -benchtime=1000000x -bench "^(BenchmarkGet.*|BenchmarkSet.*)$" - -go test -benchmem -run=^$ -benchtime=10000x -bench "^(BenchmarkParser_.*|BenchmarkEncode.*)$" - -go test -benchmem -run=^$ -benchtime=10000000x -bench "^(BenchmarkNodeGetByPath|BenchmarkStructGetByPath|BenchmarkNodeIndex|BenchmarkStructIndex|BenchmarkSliceIndex|BenchmarkMapIndex|BenchmarkNodeGet|BenchmarkSliceGet|BenchmarkMapGet|BenchmarkNodeSet|BenchmarkMapSet|BenchmarkNodeSetByIndex|BenchmarkSliceSetByIndex|BenchmarkStructSetByIndex|BenchmarkNodeUnset|BenchmarkMapUnset|BenchmarkNodUnsetByIndex|BenchmarkSliceUnsetByIndex|BenchmarkNodeAdd|BenchmarkSliceAdd|BenchmarkMapAdd)$" - -cd $pwd/external_jsonlib_test/benchmark_test -go test -benchmem -run=^$ -benchtime=100000x -bench "^(BenchmarkEncoder_.*|BenchmarkDecoder_.*)$" - -go test -benchmem -run=^$ -benchtime=1000000x -bench "^(BenchmarkGet.*|BenchmarkSet.*)$" - -go test -benchmem -run=^$ -benchtime=10000x -bench "^(BenchmarkParser_.*)$" - -unset SONIC_NO_ASYNC_GC -cd $pwd \ No newline at end of file diff --git a/vendor/github.com/bytedance/sonic/check_branch_name.sh b/vendor/github.com/bytedance/sonic/check_branch_name.sh deleted file mode 100644 index d1905dab..00000000 --- a/vendor/github.com/bytedance/sonic/check_branch_name.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash - -current=$(git status | head -n1 | sed 's/On branch //') -name=${1:-$current} -if [[ ! $name =~ ^(((opt(imize)?|feat(ure)?|doc|(bug|hot)?fix|test|refact(or)?|ci)/.+)|(main|develop)|(release/.+)|(release-v[0-9]+\.[0-9]+)|(release/v[0-9]+\.[0-9]+\.[0-9]+(-[a-z0-9.]+(\+[a-z0-9.]+)?)?)|revert-[a-z0-9]+)$ ]]; then - echo "branch name '$name' is invalid" - exit 1 -else - echo "branch name '$name' is valid" -fi diff --git a/vendor/github.com/bytedance/sonic/compat.go b/vendor/github.com/bytedance/sonic/compat.go index 015aa62b..728bc176 100644 --- a/vendor/github.com/bytedance/sonic/compat.go +++ b/vendor/github.com/bytedance/sonic/compat.go @@ -1,4 +1,4 @@ -// +build !amd64 go1.21 +// +build !amd64 !go1.16 go1.23 /* * Copyright 2021 ByteDance Inc. diff --git a/vendor/github.com/bytedance/sonic/decoder/decoder_amd64.go b/vendor/github.com/bytedance/sonic/decoder/decoder_amd64.go new file mode 100644 index 00000000..346ebbce --- /dev/null +++ b/vendor/github.com/bytedance/sonic/decoder/decoder_amd64.go @@ -0,0 +1,68 @@ +// +build amd64,go1.16,!go1.23 + +/* +* Copyright 2023 ByteDance Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package decoder + +import ( + `github.com/bytedance/sonic/internal/decoder` +) + +// Decoder is the decoder context object +type Decoder = decoder.Decoder + +// SyntaxError represents json syntax error +type SyntaxError = decoder.SyntaxError + +// MismatchTypeError represents dismatching between json and object +type MismatchTypeError = decoder.MismatchTypeError + +// Options for decode. +type Options = decoder.Options + +const ( + OptionUseInt64 Options = decoder.OptionUseInt64 + OptionUseNumber Options = decoder.OptionUseNumber + OptionUseUnicodeErrors Options = decoder.OptionUseUnicodeErrors + OptionDisableUnknown Options = decoder.OptionDisableUnknown + OptionCopyString Options = decoder.OptionCopyString + OptionValidateString Options = decoder.OptionValidateString +) + +// StreamDecoder is the decoder context object for streaming input. +type StreamDecoder = decoder.StreamDecoder + +var ( + // NewDecoder creates a new decoder instance. + NewDecoder = decoder.NewDecoder + + // NewStreamDecoder adapts to encoding/json.NewDecoder API. + // + // NewStreamDecoder returns a new decoder that reads from r. + NewStreamDecoder = decoder.NewStreamDecoder + + // Pretouch compiles vt ahead-of-time to avoid JIT compilation on-the-fly, in + // order to reduce the first-hit latency. + // + // Opts are the compile options, for example, "option.WithCompileRecursiveDepth" is + // a compile option to set the depth of recursive compile for the nested struct type. + Pretouch = decoder.Pretouch + + // Skip skips only one json value, and returns first non-blank character position and its ending position if it is valid. + // Otherwise, returns negative error code using start and invalid character position using end + Skip = decoder.Skip +) diff --git a/vendor/github.com/bytedance/sonic/decoder/decoder_compat.go b/vendor/github.com/bytedance/sonic/decoder/decoder_compat.go new file mode 100644 index 00000000..7883862c --- /dev/null +++ b/vendor/github.com/bytedance/sonic/decoder/decoder_compat.go @@ -0,0 +1,194 @@ +// +build !amd64 !go1.16 go1.23 + +/* +* Copyright 2023 ByteDance Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. + */ + +package decoder + +import ( + `bytes` + `encoding/json` + `io` + `reflect` + `unsafe` + + `github.com/bytedance/sonic/internal/native/types` + `github.com/bytedance/sonic/option` +) + +func init() { + println("WARNING: sonic only supports Go1.16~1.22 && CPU amd64, but your environment is not suitable") +} + +const ( + _F_use_int64 = 0 + _F_disable_urc = 2 + _F_disable_unknown = 3 + _F_copy_string = 4 + + _F_use_number = types.B_USE_NUMBER + _F_validate_string = types.B_VALIDATE_STRING + _F_allow_control = types.B_ALLOW_CONTROL +) + +type Options uint64 + +const ( + OptionUseInt64 Options = 1 << _F_use_int64 + OptionUseNumber Options = 1 << _F_use_number + OptionUseUnicodeErrors Options = 1 << _F_disable_urc + OptionDisableUnknown Options = 1 << _F_disable_unknown + OptionCopyString Options = 1 << _F_copy_string + OptionValidateString Options = 1 << _F_validate_string +) + +func (self *Decoder) SetOptions(opts Options) { + if (opts & OptionUseNumber != 0) && (opts & OptionUseInt64 != 0) { + panic("can't set OptionUseInt64 and OptionUseNumber both!") + } + self.f = uint64(opts) +} + + +// Decoder is the decoder context object +type Decoder struct { + i int + f uint64 + s string +} + +// NewDecoder creates a new decoder instance. +func NewDecoder(s string) *Decoder { + return &Decoder{s: s} +} + +// Pos returns the current decoding position. +func (self *Decoder) Pos() int { + return self.i +} + +func (self *Decoder) Reset(s string) { + self.s = s + self.i = 0 + // self.f = 0 +} + +// NOTE: api fallback do nothing +func (self *Decoder) CheckTrailings() error { + pos := self.i + buf := self.s + /* skip all the trailing spaces */ + if pos != len(buf) { + for pos < len(buf) && (types.SPACE_MASK & (1 << buf[pos])) != 0 { + pos++ + } + } + + /* then it must be at EOF */ + if pos == len(buf) { + return nil + } + + /* junk after JSON value */ + return nil +} + + +// Decode parses the JSON-encoded data from current position and stores the result +// in the value pointed to by val. +func (self *Decoder) Decode(val interface{}) error { + r := bytes.NewBufferString(self.s) + dec := json.NewDecoder(r) + if (self.f & uint64(OptionUseNumber)) != 0 { + dec.UseNumber() + } + if (self.f & uint64(OptionDisableUnknown)) != 0 { + dec.DisallowUnknownFields() + } + return dec.Decode(val) +} + +// UseInt64 indicates the Decoder to unmarshal an integer into an interface{} as an +// int64 instead of as a float64. +func (self *Decoder) UseInt64() { + self.f |= 1 << _F_use_int64 + self.f &^= 1 << _F_use_number +} + +// UseNumber indicates the Decoder to unmarshal a number into an interface{} as a +// json.Number instead of as a float64. +func (self *Decoder) UseNumber() { + self.f &^= 1 << _F_use_int64 + self.f |= 1 << _F_use_number +} + +// UseUnicodeErrors indicates the Decoder to return an error when encounter invalid +// UTF-8 escape sequences. +func (self *Decoder) UseUnicodeErrors() { + self.f |= 1 << _F_disable_urc +} + +// DisallowUnknownFields indicates the Decoder to return an error when the destination +// is a struct and the input contains object keys which do not match any +// non-ignored, exported fields in the destination. +func (self *Decoder) DisallowUnknownFields() { + self.f |= 1 << _F_disable_unknown +} + +// CopyString indicates the Decoder to decode string values by copying instead of referring. +func (self *Decoder) CopyString() { + self.f |= 1 << _F_copy_string +} + +// ValidateString causes the Decoder to validate string values when decoding string value +// in JSON. Validation is that, returning error when unescaped control chars(0x00-0x1f) or +// invalid UTF-8 chars in the string value of JSON. +func (self *Decoder) ValidateString() { + self.f |= 1 << _F_validate_string +} + +// Pretouch compiles vt ahead-of-time to avoid JIT compilation on-the-fly, in +// order to reduce the first-hit latency. +// +// Opts are the compile options, for example, "option.WithCompileRecursiveDepth" is +// a compile option to set the depth of recursive compile for the nested struct type. +func Pretouch(vt reflect.Type, opts ...option.CompileOption) error { + return nil +} + +type StreamDecoder = json.Decoder + +// NewStreamDecoder adapts to encoding/json.NewDecoder API. +// +// NewStreamDecoder returns a new decoder that reads from r. +func NewStreamDecoder(r io.Reader) *StreamDecoder { + return json.NewDecoder(r) +} + +// SyntaxError represents json syntax error +type SyntaxError json.SyntaxError + +// Description +func (s SyntaxError) Description() string { + return (*json.SyntaxError)(unsafe.Pointer(&s)).Error() +} +// Error +func (s SyntaxError) Error() string { + return (*json.SyntaxError)(unsafe.Pointer(&s)).Error() +} + +// MismatchTypeError represents dismatching between json and object +type MismatchTypeError json.UnmarshalTypeError \ No newline at end of file diff --git a/vendor/github.com/bytedance/sonic/encoder/encoder_amd64.go b/vendor/github.com/bytedance/sonic/encoder/encoder_amd64.go new file mode 100644 index 00000000..b4f1b7b5 --- /dev/null +++ b/vendor/github.com/bytedance/sonic/encoder/encoder_amd64.go @@ -0,0 +1,115 @@ +// +build amd64,go1.16,!go1.23 + +/* + * Copyright 2023 ByteDance Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package encoder + +import ( + `github.com/bytedance/sonic/internal/encoder` +) + + +// Encoder represents a specific set of encoder configurations. +type Encoder = encoder.Encoder + +// StreamEncoder uses io.Writer as input. +type StreamEncoder = encoder.StreamEncoder + +// Options is a set of encoding options. +type Options = encoder.Options + +const ( + // SortMapKeys indicates that the keys of a map needs to be sorted + // before serializing into JSON. + // WARNING: This hurts performance A LOT, USE WITH CARE. + SortMapKeys Options = encoder.SortMapKeys + + // EscapeHTML indicates encoder to escape all HTML characters + // after serializing into JSON (see https://pkg.go.dev/encoding/json#HTMLEscape). + // WARNING: This hurts performance A LOT, USE WITH CARE. + EscapeHTML Options = encoder.EscapeHTML + + // CompactMarshaler indicates that the output JSON from json.Marshaler + // is always compact and needs no validation + CompactMarshaler Options = encoder.CompactMarshaler + + // NoQuoteTextMarshaler indicates that the output text from encoding.TextMarshaler + // is always escaped string and needs no quoting + NoQuoteTextMarshaler Options = encoder.NoQuoteTextMarshaler + + // NoNullSliceOrMap indicates all empty Array or Object are encoded as '[]' or '{}', + // instead of 'null' + NoNullSliceOrMap Options = encoder.NoNullSliceOrMap + + // ValidateString indicates that encoder should validate the input string + // before encoding it into JSON. + ValidateString Options = encoder.ValidateString + + // NoValidateJSONMarshaler indicates that the encoder should not validate the output string + // after encoding the JSONMarshaler to JSON. + NoValidateJSONMarshaler Options = encoder.NoValidateJSONMarshaler + + // NoEncoderNewline indicates that the encoder should not add a newline after every message + NoEncoderNewline Options = encoder.NoEncoderNewline + + // CompatibleWithStd is used to be compatible with std encoder. + CompatibleWithStd Options = encoder.CompatibleWithStd +) + + +var ( + // Encode returns the JSON encoding of val, encoded with opts. + Encode = encoder.Encode + + // EncodeInto is like Encode but uses a user-supplied buffer instead of allocating a new one. + EncodeIndented = encoder.EncodeIndented + + // EncodeIndented is like Encode but applies Indent to format the output. + // Each JSON element in the output will begin on a new line beginning with prefix + // followed by one or more copies of indent according to the indentation nesting. + EncodeInto = encoder.EncodeInto + + // HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029 + // characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029 + // so that the JSON will be safe to embed inside HTML